From b007db78edcb4f9f2d590381edb9a6eac2ed26cc Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Mon, 22 May 2023 17:09:05 +0530 Subject: [PATCH 01/12] [WIP] auth for dbnode --- src/cluster/client/etcd/config.go | 20 ++ src/cmd/services/m3dbnode/config/auth.go | 170 ++++++++++++ src/cmd/services/m3dbnode/config/auth_test.go | 261 ++++++++++++++++++ src/cmd/services/m3dbnode/main/main.go | 13 + src/cmd/services/m3dbnode/server/server.go | 6 + src/dbnode/auth/benchmark_digest_test.go | 35 +++ src/dbnode/auth/digest.go | 36 +++ src/dbnode/auth/digest_test.go | 43 +++ src/dbnode/auth/entities.go | 57 ++++ src/dbnode/auth/entities_test.go | 120 ++++++++ src/dbnode/auth/enum.go | 37 +++ src/dbnode/auth/inbound.go | 84 ++++++ src/dbnode/auth/inbound_test.go | 253 +++++++++++++++++ src/dbnode/auth/integration/setup_config.go | 165 +++++++++++ src/dbnode/auth/module.go | 47 ++++ src/dbnode/auth/outbound.go | 42 +++ src/dbnode/auth/outbound_test.go | 82 ++++++ src/dbnode/client/client_creds_provider.go | 45 +++ .../client/client_creds_provider_test.go | 185 +++++++++++++ src/dbnode/client/config.go | 5 +- src/dbnode/client/connection_pool.go | 6 + src/dbnode/client/options.go | 14 + src/dbnode/client/types.go | 6 + src/dbnode/environment/config.go | 2 + src/dbnode/server/server.go | 13 + src/dbnode/server/server_creds_provider.go | 73 +++++ .../server/server_creds_provider_test.go | 144 ++++++++++ src/dbnode/topology/dynamic.go | 5 + src/dbnode/topology/static.go | 4 + src/dbnode/topology/types.go | 2 + src/x/config/configflag/flag.go | 38 ++- src/x/config/configflag/flag_test.go | 26 ++ 32 files changed, 2033 insertions(+), 6 deletions(-) create mode 100644 src/cmd/services/m3dbnode/config/auth.go create mode 100644 src/cmd/services/m3dbnode/config/auth_test.go create mode 100644 src/dbnode/auth/benchmark_digest_test.go create mode 100644 src/dbnode/auth/digest.go create mode 100644 src/dbnode/auth/digest_test.go create mode 100644 src/dbnode/auth/entities.go create mode 100644 src/dbnode/auth/entities_test.go create mode 100644 src/dbnode/auth/enum.go create mode 100644 src/dbnode/auth/inbound.go create mode 100644 src/dbnode/auth/inbound_test.go create mode 100644 src/dbnode/auth/integration/setup_config.go create mode 100644 src/dbnode/auth/module.go create mode 100644 src/dbnode/auth/outbound.go create mode 100644 src/dbnode/auth/outbound_test.go create mode 100644 src/dbnode/client/client_creds_provider.go create mode 100644 src/dbnode/client/client_creds_provider_test.go create mode 100644 src/dbnode/server/server_creds_provider.go create mode 100644 src/dbnode/server/server_creds_provider_test.go diff --git a/src/cluster/client/etcd/config.go b/src/cluster/client/etcd/config.go index 520afa385e..394a339b30 100644 --- a/src/cluster/client/etcd/config.go +++ b/src/cluster/client/etcd/config.go @@ -21,6 +21,7 @@ package etcd import ( + "fmt" "os" "time" @@ -52,6 +53,8 @@ type ClusterConfig struct { AutoSyncInterval time.Duration `yaml:"autoSyncInterval"` DialTimeout time.Duration `yaml:"dialTimeout"` + Auth *AuthConfig `yaml:"auth"` + DialOptions []grpc.DialOption `yaml:"-"` // nonserializable } @@ -119,6 +122,22 @@ func (c *KeepAliveConfig) NewOptions() KeepAliveOptions { SetKeepAliveTimeout(c.Timeout) } +// AuthConfig configures authentication behavior. +type AuthConfig struct { + Enabled bool `yaml:"enabled"` + UserName string `yaml:"username"` + Password string `yaml:"password"` +} + +func (a *AuthConfig) Validate() error { + if a.Enabled { + if a.UserName == "" || a.Password == "" { + return fmt.Errorf("credentials must be set for client's outbound") + } + } + return nil +} + // Configuration is for config service client. type Configuration struct { Zone string `yaml:"zone"` @@ -129,6 +148,7 @@ type Configuration struct { SDConfig services.Configuration `yaml:"m3sd"` WatchWithRevision int64 `yaml:"watchWithRevision"` NewDirectoryMode *os.FileMode `yaml:"newDirectoryMode"` + Auth *AuthConfig `yaml:"auth"` Retry retry.Configuration `yaml:"retry"` RequestTimeout time.Duration `yaml:"requestTimeout"` diff --git a/src/cmd/services/m3dbnode/config/auth.go b/src/cmd/services/m3dbnode/config/auth.go new file mode 100644 index 0000000000..2cdfc384a2 --- /dev/null +++ b/src/cmd/services/m3dbnode/config/auth.go @@ -0,0 +1,170 @@ +package config + +import "fmt" + +// AuthConfig is the top level config that includes secrets of inbound and +// outbound of a dbnode. +type AuthConfig struct { + Outbound *Outbounds `yaml:"outbounds"` + Inbound *Inbound `yaml:"inbounds"` +} + +// Validate validates the AuthConfig. We use this method to validate fields +// where the validator package falls short. +func (c *AuthConfig) Validate() error { + if c.Outbound == nil { + return fmt.Errorf("outbound creds are not present") + } + + if err := c.Outbound.Validate(); err != nil { + return err + } + + if c.Inbound != nil { + if err := c.Inbound.Validate(); err != nil { + return err + } + } + return nil +} + +// Outbounds consist secrets for outbounds connections for m3dbnodes and +// etcd. +type Outbounds struct { + M3DB *ServicesConfig `yaml:"m3db"` + Etcd *ServicesConfig `yaml:"etcd"` +} + +// Validate validates the Outbounds. +func (c *Outbounds) Validate() error { + if c.M3DB == nil { + return fmt.Errorf("incomplete outbound creds, m3db node creds not provided") + } + + if err := c.M3DB.Validate(); err != nil { + return err + } + + if c.Etcd == nil { + return fmt.Errorf("incomplete outbound creds, etcd node creds not provided") + } + if err := c.Etcd.Validate(); err != nil { + return err + } + + return nil +} + +// Inbound consist secrets for Inbound connections for m3dbnodes. +type Inbound struct { + M3DB *InboundConfig `yaml:"m3db"` +} + +// Validate validates the Inbound. +func (c *Inbound) Validate() error { + if c.M3DB == nil { + return fmt.Errorf("incomplete inboundcreds, m3db config not provided") + } + + if err := c.M3DB.Validate(); err != nil { + return err + } + return nil +} + +// InboundConfig consist credentials and auth mode for inbound request. +type InboundConfig struct { + Mode *string `yaml:"mode"` + Credentials []*InboundCredentials `yaml:"credentials"` +} + +// Validate validates the InboundConfig. +func (c *InboundConfig) Validate() error { + if c.Mode == nil { + return fmt.Errorf("incomplete inboundcreds, mode not provided") + } + + for _, node := range c.Credentials { + if err := node.Validate(); err != nil { + return err + } + } + + return nil +} + +// ServicesConfig consist of service level config. +type ServicesConfig struct { + NodeConfig []*ServiceConfig `yaml:"services"` +} + +// Validate validates the ServicesConfig. +func (c *ServicesConfig) Validate() error { + if c.NodeConfig == nil || len(c.NodeConfig) == 0 { + return fmt.Errorf("incomplete creds, service level creds not provided") + } + + for _, node := range c.NodeConfig { + if err := node.Validate(); err != nil { + return err + } + } + + return nil +} + +// ServiceConfig consist of service level config. +type ServiceConfig struct { + Service *OutboundCredentials `yaml:"service"` +} + +// Validate validates the ServiceConfig. +func (c *ServiceConfig) Validate() error { + if c.Service == nil { + return fmt.Errorf("incomplete creds, service level creds not provided") + } + + if err := c.Service.Validate(); err != nil { + return err + } + + return nil +} + +// OutboundCredentials is the bottom most struct consist used for storing outbounds creds. +type OutboundCredentials struct { + Zone string + Username *string + Password *string +} + +// Validate validates the Credentials. +func (c *OutboundCredentials) Validate() error { + if c.Username == nil { + return fmt.Errorf("incomplete creds, username not provided for outbounds") + } + if c.Password == nil { + return fmt.Errorf("incomplete creds, password not provided for outbounds") + } + + return nil +} + +// InboundCredentials is the bottom most struct consist used for storing outbounds creds. +type InboundCredentials struct { + Zone string `yaml:"zone"` + Username *string `yaml:"username"` + Digest *string `yaml:"digest"` +} + +// Validate validates the Credentials. +func (c *InboundCredentials) Validate() error { + if c.Username == nil { + return fmt.Errorf("incomplete creds, username not provided for inbounds") + } + if c.Digest == nil { + return fmt.Errorf("incomplete creds, digest password not provided for inbounds") + } + + return nil +} diff --git a/src/cmd/services/m3dbnode/config/auth_test.go b/src/cmd/services/m3dbnode/config/auth_test.go new file mode 100644 index 0000000000..b7729f924b --- /dev/null +++ b/src/cmd/services/m3dbnode/config/auth_test.go @@ -0,0 +1,261 @@ +package config + +import ( + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + yaml "gopkg.in/yaml.v2" + "io" + "os" + "testing" +) + +const testBaseAuthConfig = ` +outbounds: + m3db: + services: + - service: + zone: m3_abc + username: m3_bcd + password: m3_xyz + - service: + zone: m3_abc1 + username: m3_bcd1 + password: m3_xyz1 + + etcd: + services: + - service: + zone: etcd_abc + username: etcd_bcd + password: etcd_xyz + - service: + zone: etcd_abc1 + username: etcd_bcd1 + password: etcd_xyz1 + +inbounds: + m3db: + mode: NONE + credentials: + - username: user + digest: digest1 + - username: user2 + digest: digest2 +` + +const missingOutbounds = ` + +inbounds: + m3db: + mode: NONE + credentials: + - username: user + password: digest + - username: user2 + password: digest2 +` + +const missingM3dbConfigOutbounds = ` +outbounds: + etcd: + services: + - service: + zone: etcd_abc + username: etcd_bcd + password: etcd_xyz + - service: + zone: etcd_abc1 + username: etcd_bcd1 + password: etcd_xyz1 + +inbounds: + m3db: + mode: NONE + credentials: + - username: user + digest: digest1 + - username: user2 + digest: digest2 +` + +const missingETCDConfigOutbounds = ` +outbounds: + m3db: + services: + - service: + zone: m3_abc + username: m3_bcd + password: m3_xyz + - service: + zone: m3_abc1 + username: m3_bcd1 + password: m3_xyz1 + +inbounds: + m3db: + mode: NONE + credentials: + - username: user + digest: digest1 + - username: user2 + digest: digest2 +` + +const incompleteCreds = ` +outbounds: + m3db: + services: + - service: + zone: m3_abc + username: m3_bcd + - service: + zone: m3_abc1 + username: m3_bcd1 + password: m3_xyz1 + + etcd: + services: + - service: + zone: etcd_abc + username: etcd_bcd + password: etcd_xyz + - service: + zone: etcd_abc1 + username: etcd_bcd1 + password: etcd_xyz1 + +inbounds: + m3db: + mode: NONE + credentials: + - username: user + - username: user2 + digest: digest2 +` + +func TestAuthConfiguration(t *testing.T) { + t.Run("correct secrets file", func(t *testing.T) { + fd, crtErr := os.CreateTemp("", "secrets.yaml") + require.NoError(t, crtErr) + defer func() { + assert.NoError(t, fd.Close()) + assert.NoError(t, os.Remove(fd.Name())) + }() + + _, wrtErr := fd.Write([]byte(testBaseAuthConfig)) + require.NoError(t, wrtErr) + + f, opnErr := os.Open(fd.Name()) + require.NoError(t, opnErr) + + all, rdErr := io.ReadAll(f) + require.NoError(t, rdErr) + + newSecrets := &AuthConfig{} + unmarErr := yaml.Unmarshal(all, newSecrets) + require.NoError(t, unmarErr) + + err := newSecrets.Validate() + require.NoError(t, err) + }) + + t.Run("secrets file missing outbounds", func(t *testing.T) { + fd, crtErr := os.CreateTemp("", "secrets.yaml") + require.NoError(t, crtErr) + defer func() { + assert.NoError(t, fd.Close()) + assert.NoError(t, os.Remove(fd.Name())) + }() + + _, wrtErr := fd.Write([]byte(missingOutbounds)) + require.NoError(t, wrtErr) + + f, opnErr := os.Open(fd.Name()) + require.NoError(t, opnErr) + + all, rdErr := io.ReadAll(f) + require.NoError(t, rdErr) + + newSecrets := &AuthConfig{} + unmarErr := yaml.Unmarshal(all, newSecrets) + require.NoError(t, unmarErr) + + err := newSecrets.Validate() + require.Error(t, err, "outbound creds are not present") + }) + + t.Run("secrets file missing m3db outbounds", func(t *testing.T) { + fd, crtErr := os.CreateTemp("", "secrets.yaml") + require.NoError(t, crtErr) + defer func() { + assert.NoError(t, fd.Close()) + assert.NoError(t, os.Remove(fd.Name())) + }() + + _, wrtErr := fd.Write([]byte(missingM3dbConfigOutbounds)) + require.NoError(t, wrtErr) + + f, opnErr := os.Open(fd.Name()) + require.NoError(t, opnErr) + + all, rdErr := io.ReadAll(f) + require.NoError(t, rdErr) + + newSecrets := &AuthConfig{} + unmarErr := yaml.Unmarshal(all, newSecrets) + require.NoError(t, unmarErr) + + err := newSecrets.Validate() + require.Error(t, err, "incomplete outbound creds, m3db node creds not provided") + }) + + t.Run("secrets file missing etcd outbounds", func(t *testing.T) { + fd, crtErr := os.CreateTemp("", "secrets.yaml") + require.NoError(t, crtErr) + defer func() { + assert.NoError(t, fd.Close()) + assert.NoError(t, os.Remove(fd.Name())) + }() + + _, wrtErr := fd.Write([]byte(missingETCDConfigOutbounds)) + require.NoError(t, wrtErr) + + f, opnErr := os.Open(fd.Name()) + require.NoError(t, opnErr) + + all, rdErr := io.ReadAll(f) + require.NoError(t, rdErr) + + newSecrets := &AuthConfig{} + unmarErr := yaml.Unmarshal(all, newSecrets) + require.NoError(t, unmarErr) + + err := newSecrets.Validate() + require.Error(t, err, "incomplete outbound creds, etcd node creds not provided") + }) + + t.Run("secrets file contains incomplete creds", func(t *testing.T) { + fd, crtErr := os.CreateTemp("", "secrets.yaml") + require.NoError(t, crtErr) + defer func() { + assert.NoError(t, fd.Close()) + assert.NoError(t, os.Remove(fd.Name())) + }() + + _, wrtErr := fd.Write([]byte(incompleteCreds)) + require.NoError(t, wrtErr) + + f, opnErr := os.Open(fd.Name()) + require.NoError(t, opnErr) + + all, rdErr := io.ReadAll(f) + require.NoError(t, rdErr) + + newSecrets := &AuthConfig{} + unmarErr := yaml.Unmarshal(all, newSecrets) + require.NoError(t, unmarErr) + + err := newSecrets.Validate() + require.Error(t, err, "incomplete creds, password not provided") + }) +} diff --git a/src/cmd/services/m3dbnode/main/main.go b/src/cmd/services/m3dbnode/main/main.go index 2c9a8cd673..c8a120244c 100644 --- a/src/cmd/services/m3dbnode/main/main.go +++ b/src/cmd/services/m3dbnode/main/main.go @@ -46,8 +46,21 @@ func main() { log.Fatalf("error validating config: %v", err) } + var secrets config.AuthConfig + isSecretsConfigPresent, err := cfgOpts.CredentialLoad(&secrets, xconfig.Options{}) + if err != nil { + log.Fatalf("error loading secrets config: %v", err) + } + + if isSecretsConfigPresent { + if err := secrets.Validate(); err != nil { + log.Fatalf("error validating config: %v", err) + } + } + server.RunComponents(server.Options{ Configuration: cfg, + SecretsConfig: secrets, InterruptCh: xos.NewInterruptChannel(cfg.Components()), }) } diff --git a/src/cmd/services/m3dbnode/server/server.go b/src/cmd/services/m3dbnode/server/server.go index f860f2f1c1..d01f7d07a5 100644 --- a/src/cmd/services/m3dbnode/server/server.go +++ b/src/cmd/services/m3dbnode/server/server.go @@ -35,6 +35,10 @@ type Options struct { // node and a coordinator. Configuration config.Configuration + // SecretsConfig is the top level config that includes secrets of inbound and + // outbound of a dbnode. + SecretsConfig config.AuthConfig + // InterruptCh is a programmatic interrupt channel to supply to // interrupt and shutdown the server. InterruptCh <-chan error @@ -49,6 +53,7 @@ type Options struct { func RunComponents(opts Options) { var ( cfg = opts.Configuration + secrets = opts.SecretsConfig interruptCh = opts.InterruptCh shutdownCh = opts.ShutdownCh @@ -80,6 +85,7 @@ func RunComponents(opts Options) { if cfg.DB != nil { dbserver.Run(dbserver.RunOptions{ Config: *cfg.DB, + Secrets: secrets, ClientCh: dbClientCh, ClusterClientCh: clusterClientCh, InterruptCh: interruptCh, diff --git a/src/dbnode/auth/benchmark_digest_test.go b/src/dbnode/auth/benchmark_digest_test.go new file mode 100644 index 0000000000..f9376f8ae7 --- /dev/null +++ b/src/dbnode/auth/benchmark_digest_test.go @@ -0,0 +1,35 @@ +package auth + +import ( + mrand "math/rand" + "testing" +) + +const ( + charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +) + +var ( + inputStr = 10 +) + +func BenchmarkGetMD5DigestMap(b *testing.B) { + testInput := []string{} + for i := 0; i < inputStr; i++ { + testInput = append(testInput, RandomString(10)) + } + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := GenerateHash(testInput[i%inputStr]); err != nil { + b.Fatalf("error generating MD5 digest: %v", err) + } + } +} + +func RandomString(length int) string { + b := make([]byte, length) + for i := range b { + b[i] = charset[mrand.Intn(len(charset))] + } + return string(b) +} diff --git a/src/dbnode/auth/digest.go b/src/dbnode/auth/digest.go new file mode 100644 index 0000000000..7e3247c93f --- /dev/null +++ b/src/dbnode/auth/digest.go @@ -0,0 +1,36 @@ +package auth + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "hash" +) + +var ( + credentialCache = map[string]string{} + hashFunc = md5.New() +) + +func generateHashWithCacheLookup(data string, h hash.Hash) (string, error) { + if val, ok := credentialCache[data]; ok { + return val, nil + } + _, err := h.Write([]byte(data)) + if err != nil { + return "", fmt.Errorf("error generating hash") + } + generatedHash := hex.EncodeToString(h.Sum(nil)) + credentialCache[data] = generatedHash + return generatedHash, nil +} + +// GenerateHash takes data as input param and returns md5 hash either from cache or creating md5 hash on runtime. +func GenerateHash(data string) (string, error) { + defer hashFunc.Reset() + return generateHashWithCacheLookup(data, hashFunc) +} + +func invalidateCache() { + credentialCache = map[string]string{} +} diff --git a/src/dbnode/auth/digest_test.go b/src/dbnode/auth/digest_test.go new file mode 100644 index 0000000000..b528e95a2d --- /dev/null +++ b/src/dbnode/auth/digest_test.go @@ -0,0 +1,43 @@ +package auth + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestGenerateHash(t *testing.T) { + input := "teststring" + expectedOutput := "d67c5cbf5b01c9f91932e3b8def5e5f8" + + // output after cache miss. + output, err := GenerateHash(input) + assert.NoError(t, err) + assert.Equal(t, expectedOutput, output) + + // output from cache. + cacheoutput, cacheerr := GenerateHash(input) + assert.NoError(t, cacheerr) + assert.Equal(t, expectedOutput, cacheoutput) +} + +func TestGenerateHashInvalidatingCache(t *testing.T) { + input := "teststring" + expectedOutput := "d67c5cbf5b01c9f91932e3b8def5e5f8" + + // output after cache miss. + output, err := GenerateHash(input) + assert.NoError(t, err) + assert.Equal(t, expectedOutput, output) + + invalidateCache() + + // output after cache miss. + nonCachedout, nonCachederr := GenerateHash(input) + assert.NoError(t, nonCachederr) + assert.Equal(t, expectedOutput, nonCachedout) + + // output from cache. + cacheoutput, cacheerr := GenerateHash(input) + assert.NoError(t, cacheerr) + assert.Equal(t, expectedOutput, cacheoutput) +} diff --git a/src/dbnode/auth/entities.go b/src/dbnode/auth/entities.go new file mode 100644 index 0000000000..3b9d4bcc4a --- /dev/null +++ b/src/dbnode/auth/entities.go @@ -0,0 +1,57 @@ +package auth + +import ( + "errors" +) + +// InboundCredentials encapsulates credentials for inbound RPC to dbnode. +type InboundCredentials struct { + Username string + Digest string + Type CredentialType +} + +// Validate validates the InboundCredentials. +func (c *InboundCredentials) Validate() error { + if c.Username == "" { + return errors.New("username field is empty for inbound") + } + + if c.Digest == "" { + return errors.New("digest field is empty for inbound") + } + + if c.Type != ClientCredential { + return errors.New("incorrect cred type field for inbound") + } + + return nil +} + +// OutboundCredentials encapsulates credentials for outbiund RPC from dbnode. +type OutboundCredentials struct { + Username string + Password string + Zone string + Type CredentialType +} + +// Validate validates the OutboundCredentials. +func (c *OutboundCredentials) Validate() error { + if c.Username == "" { + return errors.New("username field is empty for outbound") + } + + if c.Password == "" { + return errors.New("password field is empty for outbound") + } + + if c.Type == Unknown || c.Type == ClientCredential { + return errors.New("incorrect cred type field for outbound") + } + + if c.Zone == "" { + return errors.New("zone field is not set for outbound") + } + return nil +} diff --git a/src/dbnode/auth/entities_test.go b/src/dbnode/auth/entities_test.go new file mode 100644 index 0000000000..8a0d69d547 --- /dev/null +++ b/src/dbnode/auth/entities_test.go @@ -0,0 +1,120 @@ +package auth + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestInboundCredentials_Validate(t *testing.T) { + tests := []struct { + name string + userName string + digest string + credtype CredentialType + err string + }{ + { + name: "no error", + userName: "abc", + digest: "xyz", + credtype: ClientCredential, + }, { + name: "username missing", + digest: "xyz", + err: "username field is empty", + }, { + name: "digest missing", + userName: "xyz", + credtype: ClientCredential, + err: "digest field is empty for inbound", + }, { + name: "cred type missing", + userName: "abc", + digest: "xyz", + err: "incorrect cred type field for inbound", + }, { + name: "incorrect cred type", + userName: "abc", + digest: "xyz", + credtype: EtcdCredential, + err: "incorrect cred type field for inbound", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + newCreds := &InboundCredentials{Username: tt.userName, Digest: tt.digest, Type: tt.credtype} + err := newCreds.Validate() + if tt.err != "" { + assert.Contains(t, err.Error(), tt.err) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestOutboundCredentials_Validate(t *testing.T) { + tests := []struct { + name string + userName string + password string + zone string + credtype CredentialType + err string + }{ + { + name: "no error", + userName: "abc", + password: "xyz", + zone: "foo", + credtype: EtcdCredential, + }, { + name: "username missing", + password: "xyz", + zone: "foo", + err: "username field is empty", + }, { + name: "password missing", + userName: "xyz", + zone: "foo", + credtype: EtcdCredential, + err: "password field is empty", + }, { + name: "zone missing", + userName: "abc", + password: "xyz", + credtype: PeerCredential, + err: "zone field is not set", + }, { + name: "zone missing for dbnode - etcd", + userName: "abc", + password: "xyz", + credtype: EtcdCredential, + err: "zone field is not set", + }, { + name: "cred type missing", + userName: "abc", + password: "xyz", + zone: "foo", + err: "incorrect cred type field for outbound", + }, { + name: "incorrect cred type ", + userName: "abc", + password: "xyz", + zone: "foo", + credtype: ClientCredential, + err: "incorrect cred type field for outbound", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + newCreds := &OutboundCredentials{Username: tt.userName, Password: tt.password, Zone: tt.zone, Type: tt.credtype} + err := newCreds.Validate() + if tt.err != "" { + assert.Contains(t, err.Error(), tt.err) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/src/dbnode/auth/enum.go b/src/dbnode/auth/enum.go new file mode 100644 index 0000000000..5558779bf8 --- /dev/null +++ b/src/dbnode/auth/enum.go @@ -0,0 +1,37 @@ +package auth + +// CredentialType designates credentials for different connection edges. +type CredentialType int + +const ( + // Unknown defines unknown connection edge. + Unknown CredentialType = iota + + // ClientCredential defines m3db client to dbnode connection credentials. + ClientCredential + + // PeerCredential defines dbnode to dbnode connections credentials. + PeerCredential + + // EtcdCredential defines dbnode to etcd connections credentials. + EtcdCredential +) + +// AuthMode designates a type of authentication. +type AuthMode int + +const ( + // AuthModeUnknown is unknown authentication type case. + AuthModeUnknown AuthMode = iota + + // AuthModeNoAuth is no authentication type case. + AuthModeNoAuth + + // AuthModeShadow mode runs authentication in shadow mode. Credentials will be passed + // by respective peers/clients but will not be used to reject RPCs in case of auth failure. + AuthModeShadow + + // AuthModeEnforced mode runs dbnode in enforced authentication mode. RPCs to dbnode will be rejected + // if auth fails. + AuthModeEnforced +) diff --git a/src/dbnode/auth/inbound.go b/src/dbnode/auth/inbound.go new file mode 100644 index 0000000000..803fb974db --- /dev/null +++ b/src/dbnode/auth/inbound.go @@ -0,0 +1,84 @@ +package auth + +import ( + "fmt" + "github.com/uber/tchannel-go/thrift" +) + +// Inbound encapsulates client credentials. +type Inbound struct { + clientCredentials []InboundCredentials + authMode AuthMode +} + +// ValidateCredentials validates the inbound credential and return error accordingly. +func (i *Inbound) ValidateCredentials(creds InboundCredentials) error { + if i.authMode == AuthModeNoAuth { + return nil + } + + if i.authMode == AuthModeShadow { + go func() { + credentialMatched := false + for _, p := range i.clientCredentials { + if i.MatchCredentials(p, creds) { + credentialMatched = true + break + } + } + + if !credentialMatched { + // todo emit a metric + } + }() + return nil + } + if creds.Type == Unknown { + return fmt.Errorf("unknown credential type for dbnode inbound") + } + + if creds.Type != ClientCredential { + return fmt.Errorf("incorrect credential type for dbnode inbound") + } + + if err := creds.Validate(); err != nil { + return err + } + for _, p := range i.clientCredentials { + if i.MatchCredentials(p, creds) { + return nil + } + } + return fmt.Errorf("credential not matched for dbnode inbound") +} + +// ValidateCredentialsFromThriftContext validates inbound credential from thrift context. ValidateCredsFromTCtxHeaders checks for +// call's request headers . +func (i *Inbound) ValidateCredentialsFromThriftContext(tctx thrift.Context, credtype CredentialType) error { + ctxHeaders := tctx.Headers() + userName, ok := ctxHeaders[AUTH_USERNAME] + if !ok { + userName = "" + } + + password, ok := ctxHeaders[AUTH_PASSWORD] + if !ok { + password = "" + } + // todo create digest from the password and pass to handler. + return i.ValidateCredentials( + InboundCredentials{ + Username: userName, + Digest: password, + Type: credtype, + }, + ) +} + +// MatchCredentials compares two inbound credentials and error out accordingly. +func (i *Inbound) MatchCredentials(c1, c2 InboundCredentials) bool { + if c1.Username == c2.Username && c1.Digest == c2.Digest { + return true + } + return false +} diff --git a/src/dbnode/auth/inbound_test.go b/src/dbnode/auth/inbound_test.go new file mode 100644 index 0000000000..b3e2ec96c3 --- /dev/null +++ b/src/dbnode/auth/inbound_test.go @@ -0,0 +1,253 @@ +package auth + +import ( + "context" + "github.com/stretchr/testify/assert" + "github.com/uber/tchannel-go/thrift" + "testing" +) + +func TestInbound_ValidateCredentials(t *testing.T) { + t.Run("valid credentials no auth mode", func(t *testing.T) { + inboundAuth := inboundAuthSetupNoAuthMode() + err := inboundAuth.ValidateCredentials(InboundCredentials{ + Username: "abc", + Digest: "bcd", + Type: ClientCredential, + }) + assert.NoError(t, err) + }) + + t.Run("invalid credentials no auth mode", func(t *testing.T) { + inboundAuth := inboundAuthSetupNoAuthMode() + err := inboundAuth.ValidateCredentials(InboundCredentials{ + Username: "abc1", + Digest: "bcd1", + Type: ClientCredential, + }) + assert.NoError(t, err) + }) + + t.Run("valid credentials shadow mode", func(t *testing.T) { + inboundAuth := inboundAuthSetupShadowMode() + err := inboundAuth.ValidateCredentials(InboundCredentials{ + Username: "abc", + Digest: "bcd", + Type: ClientCredential, + }) + assert.NoError(t, err) + }) + + t.Run("invalid credentials shadow mode", func(t *testing.T) { + inboundAuth := inboundAuthSetupShadowMode() + err := inboundAuth.ValidateCredentials(InboundCredentials{ + Username: "abc1", + Digest: "bcd1", + Type: ClientCredential, + }) + assert.NoError(t, err) + }) + + t.Run("valid credentials enforced mode", func(t *testing.T) { + inboundAuth := inboundAuthSetupEnabledMode() + err := inboundAuth.ValidateCredentials(InboundCredentials{ + Username: "foo", + Digest: "zoo", + Type: ClientCredential, + }) + assert.NoError(t, err) + }) + + t.Run("invalid credentials enforced mode", func(t *testing.T) { + inboundAuth := inboundAuthSetupEnabledMode() + err := inboundAuth.ValidateCredentials(InboundCredentials{ + Username: "abc1", + Digest: "bcd1", + Type: ClientCredential, + }) + assert.Error(t, err) + }) + + t.Run("unknown credential type for inbound", func(t *testing.T) { + inboundAuth := inboundAuthSetupEnabledMode() + err := inboundAuth.ValidateCredentials(InboundCredentials{ + Username: "abc1", + Digest: "bcd1", + Type: Unknown, + }) + assert.Error(t, err) + }) + + t.Run("non client credential type for inbound", func(t *testing.T) { + inboundAuth := inboundAuthSetupEnabledMode() + err := inboundAuth.ValidateCredentials(InboundCredentials{ + Username: "abc1", + Digest: "bcd1", + Type: PeerCredential, + }) + assert.Error(t, err) + }) +} + +func TestInbound_ValidateCredentialsFromThriftContext(t *testing.T) { + t.Run("valid credentials no auth mode", func(t *testing.T) { + inboundAuth := inboundAuthSetupNoAuthMode() + thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) + err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) + assert.NoError(t, err) + }) + + t.Run("invalid credentials no auth mode", func(t *testing.T) { + inboundAuth := inboundAuthSetupNoAuthMode() + thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc1", AUTH_PASSWORD: "bcd1"}) + err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) + assert.NoError(t, err) + }) + + t.Run("valid credentials shadow mode", func(t *testing.T) { + inboundAuth := inboundAuthSetupShadowMode() + thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) + err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) + assert.NoError(t, err) + }) + + t.Run("invalid credentials shadow mode", func(t *testing.T) { + inboundAuth := inboundAuthSetupShadowMode() + thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc1", AUTH_PASSWORD: "bcd1"}) + err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) + assert.NoError(t, err) + }) + + t.Run("valid credentials enforced mode", func(t *testing.T) { + inboundAuth := inboundAuthSetupEnabledMode() + thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) + err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) + assert.NoError(t, err) + }) + + t.Run("invalid credentials enforced mode", func(t *testing.T) { + inboundAuth := inboundAuthSetupEnabledMode() + thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc1", AUTH_PASSWORD: "bcd1"}) + err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) + assert.Error(t, err) + }) + + t.Run("unknown credential type for inbound", func(t *testing.T) { + inboundAuth := inboundAuthSetupEnabledMode() + thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) + err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, Unknown) + assert.Error(t, err) + }) + + t.Run("non client credential type for inbound", func(t *testing.T) { + inboundAuth := inboundAuthSetupEnabledMode() + thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) + err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, PeerCredential) + assert.Error(t, err) + }) + + t.Run("non client credential type for inbound", func(t *testing.T) { + inboundAuth := inboundAuthSetupEnabledMode() + thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{"some_u": "abc", "some_p": "bcd"}) + err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) + assert.Error(t, err) + }) +} + +func TestInbound_MatchCredentials(t *testing.T) { + t.Run("matching credentials", func(t *testing.T) { + inboundAuth := inboundAuthSetupEnabledMode() + c1 := &InboundCredentials{ + Username: "abc", + Digest: "bcd", + Type: ClientCredential, + } + + c2 := &InboundCredentials{ + Username: "abc", + Digest: "bcd", + Type: ClientCredential, + } + + ok := inboundAuth.MatchCredentials(*c1, *c2) + assert.True(t, ok) + }) + + t.Run("non matching credentials", func(t *testing.T) { + inboundAuth := inboundAuthSetupEnabledMode() + c1 := &InboundCredentials{ + Username: "abc", + Digest: "bcd", + Type: ClientCredential, + } + + c2 := &InboundCredentials{ + Username: "abc1", + Digest: "bcd", + Type: ClientCredential, + } + + ok := inboundAuth.MatchCredentials(*c1, *c2) + assert.False(t, ok) + }) +} + +func inboundAuthSetupNoAuthMode() *Inbound { + return &Inbound{ + clientCredentials: []InboundCredentials{ + { + + Username: "abc", + Digest: "bcd", + Type: ClientCredential, + }, { + + Username: "foo", + Digest: "zoo", + Type: ClientCredential, + }, + }, + authMode: AuthModeNoAuth, + } +} + +func inboundAuthSetupShadowMode() *Inbound { + return &Inbound{ + clientCredentials: []InboundCredentials{ + { + Username: "abc", + Digest: "bcd", + Type: ClientCredential, + }, { + + Username: "foo", + Digest: "zoo", + Type: ClientCredential, + }, + }, + authMode: AuthModeShadow, + } +} + +func inboundAuthSetupEnabledMode() *Inbound { + return &Inbound{ + clientCredentials: []InboundCredentials{ + { + + Username: "abc", + Digest: "bcd", + Type: ClientCredential, + }, { + + Username: "foo", + Digest: "zoo", + Type: ClientCredential, + }, + }, + authMode: AuthModeEnforced, + } +} + +func createThriftContextWithHeaders(ctx context.Context, headers map[string]string) thrift.Context { + return thrift.WithHeaders(ctx, headers) +} diff --git a/src/dbnode/auth/integration/setup_config.go b/src/dbnode/auth/integration/setup_config.go new file mode 100644 index 0000000000..602b5a7d7d --- /dev/null +++ b/src/dbnode/auth/integration/setup_config.go @@ -0,0 +1,165 @@ +package integration + +import ( + "github.com/m3db/m3/src/cmd/services/m3dbnode/config" + yaml "gopkg.in/yaml.v2" +) + +const BaseConfigWithAuthEnabled = ` +outbounds: + m3db: + services: + - service: + zone: m3_abc11 + username: m3_bcd11 + password: m3_xyz11 + - service: + zone: m3_abc1 + username: m3_bcd21 + password: m3_xyz21 + + etcd: + services: + - service: + zone: etcd_abc11 + username: etcd_bcd11 + password: etcd_xyz11 + - service: + zone: etcd_abc1 + username: etcd_bcd21 + password: etcd_xyz21 + +inbounds: + m3db: + mode: ENABLED + credentials: + - username: user + digest: digest11 + - username: user2 + digest: digest21 +` + +const BaseConfigWithAuthEnabledRefresh = ` +outbounds: + m3db: + services: + - service: + zone: m3_abc2 + username: m3_bcd2 + password: m3_xyz2 + - service: + zone: m3_abc22 + username: m3_bcd22 + password: m3_xyz22 + + etcd: + services: + - service: + zone: etcd_abc12 + username: etcd_bcd12 + password: etcd_xyz12 + - service: + zone: etcd_abc22 + username: etcd_bcd22 + password: etcd_xyz22 + +inbounds: + m3db: + mode: ENABLED + credentials: + - username: user12 + digest: digest12 + - username: user22 + digest: digest22 +` +const BaseConfigWithAuthDisabled = ` +outbounds: + m3db: + services: + - service: + zone: m3_abc11 + username: m3_bcd11 + password: m3_xyz11 + - service: + zone: m3_abc1 + username: m3_bcd21 + password: m3_xyz21 + + etcd: + services: + - service: + zone: etcd_abc11 + username: etcd_bcd11 + password: etcd_xyz11 + - service: + zone: etcd_abc1 + username: etcd_bcd21 + password: etcd_xyz21 + +inbounds: + m3db: + mode: NONE + credentials: + - username: user + digest: digest11 + - username: user2 + digest: digest21 +` + +const BaseConfigWithOutbounds = ` +outbounds: + m3db: + services: + - service: + zone: m3_abc11 + username: m3_bcd11 + password: m3_xyz11 + - service: + zone: m3_abc1 + username: m3_bcd21 + password: m3_xyz21 + + etcd: + services: + - service: + zone: etcd_abc11 + username: etcd_bcd11 + password: etcd_xyz11 + - service: + zone: etcd_abc1 + username: etcd_bcd21 + password: etcd_xyz21 + +` + +const BaseConfigWithOutboundsrefreshed = ` +outbounds: + m3db: + services: + - service: + zone: m3_abc11 + username: m3_bcd12 + password: m3_xyz12 + - service: + zone: m3_abc1 + username: m3_bcd22 + password: m3_xyz22 + + etcd: + services: + - service: + zone: etcd_abc11 + username: etcd_bcd12 + password: etcd_xyz12 + - service: + zone: etcd_abc1 + username: etcd_bcd22 + password: etcd_xyz22 + +` + +func CreateTestConfigYaml(cfg string) *config.AuthConfig { + newSecrets := &config.AuthConfig{} + yaml.Unmarshal([]byte(cfg), newSecrets) + return newSecrets +} diff --git a/src/dbnode/auth/module.go b/src/dbnode/auth/module.go new file mode 100644 index 0000000000..a2167400ee --- /dev/null +++ b/src/dbnode/auth/module.go @@ -0,0 +1,47 @@ +package auth + +import ( + "sync" +) + +const ( + // AUTH_PASSWORD defines password key in thrift ctx. + AUTH_PASSWORD = "password" + + // AUTH_USERNAME defines username key in thrift ctx. + AUTH_USERNAME = "username" + + // AUTH_ZONE defines zone key in thrift ctx. + AUTH_ZONE = "zone" +) + +var ( + // InboundAuth encapsulates inbound credentials for the dbnode. + InboundAuth *Inbound + InboundLock = &sync.Mutex{} + + // OutboundAuth encapsulates outbound credentials for dbnode and client. + OutboundAuth *Outbound +) + +// PopulateDefaultAuthConfig populates default auth config in case of no AuthConfig is present. +func PopulateDefaultAuthConfig() { + InboundAuth = &Inbound{authMode: AuthModeNoAuth} + OutboundAuth = &Outbound{} +} + +// PopulateInbound populates inbound credentials for dbnode. +func PopulateInbound(clientCredentials []InboundCredentials, authMode AuthMode) { + InboundAuth = &Inbound{ + clientCredentials: clientCredentials, + authMode: authMode, + } +} + +// PopulateOutbound populates outbound credentials for dbnode/clients. +func PopulateOutbound(peerCredentials []OutboundCredentials, etcdCredentials []OutboundCredentials) { + OutboundAuth = &Outbound{ + peerCredentials: peerCredentials, + etcdCredentials: etcdCredentials, + } +} diff --git a/src/dbnode/auth/outbound.go b/src/dbnode/auth/outbound.go new file mode 100644 index 0000000000..54104c666d --- /dev/null +++ b/src/dbnode/auth/outbound.go @@ -0,0 +1,42 @@ +package auth + +import ( + "github.com/uber/tchannel-go/thrift" +) + +// Outbound encapsulates outbound credentials. +type Outbound struct { + peerCredentials []OutboundCredentials + etcdCredentials []OutboundCredentials +} + +// WrapThriftContextWithPeerCreds wraps thrift context with outbound peer credential depending on CredentialType and zone. +func (o *Outbound) WrapThriftContextWithPeerCreds(tctx thrift.Context, zone string) thrift.Context { + for _, peerCred := range o.peerCredentials { + if peerCred.Zone == zone { + return wrapTctxWithCredentials(tctx, peerCred) + } + } + return tctx +} + +func wrapTctxWithCredentials(tCtx thrift.Context, creds OutboundCredentials) thrift.Context { + return thrift.WithHeaders(tCtx, map[string]string{ + AUTH_USERNAME: creds.Username, + AUTH_PASSWORD: creds.Password}, + ) +} + +// FetchOutboundEtcdCredentials fetches outbound etcd credentials. +func (o *Outbound) FetchOutboundEtcdCredentials(zone string) *OutboundCredentials { + for _, peerCred := range o.etcdCredentials { + if peerCred.Zone == zone { + return &OutboundCredentials{ + Username: peerCred.Username, + Password: peerCred.Password, + Zone: zone, + } + } + } + return nil +} diff --git a/src/dbnode/auth/outbound_test.go b/src/dbnode/auth/outbound_test.go new file mode 100644 index 0000000000..1e080c58c9 --- /dev/null +++ b/src/dbnode/auth/outbound_test.go @@ -0,0 +1,82 @@ +package auth + +import ( + "github.com/stretchr/testify/assert" + "github.com/uber/tchannel-go/thrift" + "testing" + "time" +) + +func TestWrapThriftContextWithCreds(t *testing.T) { + outbound := &Outbound{ + peerCredentials: []OutboundCredentials{ + { + Username: "test", + Password: "password", + Zone: "test-zone", + }, + }, + } + tctx, _ := thrift.NewContext(time.Minute) + tctx = thrift.WithHeaders(tctx, map[string]string{ + "key1": "value1", + "key2": "value2", + }) + + wrappedTctx := outbound.WrapThriftContextWithPeerCreds(tctx, "test-zone") + assert.Equal(t, "test", wrappedTctx.Headers()["username"], "Expected username in wrapped context headers") + assert.Equal(t, "password", wrappedTctx.Headers()["password"], "Expected password in wrapped context headers") + + assert.Equal(t, "", wrappedTctx.Headers()["key1"], "Expected initial keys must be flushed") + assert.Equal(t, "", wrappedTctx.Headers()["key2"], "Expected initial keys must be flushed") +} + +func TestWrapThriftContextWithOutboundPeerCredsNil(t *testing.T) { + outbound := &Outbound{} + tctx, _ := thrift.NewContext(time.Minute) + tctx = thrift.WithHeaders(tctx, map[string]string{ + "key1": "value1", + "key2": "value2", + }) + + wrappedTctx := outbound.WrapThriftContextWithPeerCreds(tctx, "test-zone") + assert.Equal(t, "", wrappedTctx.Headers()["username"], "Expected no username in wrapped context headers") + assert.Equal(t, "", wrappedTctx.Headers()["password"], "Expected no password in wrapped context headers") + + assert.Equal(t, "value1", wrappedTctx.Headers()["key1"], "Expected initial keys must be present") + assert.Equal(t, "value2", wrappedTctx.Headers()["key2"], "Expected initial keys must be present") +} + +func TestFetchOutboundEtcdCredentials(t *testing.T) { + outbound := &Outbound{ + etcdCredentials: []OutboundCredentials{ + { + Username: "test", + Password: "password", + Zone: "test-zone", + }, + }, + } + creds := outbound.FetchOutboundEtcdCredentials("test-zone") + assert.Equal(t, &outbound.etcdCredentials[0], creds, "Expected no error with matching zone creds") +} + +func TestWrapThriftContextWithOutboundEtcdCredsNil(t *testing.T) { + outbound := &Outbound{} + creds := outbound.FetchOutboundEtcdCredentials("test-zone") + assert.Nil(t, creds, "Expected no credential") +} + +func TestFetchOutboundEtcdCredentialsIncorrectZone(t *testing.T) { + outbound := &Outbound{ + etcdCredentials: []OutboundCredentials{ + { + Username: "test", + Password: "password", + Zone: "test-zone", + }, + }, + } + creds := outbound.FetchOutboundEtcdCredentials("foo") + assert.Nil(t, creds, "Expected response obj to be nil") +} diff --git a/src/dbnode/client/client_creds_provider.go b/src/dbnode/client/client_creds_provider.go new file mode 100644 index 0000000000..6345115b02 --- /dev/null +++ b/src/dbnode/client/client_creds_provider.go @@ -0,0 +1,45 @@ +package client + +import ( + "github.com/m3db/m3/src/dbnode/auth" + "github.com/m3db/m3/src/dbnode/environment" +) + +// PopulateClientOutboundAuthConfig takes clients dynamic config as populate auth global registry. +func PopulateClientOutboundAuthConfig(cfg environment.DynamicConfiguration) error { + var dbnodeCredentials []auth.OutboundCredentials + var etcdCredentails []auth.OutboundCredentials + + for _, clusterCfg := range cfg { + if ok := clusterCfg.Service.Auth.Enabled; ok { + if err := clusterCfg.Service.Auth.Validate(); err != nil { + return err + } + nodeCredential := auth.OutboundCredentials{ + Username: clusterCfg.Service.Auth.UserName, + Password: clusterCfg.Service.Auth.Password, + Zone: clusterCfg.Service.Zone, + Type: auth.ClientCredential, + } + dbnodeCredentials = append(dbnodeCredentials, nodeCredential) + } + + etcdClusters := clusterCfg.Service.ETCDClusters + for _, etcdCluster := range etcdClusters { + if ok := etcdCluster.Auth.Enabled; ok { + if err := etcdCluster.Auth.Validate(); err != nil { + return err + } + etcdCredential := auth.OutboundCredentials{ + Username: etcdCluster.Auth.UserName, + Password: etcdCluster.Auth.Password, + Zone: etcdCluster.Zone, + Type: auth.EtcdCredential, + } + etcdCredentails = append(etcdCredentails, etcdCredential) + } + } + } + auth.PopulateOutbound(dbnodeCredentials, etcdCredentails) + return nil +} diff --git a/src/dbnode/client/client_creds_provider_test.go b/src/dbnode/client/client_creds_provider_test.go new file mode 100644 index 0000000000..58fc89811c --- /dev/null +++ b/src/dbnode/client/client_creds_provider_test.go @@ -0,0 +1,185 @@ +package client + +import ( + "github.com/m3db/m3/src/dbnode/auth" + "os" + "testing" + + xconfig "github.com/m3db/m3/src/x/config" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestConfigurationWithAuthClientProvider(t *testing.T) { + clientCfg := ` + config: + service: + env: default_env + zone: embedded + service: m3db + auth: + enabled: true + username: user_db + password: pass_db + etcdClusters: + - zone: embedded + endpoints: + - etcd:2379 + - etcd:456 + auth: + enabled: true + username: user_etcd + password: pass_etcd + writeConsistencyLevel: majority + readConsistencyLevel: unstrict_majority +` + fd, err := os.CreateTemp("", "config.yaml") + require.NoError(t, err) + defer func() { + assert.NoError(t, fd.Close()) + assert.NoError(t, os.Remove(fd.Name())) + }() + + _, err = fd.Write([]byte(clientCfg)) + require.NoError(t, err) + + var cfg Configuration + err = xconfig.LoadFile(&cfg, fd.Name(), xconfig.Options{}) + require.NoError(t, err) + + defer outboundAuthCleanup() + outboundErr := PopulateClientOutboundAuthConfig(cfg.EnvironmentConfig.Services) + require.NoError(t, outboundErr) +} + +func TestConfigurationWithIncorrectDbAuth(t *testing.T) { + clientCfg := ` + config: + service: + env: default_env + zone: embedded + service: m3db + auth: + enabled: true + password: pass_db + etcdClusters: + - zone: embedded + endpoints: + - etcd:2379 + - etcd:456 + auth: + enabled: true + username: user_etcd + password: pass_etcd + writeConsistencyLevel: majority + readConsistencyLevel: unstrict_majority +` + fd, err := os.CreateTemp("", "config.yaml") + require.NoError(t, err) + defer func() { + assert.NoError(t, fd.Close()) + assert.NoError(t, os.Remove(fd.Name())) + }() + + _, err = fd.Write([]byte(clientCfg)) + require.NoError(t, err) + + var cfg Configuration + err = xconfig.LoadFile(&cfg, fd.Name(), xconfig.Options{}) + require.NoError(t, err) + + defer outboundAuthCleanup() + outboundErr := PopulateClientOutboundAuthConfig(cfg.EnvironmentConfig.Services) + require.Error(t, outboundErr) +} + +func TestConfigurationWithIncorrectEtcdAuth(t *testing.T) { + clientCfg := ` + config: + service: + env: default_env + zone: embedded + service: m3db + auth: + enabled: true + username: user_db + password: pass_db + etcdClusters: + - zone: embedded + endpoints: + - etcd:2379 + - etcd:456 + auth: + enabled: true + password: pass_etcd + writeConsistencyLevel: majority + readConsistencyLevel: unstrict_majority +` + fd, err := os.CreateTemp("", "config.yaml") + require.NoError(t, err) + defer func() { + assert.NoError(t, fd.Close()) + assert.NoError(t, os.Remove(fd.Name())) + }() + + _, err = fd.Write([]byte(clientCfg)) + require.NoError(t, err) + + var cfg Configuration + err = xconfig.LoadFile(&cfg, fd.Name(), xconfig.Options{}) + require.NoError(t, err) + + defer outboundAuthCleanup() + outboundErr := PopulateClientOutboundAuthConfig(cfg.EnvironmentConfig.Services) + require.Error(t, outboundErr) +} + +func TestConfigurationNoAuth(t *testing.T) { + clientCfg := ` + config: + service: + env: default_env + zone: embedded + service: m3db + auth: + enabled: false + username: user_db + password: pass_db + etcdClusters: + - zone: embedded + endpoints: + - etcd:2379 + - etcd:456 + auth: + enabled: false + password: pass_etcd + writeConsistencyLevel: majority + readConsistencyLevel: unstrict_majority +` + fd, err := os.CreateTemp("", "config.yaml") + require.NoError(t, err) + defer func() { + assert.NoError(t, fd.Close()) + assert.NoError(t, os.Remove(fd.Name())) + }() + + _, err = fd.Write([]byte(clientCfg)) + require.NoError(t, err) + + var cfg Configuration + err = xconfig.LoadFile(&cfg, fd.Name(), xconfig.Options{}) + require.NoError(t, err) + + defer outboundAuthCleanup() + outboundErr := PopulateClientOutboundAuthConfig(cfg.EnvironmentConfig.Services) + require.NoError(t, outboundErr) +} + +func inboundAuthCleanup() { + auth.InboundAuth = &auth.Inbound{} +} + +func outboundAuthCleanup() { + auth.OutboundAuth = &auth.Outbound{} +} diff --git a/src/dbnode/client/config.go b/src/dbnode/client/config.go index 2e1a9cb6ae..46f0e01293 100644 --- a/src/dbnode/client/config.go +++ b/src/dbnode/client/config.go @@ -338,7 +338,8 @@ func (c Configuration) NewAdminClient( SetInstrumentOptions(iopts). SetLogErrorSampleRate(c.LogErrorSampleRate). SetLogHostWriteErrorSampleRate(c.LogHostWriteErrorSampleRate). - SetLogHostFetchErrorSampleRate(c.LogHostFetchErrorSampleRate) + SetLogHostFetchErrorSampleRate(c.LogHostFetchErrorSampleRate). + SetTopologyInitializerZone(syncTopoInit.FetchZone()) if params.ClockOptions != nil { v = v.SetClockOptions(params.ClockOptions) @@ -474,6 +475,8 @@ func (c Configuration) NewAdminClient( opts = opt(opts) } + PopulateClientOutboundAuthConfig(c.EnvironmentConfig.Services) + asyncClusterOpts := NewOptionsForAsyncClusters(opts, asyncTopoInits, asyncClientOverrides) return NewAdminClient(opts, asyncClusterOpts...) } diff --git a/src/dbnode/client/connection_pool.go b/src/dbnode/client/connection_pool.go index a7c2d90db2..24e70c4053 100644 --- a/src/dbnode/client/connection_pool.go +++ b/src/dbnode/client/connection_pool.go @@ -65,6 +65,7 @@ type connPool struct { sleepHealthRetry sleepFn status status healthStatus tally.Gauge + zone string } type conn struct { @@ -103,6 +104,7 @@ func newConnectionPool(host topology.Host, opts Options) connectionPool { sleepHealth: time.Sleep, sleepHealthRetry: time.Sleep, healthStatus: scope.Gauge("health-status"), + zone: opts.FetchTopologyInitializerZone(), } return p @@ -164,6 +166,10 @@ func (p *connPool) Close() { } } +func (p *connPool) GetZone() string { + return p.zone +} + func (p *connPool) connectEvery(interval time.Duration, stutter time.Duration) { log := p.opts.InstrumentOptions().Logger() target := p.opts.MaxConnectionCount() diff --git a/src/dbnode/client/options.go b/src/dbnode/client/options.go index 402336d278..3da3c9da71 100644 --- a/src/dbnode/client/options.go +++ b/src/dbnode/client/options.go @@ -296,6 +296,7 @@ type options struct { writeTimestampOffset time.Duration namespaceInitializer namespace.Initializer thriftContextFn ThriftContextFn + zone string } // NewOptions creates a new set of client options with defaults @@ -320,6 +321,9 @@ func NewOptionsForAsyncClusters(opts Options, topoInits []topology.Initializer, if overrides[i].TargetHostQueueFlushSize != nil { options = options.SetHostQueueOpsFlushSize(*overrides[i].TargetHostQueueFlushSize) } + if topoInit.FetchZone() != "" { + options = options.SetTopologyInitializerZone(topoInit.FetchZone()) + } result = append(result, options) } return result @@ -1171,3 +1175,13 @@ func (o *options) SetThriftContextFn(value ThriftContextFn) Options { func (o *options) ThriftContextFn() ThriftContextFn { return o.thriftContextFn } + +func (o *options) SetTopologyInitializerZone(value string) Options { + opts := *o + opts.zone = value + return &opts +} + +func (o *options) FetchTopologyInitializerZone() string { + return o.zone +} diff --git a/src/dbnode/client/types.go b/src/dbnode/client/types.go index 78206bd158..ba8f918c58 100644 --- a/src/dbnode/client/types.go +++ b/src/dbnode/client/types.go @@ -731,6 +731,10 @@ type Options interface { // ThriftContextFn returns the retrier for streaming blocks. ThriftContextFn() ThriftContextFn + + SetTopologyInitializerZone(value string) Options + + FetchTopologyInitializerZone() string } // ThriftContextFn turns a context into a thrift context for a thrift call. @@ -848,6 +852,8 @@ type connectionPool interface { // Close the connection pool. Close() + + GetZone() string } type peerSource interface { diff --git a/src/dbnode/environment/config.go b/src/dbnode/environment/config.go index 64a697c959..3056301d9f 100644 --- a/src/dbnode/environment/config.go +++ b/src/dbnode/environment/config.go @@ -177,6 +177,7 @@ type ConfigureResult struct { KVStore kv.Store Async bool ClientOverrides ClientOverrides + Zone string } // ConfigureResults stores initializers and kv store for dynamic and static configs @@ -334,6 +335,7 @@ func (c Configuration) configureDynamic(cfgParams ConfigurationParameters) (Conf KVStore: kv, Async: cluster.Async, ClientOverrides: cluster.ClientOverrides, + Zone: cluster.Service.Zone, } cfgResults = append(cfgResults, result) } diff --git a/src/dbnode/server/server.go b/src/dbnode/server/server.go index b1b856c4b4..32b29aa60e 100644 --- a/src/dbnode/server/server.go +++ b/src/dbnode/server/server.go @@ -25,6 +25,7 @@ import ( "context" "errors" "fmt" + "github.com/m3db/m3/src/dbnode/auth" "io" "math" "net/http" @@ -129,6 +130,9 @@ type RunOptions struct { // instead of parsing ConfigFile if ConfigFile is not specified. Config config.DBConfiguration + // Secrets encapsulates auth config for dbnode inbounds and outbounds. + Secrets config.AuthConfig + // BootstrapCh is a channel to listen on to be notified of bootstrap. BootstrapCh chan<- struct{} @@ -192,6 +196,15 @@ func Run(runOpts RunOptions) { os.Exit(1) } + secretsValidateErr := runOpts.Secrets.Validate() + if secretsValidateErr == nil { + // Passing valid parsed AuthConfig to populate inbound and outbound credentials. + PopulateOutboundAuthConfig(runOpts.Secrets) + PopulateInboundAuthConfig(runOpts.Secrets) + } else { + auth.PopulateDefaultAuthConfig() + } + logger, err := cfg.LoggingOrDefault().BuildLogger() if err != nil { // NB(r): Use fmt.Fprintf(os.Stderr, ...) to avoid etcd.SetGlobals() diff --git a/src/dbnode/server/server_creds_provider.go b/src/dbnode/server/server_creds_provider.go new file mode 100644 index 0000000000..ed7ea8438c --- /dev/null +++ b/src/dbnode/server/server_creds_provider.go @@ -0,0 +1,73 @@ +package server + +import ( + "github.com/m3db/m3/src/cmd/services/m3dbnode/config" + "github.com/m3db/m3/src/dbnode/auth" + "strings" +) + +var ( + authModeMap = map[string]auth.AuthMode{ + "none": auth.AuthModeNoAuth, + "shadow": auth.AuthModeShadow, + "enabled": auth.AuthModeEnforced, + } +) + +// PopulateInboundAuthConfig populates inbound auth modules with the provided auth config. +func PopulateInboundAuthConfig(cfg config.AuthConfig) { + var inboundAuth []auth.InboundCredentials + nodeInbound := cfg.Inbound.M3DB + for _, nodeCfg := range nodeInbound.Credentials { + inboundAuth = append(inboundAuth, auth.InboundCredentials{ + Username: *nodeCfg.Username, + Digest: *nodeCfg.Digest, + Type: auth.ClientCredential, + }) + } + authMode := parseAuthMode(*cfg.Inbound.M3DB.Mode) + auth.PopulateInbound(inboundAuth, authMode) +} + +// RefreshInboundAuthConfig take AuthConfig as input param and populates inbound credentials global module. +func RefreshInboundAuthConfig(credentialsConfig config.AuthConfig) { + auth.InboundLock.Lock() + defer auth.InboundLock.Unlock() + PopulateInboundAuthConfig(credentialsConfig) +} + +// PopulateOutboundAuthConfig populates outbound auth modules with the provided auth config. +func PopulateOutboundAuthConfig(cfg config.AuthConfig) { + var outboundPeerAuth []auth.OutboundCredentials + nodeOutboundPeer := cfg.Outbound.M3DB + + for _, nodeCfg := range nodeOutboundPeer.NodeConfig { + outboundPeerAuth = append(outboundPeerAuth, auth.OutboundCredentials{ + Username: *nodeCfg.Service.Username, + Password: *nodeCfg.Service.Password, + Zone: nodeCfg.Service.Zone, + Type: auth.PeerCredential, + }) + } + + var outboundEtcdAuth []auth.OutboundCredentials + nodeOutboundEtcd := cfg.Outbound.Etcd + + for _, nodeCfg := range nodeOutboundEtcd.NodeConfig { + outboundEtcdAuth = append(outboundEtcdAuth, auth.OutboundCredentials{ + Username: *nodeCfg.Service.Username, + Password: *nodeCfg.Service.Password, + Zone: nodeCfg.Service.Zone, + Type: auth.EtcdCredential, + }) + } + auth.PopulateOutbound(outboundPeerAuth, outboundEtcdAuth) +} + +func parseAuthMode(str string) auth.AuthMode { + c, ok := authModeMap[strings.ToLower(str)] + if !ok { + return auth.AuthModeUnknown + } + return c +} diff --git a/src/dbnode/server/server_creds_provider_test.go b/src/dbnode/server/server_creds_provider_test.go new file mode 100644 index 0000000000..c0afd2da98 --- /dev/null +++ b/src/dbnode/server/server_creds_provider_test.go @@ -0,0 +1,144 @@ +package server + +import ( + "github.com/m3db/m3/src/dbnode/auth" + "github.com/m3db/m3/src/dbnode/auth/integration" + "github.com/stretchr/testify/assert" + "github.com/uber/tchannel-go/thrift" + "testing" + "time" +) + +func TestInboundCfgPopulate(t *testing.T) { + defer inboundAuthCleanup() + authCfg := integration.CreateTestConfigYaml(integration.BaseConfigWithAuthEnabled) + + PopulateInboundAuthConfig(*authCfg) + + for _, nodeCfg := range authCfg.Inbound.M3DB.Credentials { + err := auth.InboundAuth.ValidateCredentials(auth.InboundCredentials{ + Username: *nodeCfg.Username, + Digest: *nodeCfg.Digest, + Type: auth.ClientCredential, + }) + assert.NoError(t, err) + } +} + +func TestInboundCfgPopulateWithRefresh(t *testing.T) { + defer inboundAuthCleanup() + authCfg := integration.CreateTestConfigYaml(integration.BaseConfigWithAuthEnabled) + + PopulateInboundAuthConfig(*authCfg) + + for _, nodeCfg := range authCfg.Inbound.M3DB.Credentials { + err := auth.InboundAuth.ValidateCredentials(auth.InboundCredentials{ + Username: *nodeCfg.Username, + Digest: *nodeCfg.Digest, + Type: auth.ClientCredential, + }) + assert.NoError(t, err) + } + + refreshAuthCfg := integration.CreateTestConfigYaml(integration.BaseConfigWithAuthEnabledRefresh) + RefreshInboundAuthConfig(*refreshAuthCfg) + + for _, nodeCfg := range refreshAuthCfg.Inbound.M3DB.Credentials { + err := auth.InboundAuth.ValidateCredentials(auth.InboundCredentials{ + Username: *nodeCfg.Username, + Digest: *nodeCfg.Digest, + Type: auth.ClientCredential, + }) + assert.NoError(t, err) + } +} + +func TestInboundCfgPopulateWithIncorrectCredsAuthDisabled(t *testing.T) { + defer inboundAuthCleanup() + authCfg := integration.CreateTestConfigYaml(integration.BaseConfigWithAuthDisabled) + PopulateInboundAuthConfig(*authCfg) + + for _, nodeCfg := range authCfg.Inbound.M3DB.Credentials { + err := auth.InboundAuth.ValidateCredentials(auth.InboundCredentials{ + Username: "some_random_u", + Digest: *nodeCfg.Digest, + Type: auth.ClientCredential, + }) + assert.NoError(t, err) + } + + refreshAuthCfg := integration.CreateTestConfigYaml(integration.BaseConfigWithAuthEnabledRefresh) + RefreshInboundAuthConfig(*refreshAuthCfg) + + for _, nodeCfg := range authCfg.Inbound.M3DB.Credentials { + err := auth.InboundAuth.ValidateCredentials(auth.InboundCredentials{ + Username: *nodeCfg.Username, + Digest: "some_random_p", + Type: auth.ClientCredential, + }) + assert.Error(t, err) + } +} + +func TestInboundCfgPopulateWithCorrectCredsAuthDisabledRefreshAuthEnabled(t *testing.T) { + defer inboundAuthCleanup() + authCfg := integration.CreateTestConfigYaml(integration.BaseConfigWithAuthDisabled) + PopulateInboundAuthConfig(*authCfg) + + for _, nodeCfg := range authCfg.Inbound.M3DB.Credentials { + err := auth.InboundAuth.ValidateCredentials(auth.InboundCredentials{ + Username: "some_random_u", + Digest: *nodeCfg.Digest, + Type: auth.ClientCredential, + }) + assert.NoError(t, err) + } + + refreshAuthCfg := integration.CreateTestConfigYaml(integration.BaseConfigWithAuthEnabledRefresh) + RefreshInboundAuthConfig(*refreshAuthCfg) + + for _, nodeCfg := range refreshAuthCfg.Inbound.M3DB.Credentials { + err := auth.InboundAuth.ValidateCredentials(auth.InboundCredentials{ + Username: *nodeCfg.Username, + Digest: *nodeCfg.Digest, + Type: auth.ClientCredential, + }) + assert.NoError(t, err) + } +} + +func TestPeerOutboundCfgPopulate(t *testing.T) { + defer outboundAuthCleanup() + authCfg := integration.CreateTestConfigYaml(integration.BaseConfigWithOutbounds) + + PopulateOutboundAuthConfig(*authCfg) + tCtx, _ := thrift.NewContext(time.Minute) + + for _, nodeCfg := range authCfg.Outbound.M3DB.NodeConfig { + newTctx := auth.OutboundAuth.WrapThriftContextWithPeerCreds(tCtx, nodeCfg.Service.Zone) + assert.Equal(t, *nodeCfg.Service.Username, newTctx.Headers()["username"]) + assert.Equal(t, *nodeCfg.Service.Password, newTctx.Headers()["password"]) + } +} + +func TestEtcdOutboundCfgPopulate(t *testing.T) { + defer outboundAuthCleanup() + authCfg := integration.CreateTestConfigYaml(integration.BaseConfigWithOutbounds) + + PopulateOutboundAuthConfig(*authCfg) + + for _, nodeCfg := range authCfg.Outbound.Etcd.NodeConfig { + newTctx := auth.OutboundAuth.FetchOutboundEtcdCredentials(nodeCfg.Service.Zone) + assert.Equal(t, *nodeCfg.Service.Username, newTctx.Username) + assert.Equal(t, *nodeCfg.Service.Password, newTctx.Password) + assert.Equal(t, nodeCfg.Service.Zone, newTctx.Zone) + } +} + +func inboundAuthCleanup() { + auth.InboundAuth = &auth.Inbound{} +} + +func outboundAuthCleanup() { + auth.OutboundAuth = &auth.Outbound{} +} diff --git a/src/dbnode/topology/dynamic.go b/src/dbnode/topology/dynamic.go index 2c9d06251b..6cc2038b54 100644 --- a/src/dbnode/topology/dynamic.go +++ b/src/dbnode/topology/dynamic.go @@ -46,6 +46,7 @@ type dynamicInitializer struct { sync.Mutex opts DynamicOptions topo Topology + zone string } // NewDynamicInitializer returns a dynamic topology initializer @@ -93,6 +94,10 @@ func (i *dynamicInitializer) TopologyIsSet() (bool, error) { return true, nil } +func (i *dynamicInitializer) FetchZone() string { + return i.zone +} + type dynamicTopology struct { sync.RWMutex opts DynamicOptions diff --git a/src/dbnode/topology/static.go b/src/dbnode/topology/static.go index c3bd9515a4..bbe1f9f1ad 100644 --- a/src/dbnode/topology/static.go +++ b/src/dbnode/topology/static.go @@ -51,6 +51,10 @@ func (i staticInitializer) TopologyIsSet() (bool, error) { return true, nil } +func (i staticInitializer) FetchZone() string { + return "" +} + type staticTopology struct { w xwatch.Watchable } diff --git a/src/dbnode/topology/types.go b/src/dbnode/topology/types.go index 1756b93bbd..1c80f0304b 100644 --- a/src/dbnode/topology/types.go +++ b/src/dbnode/topology/types.go @@ -59,6 +59,8 @@ type Initializer interface { // initialized immediately or if instead it will blockingly // wait to be set on initialization TopologyIsSet() (bool, error) + + FetchZone() string } // Topology is a container of a topology map and disseminates topology map changes diff --git a/src/x/config/configflag/flag.go b/src/x/config/configflag/flag.go index 2e1f5bd873..2cec0ab006 100644 --- a/src/x/config/configflag/flag.go +++ b/src/x/config/configflag/flag.go @@ -38,6 +38,9 @@ type Options struct { // ConfigFiles (-f) is a list of config files to load ConfigFiles FlagStringSlice + // CredentialsFile (-c) loads secrets yaml files. + CredentialsFile FlagStringSlice + // ShouldDumpConfigAndExit (-d) causes MainLoad to print config to stdout, // and then exit. ShouldDumpConfigAndExit bool @@ -59,13 +62,15 @@ func (opts *Options) RegisterFlagSet(cmd *flag.FlagSet) { opts.cmd = cmd cmd.Var(&opts.ConfigFiles, "f", "Configuration files to load") + cmd.Var(&opts.CredentialsFile, "c", "Credential files") cmd.BoolVar(&opts.ShouldDumpConfigAndExit, "d", false, "Dump configuration and exit") } // MainLoad is a convenience method, intended for use in main(), which handles all // config commandline options. It: -// - Dumps config and exits if -d was passed. -// - Loads configuration otherwise. +// - Dumps config and exits if -d was passed. +// - Loads configuration otherwise. +// // Users who want a subset of this behavior should call individual methods. func (opts *Options) MainLoad(target interface{}, loadOpts config.Options) error { osFns := opts.osFns @@ -92,12 +97,35 @@ func (opts *Options) MainLoad(target interface{}, loadOpts config.Options) error return nil } +// CredentialLoad is a method used in main(), which handles secrets commandline options. It loads the secrets yaml +// file if present with -c flag. +func (opts *Options) CredentialLoad(target interface{}, loadOpts config.Options) (bool, error) { + osFns := opts.osFns + if osFns == nil { + osFns = realOS{} + } + + if len(opts.CredentialsFile.Value) == 0 { + opts.cmd.Usage() + return false, nil + } + + if err := config.LoadFiles(target, opts.CredentialsFile.Value, loadOpts); err != nil { + return false, fmt.Errorf("unable to load secrets from %s: %v", opts.CredentialsFile.Value, err) + } + return true, nil +} + // FlagStringSlice represents a slice of strings. When used as a flag variable, // it allows for multiple string values. For example, it can be used like this: -// var configFiles FlagStringSlice -// flag.Var(&configFiles, "f", "configuration file(s)") +// +// var configFiles FlagStringSlice +// flag.Var(&configFiles, "f", "configuration file(s)") +// // Then it can be invoked like this: -// ./app -f file1.yaml -f file2.yaml -f valueN.yaml +// +// ./app -f file1.yaml -f file2.yaml -f valueN.yaml +// // Finally, when the flags are parsed, the variable contains all the values. type FlagStringSlice struct { Value []string diff --git a/src/x/config/configflag/flag_test.go b/src/x/config/configflag/flag_test.go index 25cae70922..06491b008f 100644 --- a/src/x/config/configflag/flag_test.go +++ b/src/x/config/configflag/flag_test.go @@ -67,6 +67,8 @@ func TestCommandLineOptions(t *testing.T) { expectedConfig := testConfig{Foo: 1, Bar: "bar"} configFileOpts := []string{"-f", "./testdata/config1.yaml", "-f", "./testdata/config2.yaml"} + configSecretFileOpts := []string{"-c", "./testdata/config1.yaml", "-c", "./testdata/config2.yaml"} + t.Run("loads config from files", func(t *testing.T) { tctx, teardown := setup(t) defer teardown() @@ -109,6 +111,30 @@ func TestCommandLineOptions(t *testing.T) { "-f is required (no config files provided)") assert.True(t, tctx.UsageCalled) }) + t.Run("loads yaml files from -c flag", func(t *testing.T) { + tctx, teardown := setup(t) + defer teardown() + + require.NoError(t, tctx.Flags.Parse(configSecretFileOpts)) + + var cfg testConfig + _, err := tctx.Opts.CredentialLoad(&cfg, config.Options{}) + require.NoError(t, err) + assert.Equal(t, expectedConfig, cfg) + }) + + t.Run("loads yaml files from -c flag", func(t *testing.T) { + tctx, teardown := setup(t) + defer teardown() + + require.NoError(t, tctx.Flags.Parse([]string{})) + + var cfg testConfig + isPresent, err := tctx.Opts.CredentialLoad(&cfg, config.Options{}) + require.NoError(t, err) + require.False(t, isPresent) + }) + } func TestFlagArray(t *testing.T) { From 748090f55ac2df85c82fe7c28aafd790c575a793 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Tue, 23 May 2023 09:24:46 +0530 Subject: [PATCH 02/12] lint fixes --- src/cluster/client/etcd/config.go | 21 -- src/cmd/services/m3dbnode/config/auth_test.go | 213 +++++------------- src/dbnode/auth/inbound.go | 4 +- src/dbnode/auth/inbound_test.go | 40 +++- src/dbnode/auth/integration/setup_config.go | 15 +- src/dbnode/auth/outbound.go | 3 +- .../server/server_creds_provider_test.go | 5 +- src/x/config/configflag/flag.go | 5 - src/x/config/configflag/flag_test.go | 1 - 9 files changed, 107 insertions(+), 200 deletions(-) diff --git a/src/cluster/client/etcd/config.go b/src/cluster/client/etcd/config.go index 394a339b30..5e4aa8499a 100644 --- a/src/cluster/client/etcd/config.go +++ b/src/cluster/client/etcd/config.go @@ -21,7 +21,6 @@ package etcd import ( - "fmt" "os" "time" @@ -53,8 +52,6 @@ type ClusterConfig struct { AutoSyncInterval time.Duration `yaml:"autoSyncInterval"` DialTimeout time.Duration `yaml:"dialTimeout"` - Auth *AuthConfig `yaml:"auth"` - DialOptions []grpc.DialOption `yaml:"-"` // nonserializable } @@ -71,7 +68,6 @@ func (c ClusterConfig) NewCluster() Cluster { SetDialOptions(c.DialOptions). SetKeepAliveOptions(keepAliveOpts). SetTLSOptions(c.TLS.newOptions()) - // Autosync should *always* be on, unless the user very explicitly requests it to be off. They can do this via a // negative value (in which case we can assume they know what they're doing). // Therefore, only update if it's nonzero, on the assumption that zero is just the empty value. @@ -122,22 +118,6 @@ func (c *KeepAliveConfig) NewOptions() KeepAliveOptions { SetKeepAliveTimeout(c.Timeout) } -// AuthConfig configures authentication behavior. -type AuthConfig struct { - Enabled bool `yaml:"enabled"` - UserName string `yaml:"username"` - Password string `yaml:"password"` -} - -func (a *AuthConfig) Validate() error { - if a.Enabled { - if a.UserName == "" || a.Password == "" { - return fmt.Errorf("credentials must be set for client's outbound") - } - } - return nil -} - // Configuration is for config service client. type Configuration struct { Zone string `yaml:"zone"` @@ -148,7 +128,6 @@ type Configuration struct { SDConfig services.Configuration `yaml:"m3sd"` WatchWithRevision int64 `yaml:"watchWithRevision"` NewDirectoryMode *os.FileMode `yaml:"newDirectoryMode"` - Auth *AuthConfig `yaml:"auth"` Retry retry.Configuration `yaml:"retry"` RequestTimeout time.Duration `yaml:"requestTimeout"` diff --git a/src/cmd/services/m3dbnode/config/auth_test.go b/src/cmd/services/m3dbnode/config/auth_test.go index b7729f924b..98d32d54af 100644 --- a/src/cmd/services/m3dbnode/config/auth_test.go +++ b/src/cmd/services/m3dbnode/config/auth_test.go @@ -1,6 +1,7 @@ package config import ( + "errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" yaml "gopkg.in/yaml.v2" @@ -101,161 +102,61 @@ inbounds: digest: digest2 ` -const incompleteCreds = ` -outbounds: - m3db: - services: - - service: - zone: m3_abc - username: m3_bcd - - service: - zone: m3_abc1 - username: m3_bcd1 - password: m3_xyz1 - - etcd: - services: - - service: - zone: etcd_abc - username: etcd_bcd - password: etcd_xyz - - service: - zone: etcd_abc1 - username: etcd_bcd1 - password: etcd_xyz1 - -inbounds: - m3db: - mode: NONE - credentials: - - username: user - - username: user2 - digest: digest2 -` - func TestAuthConfiguration(t *testing.T) { - t.Run("correct secrets file", func(t *testing.T) { - fd, crtErr := os.CreateTemp("", "secrets.yaml") - require.NoError(t, crtErr) - defer func() { - assert.NoError(t, fd.Close()) - assert.NoError(t, os.Remove(fd.Name())) - }() - - _, wrtErr := fd.Write([]byte(testBaseAuthConfig)) - require.NoError(t, wrtErr) - - f, opnErr := os.Open(fd.Name()) - require.NoError(t, opnErr) - - all, rdErr := io.ReadAll(f) - require.NoError(t, rdErr) - - newSecrets := &AuthConfig{} - unmarErr := yaml.Unmarshal(all, newSecrets) - require.NoError(t, unmarErr) - - err := newSecrets.Validate() - require.NoError(t, err) - }) - - t.Run("secrets file missing outbounds", func(t *testing.T) { - fd, crtErr := os.CreateTemp("", "secrets.yaml") - require.NoError(t, crtErr) - defer func() { - assert.NoError(t, fd.Close()) - assert.NoError(t, os.Remove(fd.Name())) - }() - - _, wrtErr := fd.Write([]byte(missingOutbounds)) - require.NoError(t, wrtErr) - - f, opnErr := os.Open(fd.Name()) - require.NoError(t, opnErr) - - all, rdErr := io.ReadAll(f) - require.NoError(t, rdErr) - - newSecrets := &AuthConfig{} - unmarErr := yaml.Unmarshal(all, newSecrets) - require.NoError(t, unmarErr) - - err := newSecrets.Validate() - require.Error(t, err, "outbound creds are not present") - }) - - t.Run("secrets file missing m3db outbounds", func(t *testing.T) { - fd, crtErr := os.CreateTemp("", "secrets.yaml") - require.NoError(t, crtErr) - defer func() { - assert.NoError(t, fd.Close()) - assert.NoError(t, os.Remove(fd.Name())) - }() - - _, wrtErr := fd.Write([]byte(missingM3dbConfigOutbounds)) - require.NoError(t, wrtErr) - - f, opnErr := os.Open(fd.Name()) - require.NoError(t, opnErr) - - all, rdErr := io.ReadAll(f) - require.NoError(t, rdErr) - - newSecrets := &AuthConfig{} - unmarErr := yaml.Unmarshal(all, newSecrets) - require.NoError(t, unmarErr) - - err := newSecrets.Validate() - require.Error(t, err, "incomplete outbound creds, m3db node creds not provided") - }) - - t.Run("secrets file missing etcd outbounds", func(t *testing.T) { - fd, crtErr := os.CreateTemp("", "secrets.yaml") - require.NoError(t, crtErr) - defer func() { - assert.NoError(t, fd.Close()) - assert.NoError(t, os.Remove(fd.Name())) - }() - - _, wrtErr := fd.Write([]byte(missingETCDConfigOutbounds)) - require.NoError(t, wrtErr) - - f, opnErr := os.Open(fd.Name()) - require.NoError(t, opnErr) - - all, rdErr := io.ReadAll(f) - require.NoError(t, rdErr) - - newSecrets := &AuthConfig{} - unmarErr := yaml.Unmarshal(all, newSecrets) - require.NoError(t, unmarErr) - - err := newSecrets.Validate() - require.Error(t, err, "incomplete outbound creds, etcd node creds not provided") - }) - - t.Run("secrets file contains incomplete creds", func(t *testing.T) { - fd, crtErr := os.CreateTemp("", "secrets.yaml") - require.NoError(t, crtErr) - defer func() { - assert.NoError(t, fd.Close()) - assert.NoError(t, os.Remove(fd.Name())) - }() - - _, wrtErr := fd.Write([]byte(incompleteCreds)) - require.NoError(t, wrtErr) - - f, opnErr := os.Open(fd.Name()) - require.NoError(t, opnErr) - - all, rdErr := io.ReadAll(f) - require.NoError(t, rdErr) - - newSecrets := &AuthConfig{} - unmarErr := yaml.Unmarshal(all, newSecrets) - require.NoError(t, unmarErr) - - err := newSecrets.Validate() - require.Error(t, err, "incomplete creds, password not provided") - }) + tests := []struct { + name string + cfg string + err error + }{ + { + name: "correct secrets file", + cfg: testBaseAuthConfig, + err: nil, + }, { + name: "secrets file missing outbounds", + cfg: missingOutbounds, + err: errors.New("outbound creds are not present"), + }, { + name: "secrets file missing m3db outbounds", + cfg: missingM3dbConfigOutbounds, + err: errors.New("incomplete outbound creds, m3db node creds not provided"), + }, { + name: "secrets file missing etcd outbounds", + cfg: missingETCDConfigOutbounds, + err: errors.New("incomplete outbound creds, etcd node creds not provided"), + }, { + name: "secrets file contains incomplete creds", + cfg: missingETCDConfigOutbounds, + err: errors.New("incomplete creds, password not provided"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fd, crtErr := os.CreateTemp("", "secrets.yaml") + require.NoError(t, crtErr) + defer func() { + assert.NoError(t, fd.Close()) + assert.NoError(t, os.Remove(fd.Name())) + }() + + _, wrtErr := fd.Write([]byte(tt.cfg)) + require.NoError(t, wrtErr) + + f, opnErr := os.Open(fd.Name()) + require.NoError(t, opnErr) + + all, rdErr := io.ReadAll(f) + require.NoError(t, rdErr) + + newSecrets := &AuthConfig{} + unmarErr := yaml.Unmarshal(all, newSecrets) + require.NoError(t, unmarErr) + + err := newSecrets.Validate() + if tt.err != nil { + assert.Error(t, err) + } + }) + } } diff --git a/src/dbnode/auth/inbound.go b/src/dbnode/auth/inbound.go index 803fb974db..441a8a2def 100644 --- a/src/dbnode/auth/inbound.go +++ b/src/dbnode/auth/inbound.go @@ -2,6 +2,7 @@ package auth import ( "fmt" + "github.com/uber/tchannel-go/thrift" ) @@ -52,8 +53,7 @@ func (i *Inbound) ValidateCredentials(creds InboundCredentials) error { return fmt.Errorf("credential not matched for dbnode inbound") } -// ValidateCredentialsFromThriftContext validates inbound credential from thrift context. ValidateCredsFromTCtxHeaders checks for -// call's request headers . +// ValidateCredentialsFromThriftContext validates inbound credential from thrift context. func (i *Inbound) ValidateCredentialsFromThriftContext(tctx thrift.Context, credtype CredentialType) error { ctxHeaders := tctx.Headers() userName, ok := ctxHeaders[AUTH_USERNAME] diff --git a/src/dbnode/auth/inbound_test.go b/src/dbnode/auth/inbound_test.go index b3e2ec96c3..2a2612ed2b 100644 --- a/src/dbnode/auth/inbound_test.go +++ b/src/dbnode/auth/inbound_test.go @@ -2,9 +2,10 @@ package auth import ( "context" + "testing" + "github.com/stretchr/testify/assert" "github.com/uber/tchannel-go/thrift" - "testing" ) func TestInbound_ValidateCredentials(t *testing.T) { @@ -92,63 +93,82 @@ func TestInbound_ValidateCredentials(t *testing.T) { func TestInbound_ValidateCredentialsFromThriftContext(t *testing.T) { t.Run("valid credentials no auth mode", func(t *testing.T) { inboundAuth := inboundAuthSetupNoAuthMode() - thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) + thriftCtx := createThriftContextWithHeaders( + context.Background(), + map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}, + ) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) assert.NoError(t, err) }) t.Run("invalid credentials no auth mode", func(t *testing.T) { inboundAuth := inboundAuthSetupNoAuthMode() - thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc1", AUTH_PASSWORD: "bcd1"}) + thriftCtx := createThriftContextWithHeaders( + context.Background(), + map[string]string{AUTH_USERNAME: "abc1", AUTH_PASSWORD: "bcd1"}) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) assert.NoError(t, err) }) t.Run("valid credentials shadow mode", func(t *testing.T) { inboundAuth := inboundAuthSetupShadowMode() - thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) + thriftCtx := createThriftContextWithHeaders( + context.Background(), + map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) assert.NoError(t, err) }) t.Run("invalid credentials shadow mode", func(t *testing.T) { inboundAuth := inboundAuthSetupShadowMode() - thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc1", AUTH_PASSWORD: "bcd1"}) + thriftCtx := createThriftContextWithHeaders( + context.Background(), + map[string]string{AUTH_USERNAME: "abc1", AUTH_PASSWORD: "bcd1"}) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) assert.NoError(t, err) }) t.Run("valid credentials enforced mode", func(t *testing.T) { inboundAuth := inboundAuthSetupEnabledMode() - thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) + thriftCtx := createThriftContextWithHeaders( + context.Background(), + map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) assert.NoError(t, err) }) t.Run("invalid credentials enforced mode", func(t *testing.T) { inboundAuth := inboundAuthSetupEnabledMode() - thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc1", AUTH_PASSWORD: "bcd1"}) + thriftCtx := createThriftContextWithHeaders( + context.Background(), + map[string]string{AUTH_USERNAME: "abc1", AUTH_PASSWORD: "bcd1"}) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) assert.Error(t, err) }) t.Run("unknown credential type for inbound", func(t *testing.T) { inboundAuth := inboundAuthSetupEnabledMode() - thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) + thriftCtx := createThriftContextWithHeaders( + context.Background(), + map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, Unknown) assert.Error(t, err) }) t.Run("non client credential type for inbound", func(t *testing.T) { inboundAuth := inboundAuthSetupEnabledMode() - thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) + thriftCtx := createThriftContextWithHeaders( + context.Background(), + map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, PeerCredential) assert.Error(t, err) }) t.Run("non client credential type for inbound", func(t *testing.T) { inboundAuth := inboundAuthSetupEnabledMode() - thriftCtx := createThriftContextWithHeaders(context.Background(), map[string]string{"some_u": "abc", "some_p": "bcd"}) + thriftCtx := createThriftContextWithHeaders( + context.Background(), + map[string]string{"some_u": "abc", "some_p": "bcd"}) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) assert.Error(t, err) }) diff --git a/src/dbnode/auth/integration/setup_config.go b/src/dbnode/auth/integration/setup_config.go index 602b5a7d7d..b0b12c8127 100644 --- a/src/dbnode/auth/integration/setup_config.go +++ b/src/dbnode/auth/integration/setup_config.go @@ -1,10 +1,12 @@ package integration import ( - "github.com/m3db/m3/src/cmd/services/m3dbnode/config" yaml "gopkg.in/yaml.v2" + + "github.com/m3db/m3/src/cmd/services/m3dbnode/config" ) +// BaseConfigWithAuthEnabled encapsulate auth enabled config. const BaseConfigWithAuthEnabled = ` outbounds: m3db: @@ -39,6 +41,7 @@ inbounds: digest: digest21 ` +// BaseConfigWithAuthEnabledRefresh encapsulate auth enabled config refreshed. const BaseConfigWithAuthEnabledRefresh = ` outbounds: m3db: @@ -72,6 +75,8 @@ inbounds: - username: user22 digest: digest22 ` + +// BaseConfigWithAuthDisabled encapsulate auth disabled config. const BaseConfigWithAuthDisabled = ` outbounds: m3db: @@ -106,6 +111,7 @@ inbounds: digest: digest21 ` +// BaseConfigWithOutbounds encapsulate auth config with valid outbounds. const BaseConfigWithOutbounds = ` outbounds: m3db: @@ -132,6 +138,7 @@ outbounds: ` +// BaseConfigWithOutboundsrefreshed encapsulate auth config with valid outbounds refresh. const BaseConfigWithOutboundsrefreshed = ` outbounds: m3db: @@ -158,8 +165,12 @@ outbounds: ` +// CreateTestConfigYaml returns authconfig. func CreateTestConfigYaml(cfg string) *config.AuthConfig { newSecrets := &config.AuthConfig{} - yaml.Unmarshal([]byte(cfg), newSecrets) + err := yaml.Unmarshal([]byte(cfg), newSecrets) + if err != nil { + return nil + } return newSecrets } diff --git a/src/dbnode/auth/outbound.go b/src/dbnode/auth/outbound.go index 54104c666d..06aaa6a048 100644 --- a/src/dbnode/auth/outbound.go +++ b/src/dbnode/auth/outbound.go @@ -10,7 +10,8 @@ type Outbound struct { etcdCredentials []OutboundCredentials } -// WrapThriftContextWithPeerCreds wraps thrift context with outbound peer credential depending on CredentialType and zone. +// WrapThriftContextWithPeerCreds wraps thrift context with outbound peer credential +// depending on CredentialType and zone. func (o *Outbound) WrapThriftContextWithPeerCreds(tctx thrift.Context, zone string) thrift.Context { for _, peerCred := range o.peerCredentials { if peerCred.Zone == zone { diff --git a/src/dbnode/server/server_creds_provider_test.go b/src/dbnode/server/server_creds_provider_test.go index c0afd2da98..9da37fe74e 100644 --- a/src/dbnode/server/server_creds_provider_test.go +++ b/src/dbnode/server/server_creds_provider_test.go @@ -1,12 +1,13 @@ package server import ( + "testing" + "time" + "github.com/m3db/m3/src/dbnode/auth" "github.com/m3db/m3/src/dbnode/auth/integration" "github.com/stretchr/testify/assert" "github.com/uber/tchannel-go/thrift" - "testing" - "time" ) func TestInboundCfgPopulate(t *testing.T) { diff --git a/src/x/config/configflag/flag.go b/src/x/config/configflag/flag.go index 2cec0ab006..eca980ce46 100644 --- a/src/x/config/configflag/flag.go +++ b/src/x/config/configflag/flag.go @@ -100,11 +100,6 @@ func (opts *Options) MainLoad(target interface{}, loadOpts config.Options) error // CredentialLoad is a method used in main(), which handles secrets commandline options. It loads the secrets yaml // file if present with -c flag. func (opts *Options) CredentialLoad(target interface{}, loadOpts config.Options) (bool, error) { - osFns := opts.osFns - if osFns == nil { - osFns = realOS{} - } - if len(opts.CredentialsFile.Value) == 0 { opts.cmd.Usage() return false, nil diff --git a/src/x/config/configflag/flag_test.go b/src/x/config/configflag/flag_test.go index 06491b008f..41c362211b 100644 --- a/src/x/config/configflag/flag_test.go +++ b/src/x/config/configflag/flag_test.go @@ -134,7 +134,6 @@ func TestCommandLineOptions(t *testing.T) { require.NoError(t, err) require.False(t, isPresent) }) - } func TestFlagArray(t *testing.T) { From 207495a26b3184e4691f31136a15e5136b3d5375 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Tue, 23 May 2023 09:34:06 +0530 Subject: [PATCH 03/12] lint fixes --- src/cluster/client/etcd/config.go | 20 ++++++++++++++++++++ src/x/config/configflag/flag.go | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/cluster/client/etcd/config.go b/src/cluster/client/etcd/config.go index 5e4aa8499a..ab0850a965 100644 --- a/src/cluster/client/etcd/config.go +++ b/src/cluster/client/etcd/config.go @@ -21,6 +21,7 @@ package etcd import ( + "fmt" "os" "time" @@ -52,6 +53,8 @@ type ClusterConfig struct { AutoSyncInterval time.Duration `yaml:"autoSyncInterval"` DialTimeout time.Duration `yaml:"dialTimeout"` + Auth *AuthConfig `yaml:"auth"` + DialOptions []grpc.DialOption `yaml:"-"` // nonserializable } @@ -118,6 +121,22 @@ func (c *KeepAliveConfig) NewOptions() KeepAliveOptions { SetKeepAliveTimeout(c.Timeout) } +// AuthConfig configures authentication behavior. +type AuthConfig struct { + Enabled bool `yaml:"enabled"` + UserName string `yaml:"username"` + Password string `yaml:"password"` +} + +func (a *AuthConfig) Validate() error { + if a.Enabled { + if a.UserName == "" || a.Password == "" { + return fmt.Errorf("credentials must be set for client's outbound") + } + } + return nil +} + // Configuration is for config service client. type Configuration struct { Zone string `yaml:"zone"` @@ -128,6 +147,7 @@ type Configuration struct { SDConfig services.Configuration `yaml:"m3sd"` WatchWithRevision int64 `yaml:"watchWithRevision"` NewDirectoryMode *os.FileMode `yaml:"newDirectoryMode"` + Auth *AuthConfig `yaml:"auth"` Retry retry.Configuration `yaml:"retry"` RequestTimeout time.Duration `yaml:"requestTimeout"` diff --git a/src/x/config/configflag/flag.go b/src/x/config/configflag/flag.go index eca980ce46..9b912f0a66 100644 --- a/src/x/config/configflag/flag.go +++ b/src/x/config/configflag/flag.go @@ -106,7 +106,7 @@ func (opts *Options) CredentialLoad(target interface{}, loadOpts config.Options) } if err := config.LoadFiles(target, opts.CredentialsFile.Value, loadOpts); err != nil { - return false, fmt.Errorf("unable to load secrets from %s: %v", opts.CredentialsFile.Value, err) + return false, fmt.Errorf("unable to load secrets from %s: %w", opts.CredentialsFile.Value, err) } return true, nil } From 5955868141a0187331145fb70fffd2f82879de35 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Tue, 23 May 2023 10:18:50 +0530 Subject: [PATCH 04/12] mocks regen --- src/cluster/client/etcd/config.go | 1 + src/cmd/services/m3dbnode/config/auth_test.go | 9 +-- src/dbnode/auth/benchmark_digest_test.go | 2 +- src/dbnode/auth/digest.go | 4 +- src/dbnode/auth/digest_test.go | 3 +- src/dbnode/auth/entities_test.go | 3 +- src/dbnode/auth/enum.go | 6 +- src/dbnode/auth/inbound.go | 6 +- src/dbnode/auth/inbound_test.go | 16 ++--- src/dbnode/auth/integration/setup_config.go | 4 +- src/dbnode/auth/module.go | 15 ++-- src/dbnode/auth/outbound.go | 4 +- src/dbnode/auth/outbound_test.go | 5 +- src/dbnode/client/client_mock.go | 72 ++++++++++++++++++- src/dbnode/server/server.go | 2 +- src/dbnode/server/server_creds_provider.go | 15 ++-- .../server/server_creds_provider_test.go | 1 + src/dbnode/topology/topology_mock.go | 16 ++++- 18 files changed, 137 insertions(+), 47 deletions(-) diff --git a/src/cluster/client/etcd/config.go b/src/cluster/client/etcd/config.go index ab0850a965..ed6348ef62 100644 --- a/src/cluster/client/etcd/config.go +++ b/src/cluster/client/etcd/config.go @@ -128,6 +128,7 @@ type AuthConfig struct { Password string `yaml:"password"` } +// Validate validates the AuthConfig. func (a *AuthConfig) Validate() error { if a.Enabled { if a.UserName == "" || a.Password == "" { diff --git a/src/cmd/services/m3dbnode/config/auth_test.go b/src/cmd/services/m3dbnode/config/auth_test.go index 98d32d54af..f8e3f61e42 100644 --- a/src/cmd/services/m3dbnode/config/auth_test.go +++ b/src/cmd/services/m3dbnode/config/auth_test.go @@ -2,12 +2,13 @@ package config import ( "errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - yaml "gopkg.in/yaml.v2" "io" "os" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" ) const testBaseAuthConfig = ` @@ -140,7 +141,7 @@ func TestAuthConfiguration(t *testing.T) { assert.NoError(t, os.Remove(fd.Name())) }() - _, wrtErr := fd.Write([]byte(tt.cfg)) + _, wrtErr := fd.WriteString(tt.cfg) require.NoError(t, wrtErr) f, opnErr := os.Open(fd.Name()) diff --git a/src/dbnode/auth/benchmark_digest_test.go b/src/dbnode/auth/benchmark_digest_test.go index f9376f8ae7..871628c00b 100644 --- a/src/dbnode/auth/benchmark_digest_test.go +++ b/src/dbnode/auth/benchmark_digest_test.go @@ -29,7 +29,7 @@ func BenchmarkGetMD5DigestMap(b *testing.B) { func RandomString(length int) string { b := make([]byte, length) for i := range b { - b[i] = charset[mrand.Intn(len(charset))] + b[i] = charset[mrand.Intn(len(charset))] // #nosec } return string(b) } diff --git a/src/dbnode/auth/digest.go b/src/dbnode/auth/digest.go index 7e3247c93f..fbfaf61c61 100644 --- a/src/dbnode/auth/digest.go +++ b/src/dbnode/auth/digest.go @@ -1,7 +1,7 @@ package auth import ( - "crypto/md5" + "crypto/md5" // #nosec "encoding/hex" "fmt" "hash" @@ -9,7 +9,7 @@ import ( var ( credentialCache = map[string]string{} - hashFunc = md5.New() + hashFunc = md5.New() // #nosec ) func generateHashWithCacheLookup(data string, h hash.Hash) (string, error) { diff --git a/src/dbnode/auth/digest_test.go b/src/dbnode/auth/digest_test.go index b528e95a2d..faac166f08 100644 --- a/src/dbnode/auth/digest_test.go +++ b/src/dbnode/auth/digest_test.go @@ -1,8 +1,9 @@ package auth import ( - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) func TestGenerateHash(t *testing.T) { diff --git a/src/dbnode/auth/entities_test.go b/src/dbnode/auth/entities_test.go index 8a0d69d547..0bf0c3a956 100644 --- a/src/dbnode/auth/entities_test.go +++ b/src/dbnode/auth/entities_test.go @@ -1,8 +1,9 @@ package auth import ( - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) func TestInboundCredentials_Validate(t *testing.T) { diff --git a/src/dbnode/auth/enum.go b/src/dbnode/auth/enum.go index 5558779bf8..e82b5fc24c 100644 --- a/src/dbnode/auth/enum.go +++ b/src/dbnode/auth/enum.go @@ -17,12 +17,12 @@ const ( EtcdCredential ) -// AuthMode designates a type of authentication. -type AuthMode int +// Mode designates a type of authentication. +type Mode int const ( // AuthModeUnknown is unknown authentication type case. - AuthModeUnknown AuthMode = iota + AuthModeUnknown Mode = iota // AuthModeNoAuth is no authentication type case. AuthModeNoAuth diff --git a/src/dbnode/auth/inbound.go b/src/dbnode/auth/inbound.go index 441a8a2def..c38050ae67 100644 --- a/src/dbnode/auth/inbound.go +++ b/src/dbnode/auth/inbound.go @@ -9,7 +9,7 @@ import ( // Inbound encapsulates client credentials. type Inbound struct { clientCredentials []InboundCredentials - authMode AuthMode + authMode Mode } // ValidateCredentials validates the inbound credential and return error accordingly. @@ -56,12 +56,12 @@ func (i *Inbound) ValidateCredentials(creds InboundCredentials) error { // ValidateCredentialsFromThriftContext validates inbound credential from thrift context. func (i *Inbound) ValidateCredentialsFromThriftContext(tctx thrift.Context, credtype CredentialType) error { ctxHeaders := tctx.Headers() - userName, ok := ctxHeaders[AUTH_USERNAME] + userName, ok := ctxHeaders[AuthUsername] if !ok { userName = "" } - password, ok := ctxHeaders[AUTH_PASSWORD] + password, ok := ctxHeaders[AuthPassword] if !ok { password = "" } diff --git a/src/dbnode/auth/inbound_test.go b/src/dbnode/auth/inbound_test.go index 2a2612ed2b..a69ceecd16 100644 --- a/src/dbnode/auth/inbound_test.go +++ b/src/dbnode/auth/inbound_test.go @@ -95,7 +95,7 @@ func TestInbound_ValidateCredentialsFromThriftContext(t *testing.T) { inboundAuth := inboundAuthSetupNoAuthMode() thriftCtx := createThriftContextWithHeaders( context.Background(), - map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}, + map[string]string{AuthUsername: "abc", AuthPassword: "bcd"}, ) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) assert.NoError(t, err) @@ -105,7 +105,7 @@ func TestInbound_ValidateCredentialsFromThriftContext(t *testing.T) { inboundAuth := inboundAuthSetupNoAuthMode() thriftCtx := createThriftContextWithHeaders( context.Background(), - map[string]string{AUTH_USERNAME: "abc1", AUTH_PASSWORD: "bcd1"}) + map[string]string{AuthUsername: "abc1", AuthPassword: "bcd1"}) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) assert.NoError(t, err) }) @@ -114,7 +114,7 @@ func TestInbound_ValidateCredentialsFromThriftContext(t *testing.T) { inboundAuth := inboundAuthSetupShadowMode() thriftCtx := createThriftContextWithHeaders( context.Background(), - map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) + map[string]string{AuthUsername: "abc", AuthPassword: "bcd"}) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) assert.NoError(t, err) }) @@ -123,7 +123,7 @@ func TestInbound_ValidateCredentialsFromThriftContext(t *testing.T) { inboundAuth := inboundAuthSetupShadowMode() thriftCtx := createThriftContextWithHeaders( context.Background(), - map[string]string{AUTH_USERNAME: "abc1", AUTH_PASSWORD: "bcd1"}) + map[string]string{AuthUsername: "abc1", AuthPassword: "bcd1"}) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) assert.NoError(t, err) }) @@ -132,7 +132,7 @@ func TestInbound_ValidateCredentialsFromThriftContext(t *testing.T) { inboundAuth := inboundAuthSetupEnabledMode() thriftCtx := createThriftContextWithHeaders( context.Background(), - map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) + map[string]string{AuthUsername: "abc", AuthPassword: "bcd"}) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) assert.NoError(t, err) }) @@ -141,7 +141,7 @@ func TestInbound_ValidateCredentialsFromThriftContext(t *testing.T) { inboundAuth := inboundAuthSetupEnabledMode() thriftCtx := createThriftContextWithHeaders( context.Background(), - map[string]string{AUTH_USERNAME: "abc1", AUTH_PASSWORD: "bcd1"}) + map[string]string{AuthUsername: "abc1", AuthPassword: "bcd1"}) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, ClientCredential) assert.Error(t, err) }) @@ -150,7 +150,7 @@ func TestInbound_ValidateCredentialsFromThriftContext(t *testing.T) { inboundAuth := inboundAuthSetupEnabledMode() thriftCtx := createThriftContextWithHeaders( context.Background(), - map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) + map[string]string{AuthUsername: "abc", AuthPassword: "bcd"}) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, Unknown) assert.Error(t, err) }) @@ -159,7 +159,7 @@ func TestInbound_ValidateCredentialsFromThriftContext(t *testing.T) { inboundAuth := inboundAuthSetupEnabledMode() thriftCtx := createThriftContextWithHeaders( context.Background(), - map[string]string{AUTH_USERNAME: "abc", AUTH_PASSWORD: "bcd"}) + map[string]string{AuthUsername: "abc", AuthPassword: "bcd"}) err := inboundAuth.ValidateCredentialsFromThriftContext(thriftCtx, PeerCredential) assert.Error(t, err) }) diff --git a/src/dbnode/auth/integration/setup_config.go b/src/dbnode/auth/integration/setup_config.go index b0b12c8127..87b38e60ca 100644 --- a/src/dbnode/auth/integration/setup_config.go +++ b/src/dbnode/auth/integration/setup_config.go @@ -1,9 +1,9 @@ package integration import ( - yaml "gopkg.in/yaml.v2" - "github.com/m3db/m3/src/cmd/services/m3dbnode/config" + + "gopkg.in/yaml.v2" ) // BaseConfigWithAuthEnabled encapsulate auth enabled config. diff --git a/src/dbnode/auth/module.go b/src/dbnode/auth/module.go index a2167400ee..bc4d914507 100644 --- a/src/dbnode/auth/module.go +++ b/src/dbnode/auth/module.go @@ -5,19 +5,20 @@ import ( ) const ( - // AUTH_PASSWORD defines password key in thrift ctx. - AUTH_PASSWORD = "password" + // AuthPassword defines password key in thrift ctx. + AuthPassword = "password" - // AUTH_USERNAME defines username key in thrift ctx. - AUTH_USERNAME = "username" + // AuthUsername defines username key in thrift ctx. + AuthUsername = "username" - // AUTH_ZONE defines zone key in thrift ctx. - AUTH_ZONE = "zone" + // AuthZone defines zone key in thrift ctx. + AuthZone = "zone" ) var ( // InboundAuth encapsulates inbound credentials for the dbnode. InboundAuth *Inbound + // InboundLock encapsulates mutual exclusion lock, used while refreshing creds. InboundLock = &sync.Mutex{} // OutboundAuth encapsulates outbound credentials for dbnode and client. @@ -31,7 +32,7 @@ func PopulateDefaultAuthConfig() { } // PopulateInbound populates inbound credentials for dbnode. -func PopulateInbound(clientCredentials []InboundCredentials, authMode AuthMode) { +func PopulateInbound(clientCredentials []InboundCredentials, authMode Mode) { InboundAuth = &Inbound{ clientCredentials: clientCredentials, authMode: authMode, diff --git a/src/dbnode/auth/outbound.go b/src/dbnode/auth/outbound.go index 06aaa6a048..be0c593a3e 100644 --- a/src/dbnode/auth/outbound.go +++ b/src/dbnode/auth/outbound.go @@ -23,8 +23,8 @@ func (o *Outbound) WrapThriftContextWithPeerCreds(tctx thrift.Context, zone stri func wrapTctxWithCredentials(tCtx thrift.Context, creds OutboundCredentials) thrift.Context { return thrift.WithHeaders(tCtx, map[string]string{ - AUTH_USERNAME: creds.Username, - AUTH_PASSWORD: creds.Password}, + AuthUsername: creds.Username, + AuthPassword: creds.Password}, ) } diff --git a/src/dbnode/auth/outbound_test.go b/src/dbnode/auth/outbound_test.go index 1e080c58c9..1b76ba1885 100644 --- a/src/dbnode/auth/outbound_test.go +++ b/src/dbnode/auth/outbound_test.go @@ -1,10 +1,11 @@ package auth import ( - "github.com/stretchr/testify/assert" - "github.com/uber/tchannel-go/thrift" "testing" "time" + + "github.com/stretchr/testify/assert" + "github.com/uber/tchannel-go/thrift" ) func TestWrapThriftContextWithCreds(t *testing.T) { diff --git a/src/dbnode/client/client_mock.go b/src/dbnode/client/client_mock.go index a997fa514b..53ab3f1eef 100644 --- a/src/dbnode/client/client_mock.go +++ b/src/dbnode/client/client_mock.go @@ -1,7 +1,7 @@ // Code generated by MockGen. DO NOT EDIT. // Source: ../../client/types.go -// Copyright (c) 2022 Uber Technologies, Inc. +// Copyright (c) 2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -1451,6 +1451,20 @@ func (mr *MockOptionsMockRecorder) FetchRetrier() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchRetrier", reflect.TypeOf((*MockOptions)(nil).FetchRetrier)) } +// FetchTopologyInitializerZone mocks base method. +func (m *MockOptions) FetchTopologyInitializerZone() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FetchTopologyInitializerZone") + ret0, _ := ret[0].(string) + return ret0 +} + +// FetchTopologyInitializerZone indicates an expected call of FetchTopologyInitializerZone. +func (mr *MockOptionsMockRecorder) FetchTopologyInitializerZone() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchTopologyInitializerZone", reflect.TypeOf((*MockOptions)(nil).FetchTopologyInitializerZone)) +} + // HostConnectTimeout mocks base method. func (m *MockOptions) HostConnectTimeout() time.Duration { m.ctrl.T.Helper() @@ -2459,6 +2473,20 @@ func (mr *MockOptionsMockRecorder) SetTopologyInitializer(value interface{}) *go return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTopologyInitializer", reflect.TypeOf((*MockOptions)(nil).SetTopologyInitializer), value) } +// SetTopologyInitializerZone mocks base method. +func (m *MockOptions) SetTopologyInitializerZone(value string) Options { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetTopologyInitializerZone", value) + ret0, _ := ret[0].(Options) + return ret0 +} + +// SetTopologyInitializerZone indicates an expected call of SetTopologyInitializerZone. +func (mr *MockOptionsMockRecorder) SetTopologyInitializerZone(value interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTopologyInitializerZone", reflect.TypeOf((*MockOptions)(nil).SetTopologyInitializerZone), value) +} + // SetTruncateRequestTimeout mocks base method. func (m *MockOptions) SetTruncateRequestTimeout(value time.Duration) Options { m.ctrl.T.Helper() @@ -3238,6 +3266,20 @@ func (mr *MockAdminOptionsMockRecorder) FetchSeriesBlocksMetadataBatchTimeout() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchSeriesBlocksMetadataBatchTimeout", reflect.TypeOf((*MockAdminOptions)(nil).FetchSeriesBlocksMetadataBatchTimeout)) } +// FetchTopologyInitializerZone mocks base method. +func (m *MockAdminOptions) FetchTopologyInitializerZone() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FetchTopologyInitializerZone") + ret0, _ := ret[0].(string) + return ret0 +} + +// FetchTopologyInitializerZone indicates an expected call of FetchTopologyInitializerZone. +func (mr *MockAdminOptionsMockRecorder) FetchTopologyInitializerZone() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchTopologyInitializerZone", reflect.TypeOf((*MockAdminOptions)(nil).FetchTopologyInitializerZone)) +} + // HostConnectTimeout mocks base method. func (m *MockAdminOptions) HostConnectTimeout() time.Duration { m.ctrl.T.Helper() @@ -4372,6 +4414,20 @@ func (mr *MockAdminOptionsMockRecorder) SetTopologyInitializer(value interface{} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTopologyInitializer", reflect.TypeOf((*MockAdminOptions)(nil).SetTopologyInitializer), value) } +// SetTopologyInitializerZone mocks base method. +func (m *MockAdminOptions) SetTopologyInitializerZone(value string) Options { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetTopologyInitializerZone", value) + ret0, _ := ret[0].(Options) + return ret0 +} + +// SetTopologyInitializerZone indicates an expected call of SetTopologyInitializerZone. +func (mr *MockAdminOptionsMockRecorder) SetTopologyInitializerZone(value interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTopologyInitializerZone", reflect.TypeOf((*MockAdminOptions)(nil).SetTopologyInitializerZone), value) +} + // SetTruncateRequestTimeout mocks base method. func (m *MockAdminOptions) SetTruncateRequestTimeout(value time.Duration) Options { m.ctrl.T.Helper() @@ -5378,6 +5434,20 @@ func (mr *MockconnectionPoolMockRecorder) ConnectionCount() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConnectionCount", reflect.TypeOf((*MockconnectionPool)(nil).ConnectionCount)) } +// GetZone mocks base method. +func (m *MockconnectionPool) GetZone() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetZone") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetZone indicates an expected call of GetZone. +func (mr *MockconnectionPoolMockRecorder) GetZone() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetZone", reflect.TypeOf((*MockconnectionPool)(nil).GetZone)) +} + // NextClient mocks base method. func (m *MockconnectionPool) NextClient() (rpc.TChanNode, Channel, error) { m.ctrl.T.Helper() diff --git a/src/dbnode/server/server.go b/src/dbnode/server/server.go index 32b29aa60e..5101a810df 100644 --- a/src/dbnode/server/server.go +++ b/src/dbnode/server/server.go @@ -25,7 +25,6 @@ import ( "context" "errors" "fmt" - "github.com/m3db/m3/src/dbnode/auth" "io" "math" "net/http" @@ -46,6 +45,7 @@ import ( "github.com/m3db/m3/src/cluster/placementhandler" "github.com/m3db/m3/src/cluster/placementhandler/handleroptions" "github.com/m3db/m3/src/cmd/services/m3dbnode/config" + "github.com/m3db/m3/src/dbnode/auth" "github.com/m3db/m3/src/dbnode/client" "github.com/m3db/m3/src/dbnode/encoding" "github.com/m3db/m3/src/dbnode/encoding/m3tsz" diff --git a/src/dbnode/server/server_creds_provider.go b/src/dbnode/server/server_creds_provider.go index ed7ea8438c..99177d55c5 100644 --- a/src/dbnode/server/server_creds_provider.go +++ b/src/dbnode/server/server_creds_provider.go @@ -1,13 +1,14 @@ package server import ( + "strings" + "github.com/m3db/m3/src/cmd/services/m3dbnode/config" "github.com/m3db/m3/src/dbnode/auth" - "strings" ) var ( - authModeMap = map[string]auth.AuthMode{ + authModeMap = map[string]auth.Mode{ "none": auth.AuthModeNoAuth, "shadow": auth.AuthModeShadow, "enabled": auth.AuthModeEnforced, @@ -16,8 +17,8 @@ var ( // PopulateInboundAuthConfig populates inbound auth modules with the provided auth config. func PopulateInboundAuthConfig(cfg config.AuthConfig) { - var inboundAuth []auth.InboundCredentials nodeInbound := cfg.Inbound.M3DB + inboundAuth := make([]auth.InboundCredentials, 0, len(nodeInbound.Credentials)) for _, nodeCfg := range nodeInbound.Credentials { inboundAuth = append(inboundAuth, auth.InboundCredentials{ Username: *nodeCfg.Username, @@ -38,9 +39,8 @@ func RefreshInboundAuthConfig(credentialsConfig config.AuthConfig) { // PopulateOutboundAuthConfig populates outbound auth modules with the provided auth config. func PopulateOutboundAuthConfig(cfg config.AuthConfig) { - var outboundPeerAuth []auth.OutboundCredentials nodeOutboundPeer := cfg.Outbound.M3DB - + outboundPeerAuth := make([]auth.OutboundCredentials, 0, len(nodeOutboundPeer.NodeConfig)) for _, nodeCfg := range nodeOutboundPeer.NodeConfig { outboundPeerAuth = append(outboundPeerAuth, auth.OutboundCredentials{ Username: *nodeCfg.Service.Username, @@ -50,9 +50,8 @@ func PopulateOutboundAuthConfig(cfg config.AuthConfig) { }) } - var outboundEtcdAuth []auth.OutboundCredentials nodeOutboundEtcd := cfg.Outbound.Etcd - + outboundEtcdAuth := make([]auth.OutboundCredentials, 0, len(nodeOutboundEtcd.NodeConfig)) for _, nodeCfg := range nodeOutboundEtcd.NodeConfig { outboundEtcdAuth = append(outboundEtcdAuth, auth.OutboundCredentials{ Username: *nodeCfg.Service.Username, @@ -64,7 +63,7 @@ func PopulateOutboundAuthConfig(cfg config.AuthConfig) { auth.PopulateOutbound(outboundPeerAuth, outboundEtcdAuth) } -func parseAuthMode(str string) auth.AuthMode { +func parseAuthMode(str string) auth.Mode { c, ok := authModeMap[strings.ToLower(str)] if !ok { return auth.AuthModeUnknown diff --git a/src/dbnode/server/server_creds_provider_test.go b/src/dbnode/server/server_creds_provider_test.go index 9da37fe74e..1bb2daca19 100644 --- a/src/dbnode/server/server_creds_provider_test.go +++ b/src/dbnode/server/server_creds_provider_test.go @@ -6,6 +6,7 @@ import ( "github.com/m3db/m3/src/dbnode/auth" "github.com/m3db/m3/src/dbnode/auth/integration" + "github.com/stretchr/testify/assert" "github.com/uber/tchannel-go/thrift" ) diff --git a/src/dbnode/topology/topology_mock.go b/src/dbnode/topology/topology_mock.go index 29a8501abe..4c82a0c8e7 100644 --- a/src/dbnode/topology/topology_mock.go +++ b/src/dbnode/topology/topology_mock.go @@ -1,7 +1,7 @@ // Code generated by MockGen. DO NOT EDIT. // Source: ../../topology/types.go -// Copyright (c) 2022 Uber Technologies, Inc. +// Copyright (c) 2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -175,6 +175,20 @@ func (m *MockInitializer) EXPECT() *MockInitializerMockRecorder { return m.recorder } +// FetchZone mocks base method. +func (m *MockInitializer) FetchZone() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FetchZone") + ret0, _ := ret[0].(string) + return ret0 +} + +// FetchZone indicates an expected call of FetchZone. +func (mr *MockInitializerMockRecorder) FetchZone() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchZone", reflect.TypeOf((*MockInitializer)(nil).FetchZone)) +} + // Init mocks base method. func (m *MockInitializer) Init() (Topology, error) { m.ctrl.T.Helper() From 440e253bbccd4cc407ecefae4d635f5851f07629 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Tue, 23 May 2023 10:38:51 +0530 Subject: [PATCH 05/12] lint --- .../client/client_creds_provider_test.go | 68 +++---------------- 1 file changed, 11 insertions(+), 57 deletions(-) diff --git a/src/dbnode/client/client_creds_provider_test.go b/src/dbnode/client/client_creds_provider_test.go index 58fc89811c..4e6f6f6bd9 100644 --- a/src/dbnode/client/client_creds_provider_test.go +++ b/src/dbnode/client/client_creds_provider_test.go @@ -1,10 +1,10 @@ package client import ( - "github.com/m3db/m3/src/dbnode/auth" "os" "testing" + "github.com/m3db/m3/src/dbnode/auth" xconfig "github.com/m3db/m3/src/x/config" "github.com/stretchr/testify/assert" @@ -34,22 +34,7 @@ func TestConfigurationWithAuthClientProvider(t *testing.T) { writeConsistencyLevel: majority readConsistencyLevel: unstrict_majority ` - fd, err := os.CreateTemp("", "config.yaml") - require.NoError(t, err) - defer func() { - assert.NoError(t, fd.Close()) - assert.NoError(t, os.Remove(fd.Name())) - }() - - _, err = fd.Write([]byte(clientCfg)) - require.NoError(t, err) - - var cfg Configuration - err = xconfig.LoadFile(&cfg, fd.Name(), xconfig.Options{}) - require.NoError(t, err) - - defer outboundAuthCleanup() - outboundErr := PopulateClientOutboundAuthConfig(cfg.EnvironmentConfig.Services) + outboundErr := setupAndLoadCfg(t, clientCfg) require.NoError(t, outboundErr) } @@ -75,22 +60,7 @@ func TestConfigurationWithIncorrectDbAuth(t *testing.T) { writeConsistencyLevel: majority readConsistencyLevel: unstrict_majority ` - fd, err := os.CreateTemp("", "config.yaml") - require.NoError(t, err) - defer func() { - assert.NoError(t, fd.Close()) - assert.NoError(t, os.Remove(fd.Name())) - }() - - _, err = fd.Write([]byte(clientCfg)) - require.NoError(t, err) - - var cfg Configuration - err = xconfig.LoadFile(&cfg, fd.Name(), xconfig.Options{}) - require.NoError(t, err) - - defer outboundAuthCleanup() - outboundErr := PopulateClientOutboundAuthConfig(cfg.EnvironmentConfig.Services) + outboundErr := setupAndLoadCfg(t, clientCfg) require.Error(t, outboundErr) } @@ -116,22 +86,7 @@ func TestConfigurationWithIncorrectEtcdAuth(t *testing.T) { writeConsistencyLevel: majority readConsistencyLevel: unstrict_majority ` - fd, err := os.CreateTemp("", "config.yaml") - require.NoError(t, err) - defer func() { - assert.NoError(t, fd.Close()) - assert.NoError(t, os.Remove(fd.Name())) - }() - - _, err = fd.Write([]byte(clientCfg)) - require.NoError(t, err) - - var cfg Configuration - err = xconfig.LoadFile(&cfg, fd.Name(), xconfig.Options{}) - require.NoError(t, err) - - defer outboundAuthCleanup() - outboundErr := PopulateClientOutboundAuthConfig(cfg.EnvironmentConfig.Services) + outboundErr := setupAndLoadCfg(t, clientCfg) require.Error(t, outboundErr) } @@ -157,6 +112,11 @@ func TestConfigurationNoAuth(t *testing.T) { writeConsistencyLevel: majority readConsistencyLevel: unstrict_majority ` + outboundErr := setupAndLoadCfg(t, clientCfg) + require.NoError(t, outboundErr) +} + +func setupAndLoadCfg(t *testing.T, config string) error { fd, err := os.CreateTemp("", "config.yaml") require.NoError(t, err) defer func() { @@ -164,7 +124,7 @@ func TestConfigurationNoAuth(t *testing.T) { assert.NoError(t, os.Remove(fd.Name())) }() - _, err = fd.Write([]byte(clientCfg)) + _, err = fd.WriteString(config) require.NoError(t, err) var cfg Configuration @@ -172,14 +132,8 @@ func TestConfigurationNoAuth(t *testing.T) { require.NoError(t, err) defer outboundAuthCleanup() - outboundErr := PopulateClientOutboundAuthConfig(cfg.EnvironmentConfig.Services) - require.NoError(t, outboundErr) + return PopulateClientOutboundAuthConfig(cfg.EnvironmentConfig.Services) } - -func inboundAuthCleanup() { - auth.InboundAuth = &auth.Inbound{} -} - func outboundAuthCleanup() { auth.OutboundAuth = &auth.Outbound{} } From b0533bfd7dfaa9c0f4cad517baabe01cf7eae717 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Tue, 23 May 2023 10:53:57 +0530 Subject: [PATCH 06/12] lint --- src/dbnode/client/config.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dbnode/client/config.go b/src/dbnode/client/config.go index 46f0e01293..832d85886d 100644 --- a/src/dbnode/client/config.go +++ b/src/dbnode/client/config.go @@ -475,8 +475,9 @@ func (c Configuration) NewAdminClient( opts = opt(opts) } - PopulateClientOutboundAuthConfig(c.EnvironmentConfig.Services) - + if authErr := PopulateClientOutboundAuthConfig(c.EnvironmentConfig.Services); authErr != nil { + return nil, authErr + } asyncClusterOpts := NewOptionsForAsyncClusters(opts, asyncTopoInits, asyncClientOverrides) return NewAdminClient(opts, asyncClusterOpts...) } From 91062ea8616cf0c4de2ef68b3303642a97a79315 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Tue, 23 May 2023 11:14:49 +0530 Subject: [PATCH 07/12] make clean --- src/cmd/services/m3dbnode/config/auth.go | 20 +++++++++++++++++++ src/cmd/services/m3dbnode/config/auth_test.go | 20 +++++++++++++++++++ src/dbnode/auth/benchmark_digest_test.go | 20 +++++++++++++++++++ src/dbnode/auth/digest.go | 20 +++++++++++++++++++ src/dbnode/auth/digest_test.go | 20 +++++++++++++++++++ src/dbnode/auth/entities.go | 20 +++++++++++++++++++ src/dbnode/auth/entities_test.go | 20 +++++++++++++++++++ src/dbnode/auth/enum.go | 20 +++++++++++++++++++ src/dbnode/auth/inbound.go | 20 +++++++++++++++++++ src/dbnode/auth/inbound_test.go | 20 +++++++++++++++++++ src/dbnode/auth/integration/setup_config.go | 20 +++++++++++++++++++ src/dbnode/auth/module.go | 20 +++++++++++++++++++ src/dbnode/auth/outbound.go | 20 +++++++++++++++++++ src/dbnode/auth/outbound_test.go | 20 +++++++++++++++++++ src/dbnode/client/client_creds_provider.go | 20 +++++++++++++++++++ .../client/client_creds_provider_test.go | 20 +++++++++++++++++++ src/dbnode/server/server_creds_provider.go | 20 +++++++++++++++++++ .../server/server_creds_provider_test.go | 20 +++++++++++++++++++ 18 files changed, 360 insertions(+) diff --git a/src/cmd/services/m3dbnode/config/auth.go b/src/cmd/services/m3dbnode/config/auth.go index 2cdfc384a2..c09b90eef4 100644 --- a/src/cmd/services/m3dbnode/config/auth.go +++ b/src/cmd/services/m3dbnode/config/auth.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package config import "fmt" diff --git a/src/cmd/services/m3dbnode/config/auth_test.go b/src/cmd/services/m3dbnode/config/auth_test.go index f8e3f61e42..ec2d203aeb 100644 --- a/src/cmd/services/m3dbnode/config/auth_test.go +++ b/src/cmd/services/m3dbnode/config/auth_test.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package config import ( diff --git a/src/dbnode/auth/benchmark_digest_test.go b/src/dbnode/auth/benchmark_digest_test.go index 871628c00b..326fe29363 100644 --- a/src/dbnode/auth/benchmark_digest_test.go +++ b/src/dbnode/auth/benchmark_digest_test.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package auth import ( diff --git a/src/dbnode/auth/digest.go b/src/dbnode/auth/digest.go index fbfaf61c61..c4d67cf8ef 100644 --- a/src/dbnode/auth/digest.go +++ b/src/dbnode/auth/digest.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package auth import ( diff --git a/src/dbnode/auth/digest_test.go b/src/dbnode/auth/digest_test.go index faac166f08..950a8a5f26 100644 --- a/src/dbnode/auth/digest_test.go +++ b/src/dbnode/auth/digest_test.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package auth import ( diff --git a/src/dbnode/auth/entities.go b/src/dbnode/auth/entities.go index 3b9d4bcc4a..d742883a41 100644 --- a/src/dbnode/auth/entities.go +++ b/src/dbnode/auth/entities.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package auth import ( diff --git a/src/dbnode/auth/entities_test.go b/src/dbnode/auth/entities_test.go index 0bf0c3a956..084d7e46cc 100644 --- a/src/dbnode/auth/entities_test.go +++ b/src/dbnode/auth/entities_test.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package auth import ( diff --git a/src/dbnode/auth/enum.go b/src/dbnode/auth/enum.go index e82b5fc24c..5ff50d36b2 100644 --- a/src/dbnode/auth/enum.go +++ b/src/dbnode/auth/enum.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package auth // CredentialType designates credentials for different connection edges. diff --git a/src/dbnode/auth/inbound.go b/src/dbnode/auth/inbound.go index c38050ae67..9fd41830e2 100644 --- a/src/dbnode/auth/inbound.go +++ b/src/dbnode/auth/inbound.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package auth import ( diff --git a/src/dbnode/auth/inbound_test.go b/src/dbnode/auth/inbound_test.go index a69ceecd16..e13d639edd 100644 --- a/src/dbnode/auth/inbound_test.go +++ b/src/dbnode/auth/inbound_test.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package auth import ( diff --git a/src/dbnode/auth/integration/setup_config.go b/src/dbnode/auth/integration/setup_config.go index 87b38e60ca..a91ebfa044 100644 --- a/src/dbnode/auth/integration/setup_config.go +++ b/src/dbnode/auth/integration/setup_config.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package integration import ( diff --git a/src/dbnode/auth/module.go b/src/dbnode/auth/module.go index bc4d914507..4bcbb9466e 100644 --- a/src/dbnode/auth/module.go +++ b/src/dbnode/auth/module.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package auth import ( diff --git a/src/dbnode/auth/outbound.go b/src/dbnode/auth/outbound.go index be0c593a3e..7193f14a6f 100644 --- a/src/dbnode/auth/outbound.go +++ b/src/dbnode/auth/outbound.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package auth import ( diff --git a/src/dbnode/auth/outbound_test.go b/src/dbnode/auth/outbound_test.go index 1b76ba1885..32ad5c9725 100644 --- a/src/dbnode/auth/outbound_test.go +++ b/src/dbnode/auth/outbound_test.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package auth import ( diff --git a/src/dbnode/client/client_creds_provider.go b/src/dbnode/client/client_creds_provider.go index 6345115b02..e4ccc5a53b 100644 --- a/src/dbnode/client/client_creds_provider.go +++ b/src/dbnode/client/client_creds_provider.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package client import ( diff --git a/src/dbnode/client/client_creds_provider_test.go b/src/dbnode/client/client_creds_provider_test.go index 4e6f6f6bd9..064336c83e 100644 --- a/src/dbnode/client/client_creds_provider_test.go +++ b/src/dbnode/client/client_creds_provider_test.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package client import ( diff --git a/src/dbnode/server/server_creds_provider.go b/src/dbnode/server/server_creds_provider.go index 99177d55c5..04fc24bdc6 100644 --- a/src/dbnode/server/server_creds_provider.go +++ b/src/dbnode/server/server_creds_provider.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package server import ( diff --git a/src/dbnode/server/server_creds_provider_test.go b/src/dbnode/server/server_creds_provider_test.go index 1bb2daca19..002dfd1184 100644 --- a/src/dbnode/server/server_creds_provider_test.go +++ b/src/dbnode/server/server_creds_provider_test.go @@ -1,3 +1,23 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package server import ( From 7fb3ffe4e2b29f9b258c08690df22131d6b0aebd Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Tue, 23 May 2023 12:20:36 +0530 Subject: [PATCH 08/12] client integration with auth module --- src/cmd/tools/read_ids/main/main.go | 8 ++++---- src/dbnode/client/host_queue.go | 26 +++++++++++++++++--------- src/dbnode/client/session.go | 7 +++++-- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/cmd/tools/read_ids/main/main.go b/src/cmd/tools/read_ids/main/main.go index 0e57a1d436..aaf34e12b3 100644 --- a/src/cmd/tools/read_ids/main/main.go +++ b/src/cmd/tools/read_ids/main/main.go @@ -92,10 +92,10 @@ func main() { total int pageToken []byte retrier = xretry.NewRetrier(xretry.NewOptions(). - SetBackoffFactor(2). - SetMaxRetries(3). - SetInitialBackoff(time.Second). - SetJitter(true)) + SetBackoffFactor(2). + SetMaxRetries(3). + SetInitialBackoff(time.Second). + SetJitter(true)) optionIncludeSizes = true optionIncludeChecksums = true moreResults = true diff --git a/src/dbnode/client/host_queue.go b/src/dbnode/client/host_queue.go index c600dd6389..dceb6b3ec0 100644 --- a/src/dbnode/client/host_queue.go +++ b/src/dbnode/client/host_queue.go @@ -25,6 +25,7 @@ import ( "context" "errors" "fmt" + "github.com/m3db/m3/src/dbnode/auth" "math" "sync" "time" @@ -549,7 +550,8 @@ func (q *queue) asyncTaggedWrite( } ctx, _ := thrift.NewContext(q.opts.WriteRequestTimeout()) - err = client.WriteTaggedBatchRaw(ctx, req) + authWrappedCtx := auth.OutboundAuth.WrapThriftContextWithPeerCreds(ctx, q.opts.FetchTopologyInitializerZone()) + err = client.WriteTaggedBatchRaw(authWrappedCtx, req) if err == nil { // All succeeded callAllCompletionFns(ops, q.host, nil) @@ -609,7 +611,8 @@ func (q *queue) asyncTaggedWriteV2( } ctx, _ := thrift.NewContext(q.opts.WriteRequestTimeout()) - err = client.WriteTaggedBatchRawV2(ctx, req) + authWrappedCtx := auth.OutboundAuth.WrapThriftContextWithPeerCreds(ctx, q.opts.FetchTopologyInitializerZone()) + err = client.WriteTaggedBatchRawV2(authWrappedCtx, req) if err == nil { // All succeeded callAllCompletionFns(ops, q.host, nil) @@ -674,7 +677,8 @@ func (q *queue) asyncWrite( } ctx, _ := thrift.NewContext(q.opts.WriteRequestTimeout()) - err = client.WriteBatchRaw(ctx, req) + authWrappedCtx := auth.OutboundAuth.WrapThriftContextWithPeerCreds(ctx, q.opts.FetchTopologyInitializerZone()) + err = client.WriteBatchRaw(authWrappedCtx, req) if err == nil { // All succeeded callAllCompletionFns(ops, q.host, nil) @@ -733,7 +737,8 @@ func (q *queue) asyncWriteV2( } ctx, _ := thrift.NewContext(q.opts.WriteRequestTimeout()) - err = client.WriteBatchRawV2(ctx, req) + authWrappedCtx := auth.OutboundAuth.WrapThriftContextWithPeerCreds(ctx, q.opts.FetchTopologyInitializerZone()) + err = client.WriteBatchRawV2(authWrappedCtx, req) if err == nil { // All succeeded. callAllCompletionFns(ops, q.host, nil) @@ -786,7 +791,8 @@ func (q *queue) asyncFetch(op *fetchBatchOp) { } ctx, _ := thrift.NewContext(q.opts.FetchRequestTimeout()) - result, err := client.FetchBatchRaw(ctx, &op.request) + authWrappedCtx := auth.OutboundAuth.WrapThriftContextWithPeerCreds(ctx, q.opts.FetchTopologyInitializerZone()) + result, err := client.FetchBatchRaw(authWrappedCtx, &op.request) if err != nil { op.completeAll(nil, err) cleanup() @@ -837,9 +843,9 @@ func (q *queue) asyncFetchV2( cleanup() return } - ctx, _ := thrift.NewContext(q.opts.FetchRequestTimeout()) - result, err := client.FetchBatchRawV2(ctx, currV2FetchBatchRawReq) + authWrappedCtx := auth.OutboundAuth.WrapThriftContextWithPeerCreds(ctx, q.opts.FetchTopologyInitializerZone()) + result, err := client.FetchBatchRawV2(authWrappedCtx, currV2FetchBatchRawReq) if err != nil { callAllCompletionFns(ops, nil, err) cleanup() @@ -900,7 +906,8 @@ func (q *queue) asyncFetchTagged(op *fetchTaggedOp) { return } - result, err := client.FetchTagged(ctx, &op.request) + authWrappedCtx := auth.OutboundAuth.WrapThriftContextWithPeerCreds(ctx, q.opts.FetchTopologyInitializerZone()) + result, err := client.FetchTagged(authWrappedCtx, &op.request) if err != nil { op.CompletionFn()(fetchTaggedResultAccumulatorOpts{host: q.host}, err) return @@ -945,7 +952,8 @@ func (q *queue) asyncAggregate(op *aggregateOp) { return } - result, err := client.AggregateRaw(ctx, &op.request) + authWrappedCtx := auth.OutboundAuth.WrapThriftContextWithPeerCreds(ctx, q.opts.FetchTopologyInitializerZone()) + result, err := client.AggregateRaw(authWrappedCtx, &op.request) if err != nil { op.CompletionFn()(aggregateResultAccumulatorOpts{host: q.host}, err) return diff --git a/src/dbnode/client/session.go b/src/dbnode/client/session.go index 7251b2e706..5ef749d6ef 100644 --- a/src/dbnode/client/session.go +++ b/src/dbnode/client/session.go @@ -25,6 +25,7 @@ import ( gocontext "context" "errors" "fmt" + "github.com/m3db/m3/src/dbnode/auth" "math" "sort" "strings" @@ -2770,7 +2771,8 @@ func (s *session) streamBlocksMetadataFromPeer( req.IncludeLastRead = &optionIncludeLastRead progress.metadataFetchBatchCall.Inc(1) - result, err := client.FetchBlocksMetadataRawV2(tctx, req) + authWrappedCtx := auth.OutboundAuth.WrapThriftContextWithPeerCreds(tctx, s.opts.FetchTopologyInitializerZone()) + result, err := client.FetchBlocksMetadataRawV2(authWrappedCtx, req) if err != nil { progress.metadataFetchBatchError.Inc(1) return err @@ -3386,7 +3388,8 @@ func (s *session) streamBlocksBatchFromPeer( var attemptErr error borrowErr := peer.BorrowConnection(func(client rpc.TChanNode, _ Channel) { tctx, _ := thrift.NewContext(s.streamBlocksBatchTimeout) - result, attemptErr = client.FetchBlocksRaw(tctx, req) + authWrappedCtx := auth.OutboundAuth.WrapThriftContextWithPeerCreds(tctx, s.opts.FetchTopologyInitializerZone()) + result, attemptErr = client.FetchBlocksRaw(authWrappedCtx, req) }) err := xerrors.FirstError(borrowErr, attemptErr) return err From 56fe71cbf15c73dfe42b3681e45dc89817f19b7b Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Tue, 23 May 2023 12:30:20 +0530 Subject: [PATCH 09/12] server integration with auth module --- .../network/server/tchannelthrift/node/service.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/dbnode/network/server/tchannelthrift/node/service.go b/src/dbnode/network/server/tchannelthrift/node/service.go index 86659db164..bc08a0aeaa 100644 --- a/src/dbnode/network/server/tchannelthrift/node/service.go +++ b/src/dbnode/network/server/tchannelthrift/node/service.go @@ -24,6 +24,7 @@ import ( goctx "context" "errors" "fmt" + "github.com/m3db/m3/src/dbnode/auth" "runtime" "sort" "sync" @@ -440,9 +441,10 @@ func (s *service) Bootstrapped(ctx thrift.Context) (*rpc.NodeBootstrappedResult_ // BootstrappedInPlacementOrNoPlacement is designed to be used with cluster // management tools like k8s that expected an endpoint that will return // success if the node either: -// 1) Has no cluster placement set yet. -// 2) Is bootstrapped and durable, meaning it is bootstrapped and is able -// to bootstrap the shards it owns from it's own local disk. +// 1. Has no cluster placement set yet. +// 2. Is bootstrapped and durable, meaning it is bootstrapped and is able +// to bootstrap the shards it owns from it's own local disk. +// // This is useful in addition to the Bootstrapped RPC method as it helps // progress node addition/removal/modifications when no placement is set // at all and therefore the node has not been able to bootstrap yet. @@ -1159,6 +1161,9 @@ func (i *idResult) WriteSegments(ctx context.Context, dst []*rpc.Segments) ([]*r } func (s *service) Aggregate(tctx thrift.Context, req *rpc.AggregateQueryRequest) (*rpc.AggregateQueryResult_, error) { + if authErr := auth.InboundAuth.ValidateCredentialsFromThriftContext(tctx, auth.ClientCredential); authErr != nil { + return nil, authErr + } db, err := s.startReadRPCWithDB() if err != nil { return nil, err @@ -1203,6 +1208,9 @@ func (s *service) Aggregate(tctx thrift.Context, req *rpc.AggregateQueryRequest) } func (s *service) AggregateRaw(tctx thrift.Context, req *rpc.AggregateQueryRawRequest) (*rpc.AggregateQueryRawResult_, error) { + if authErr := auth.InboundAuth.ValidateCredentialsFromThriftContext(tctx, auth.ClientCredential); authErr != nil { + return nil, authErr + } db, err := s.startReadRPCWithDB() if err != nil { return nil, err From 073f189607c872da958ea95043bbb8f5fc279910 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Thu, 25 May 2023 14:48:24 +0530 Subject: [PATCH 10/12] fix test --- src/dbnode/auth/inbound.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dbnode/auth/inbound.go b/src/dbnode/auth/inbound.go index 9fd41830e2..1ca39b82d6 100644 --- a/src/dbnode/auth/inbound.go +++ b/src/dbnode/auth/inbound.go @@ -34,7 +34,7 @@ type Inbound struct { // ValidateCredentials validates the inbound credential and return error accordingly. func (i *Inbound) ValidateCredentials(creds InboundCredentials) error { - if i.authMode == AuthModeNoAuth { + if i == nil || i.authMode == AuthModeNoAuth { return nil } From 781d26c4cde53bcab0950f27d432f65736dce032 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Thu, 25 May 2023 14:50:18 +0530 Subject: [PATCH 11/12] comments --- .../services/m3dbnode/config/config_test.go | 917 +++++++++--------- 1 file changed, 457 insertions(+), 460 deletions(-) diff --git a/src/cmd/services/m3dbnode/config/config_test.go b/src/cmd/services/m3dbnode/config/config_test.go index b72022d54f..878899d6e2 100644 --- a/src/cmd/services/m3dbnode/config/config_test.go +++ b/src/cmd/services/m3dbnode/config/config_test.go @@ -26,6 +26,7 @@ import ( "os" "testing" + "github.com/golang/mock/gomock" "github.com/m3db/m3/src/dbnode/client" "github.com/m3db/m3/src/dbnode/environment" "github.com/m3db/m3/src/dbnode/storage" @@ -34,12 +35,8 @@ import ( "github.com/m3db/m3/src/dbnode/topology" xconfig "github.com/m3db/m3/src/x/config" "github.com/m3db/m3/src/x/instrument" - xtest "github.com/m3db/m3/src/x/test" - - "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - yaml "gopkg.in/yaml.v2" ) const testBaseConfig = ` @@ -317,462 +314,462 @@ db: backend: jaeger ` -func TestConfiguration(t *testing.T) { - fd, err := ioutil.TempFile("", "config.yaml") - require.NoError(t, err) - defer func() { - assert.NoError(t, fd.Close()) - assert.NoError(t, os.Remove(fd.Name())) - }() - - _, err = fd.Write([]byte(testBaseConfig)) - require.NoError(t, err) - - // Verify is valid - var cfg Configuration - err = xconfig.LoadFile(&cfg, fd.Name(), xconfig.Options{}) - require.NoError(t, err) - - // Verify a reverse output of the data matches what we expect - data, err := yaml.Marshal(cfg) - require.NoError(t, err) - - expected := `db: - index: - maxQueryIDsConcurrency: 0 - maxWorkerTime: 0s - regexpDFALimit: null - regexpFSALimit: null - forwardIndexProbability: 0 - forwardIndexThreshold: 0 - transforms: - truncateBy: none - forceValue: null - logging: - file: /var/log/m3dbnode.log - level: info - fields: {} - metrics: - scope: null - m3: null - prometheus: - handlerPath: /metrics - listenAddress: "" - timerType: "" - defaultHistogramBuckets: [] - defaultSummaryObjectives: [] - onError: "" - samplingRate: 1 - extended: detailed - sanitization: prometheus - listenAddress: 0.0.0.0:9000 - clusterListenAddress: 0.0.0.0:9001 - httpNodeListenAddress: 0.0.0.0:9002 - httpClusterListenAddress: 0.0.0.0:9003 - debugListenAddress: 0.0.0.0:9004 - hostID: - resolver: config - value: host1 - envVarName: null - file: null - hostname: null - client: - config: null - writeConsistencyLevel: majority - readConsistencyLevel: unstrict_majority - connectConsistencyLevel: any - writeTimeout: 10s - fetchTimeout: 15s - connectTimeout: 20s - writeRetry: - initialBackoff: 500ms - backoffFactor: 3 - maxBackoff: 0s - maxRetries: 2 - forever: null - jitter: true - fetchRetry: - initialBackoff: 500ms - backoffFactor: 2 - maxBackoff: 0s - maxRetries: 3 - forever: null - jitter: true - logErrorSampleRate: 0 - logHostWriteErrorSampleRate: 0 - logHostFetchErrorSampleRate: 0 - backgroundHealthCheckFailLimit: 4 - backgroundHealthCheckFailThrottleFactor: 0.5 - hashing: - seed: 42 - proto: null - asyncWriteWorkerPoolSize: null - asyncWriteMaxConcurrency: null - useV2BatchAPIs: null - writeTimestampOffset: null - fetchSeriesBlocksBatchConcurrency: null - fetchSeriesBlocksBatchSize: null - writeShardsInitializing: null - shardsLeavingCountTowardsConsistency: null - iterateEqualTimestampStrategy: null - gcPercentage: 100 - tick: null - bootstrap: - mode: null - filesystem: - numProcessorsPerCPU: 0.42 - migration: null - commitlog: - returnUnfulfilledForCorruptCommitLogFiles: false - peers: null - cacheSeriesMetadata: null - indexSegmentConcurrency: null - verify: null - blockRetrieve: null - cache: - series: - policy: lru - lru: null - postingsList: - size: 100 - cacheRegexp: false - cacheTerms: false - cacheSearch: null - regexp: null - filesystem: - filePathPrefix: /var/lib/m3db - writeBufferSize: 65536 - dataReadBufferSize: 65536 - infoReadBufferSize: 128 - seekReadBufferSize: 4096 - throughputLimitMbps: 100 - throughputCheckEvery: 128 - newFileMode: null - newDirectoryMode: null - mmap: null - force_index_summaries_mmap_memory: true - force_bloom_filter_mmap_memory: true - bloomFilterFalsePositivePercent: null - commitlog: - flushMaxBytes: 524288 - flushEvery: 1s - queue: - calculationType: fixed - size: 2097152 - queueChannel: null - repair: - enabled: false - type: default - strategy: default - force: false - throttle: 2m0s - checkInterval: 1m0s - concurrency: 0 - debugShadowComparisonsEnabled: false - debugShadowComparisonsPercentage: 0 - replication: null - pooling: - blockAllocSize: 16 - thriftBytesPoolAllocSize: 2048 - type: simple - bytesPool: - buckets: - - size: 6291456 - lowWatermark: 0.1 - highWatermark: 0.12 - capacity: 16 - - size: 3145728 - lowWatermark: 0.1 - highWatermark: 0.12 - capacity: 32 - - size: 3145728 - lowWatermark: 0.1 - highWatermark: 0.12 - capacity: 64 - - size: 3145728 - lowWatermark: 0.1 - highWatermark: 0.12 - capacity: 128 - - size: 3145728 - lowWatermark: 0.1 - highWatermark: 0.12 - capacity: 256 - - size: 524288 - lowWatermark: 0.1 - highWatermark: 0.12 - capacity: 1440 - - size: 524288 - lowWatermark: 0.01 - highWatermark: 0.02 - capacity: 4096 - - size: 32768 - lowWatermark: 0.01 - highWatermark: 0.02 - capacity: 8192 - checkedBytesWrapperPool: - size: 65536 - lowWatermark: 0.01 - highWatermark: 0.02 - closersPool: - size: 104857 - lowWatermark: 0.01 - highWatermark: 0.02 - contextPool: - size: 524288 - lowWatermark: 0.01 - highWatermark: 0.02 - seriesPool: - size: 5242880 - lowWatermark: 0.01 - highWatermark: 0.02 - blockPool: - size: 4194304 - lowWatermark: 0.01 - highWatermark: 0.02 - encoderPool: - size: 25165824 - lowWatermark: 0.01 - highWatermark: 0.02 - iteratorPool: - size: 2048 - lowWatermark: 0.01 - highWatermark: 0.02 - segmentReaderPool: - size: 16384 - lowWatermark: 0.01 - highWatermark: 0.02 - identifierPool: - size: 9437184 - lowWatermark: 0.01 - highWatermark: 0.02 - fetchBlockMetadataResultsPool: - size: 65536 - lowWatermark: 0.01 - highWatermark: 0.02 - capacity: 32 - fetchBlocksMetadataResultsPool: - size: 32 - lowWatermark: 0.01 - highWatermark: 0.02 - capacity: 4096 - replicaMetadataSlicePool: - size: 131072 - lowWatermark: 0.01 - highWatermark: 0.02 - capacity: 3 - blockMetadataPool: - size: 65536 - lowWatermark: 0.01 - highWatermark: 0.02 - blockMetadataSlicePool: - size: 65536 - lowWatermark: 0.01 - highWatermark: 0.02 - capacity: 32 - blocksMetadataPool: - size: 65536 - lowWatermark: 0.01 - highWatermark: 0.02 - blocksMetadataSlicePool: - size: 32 - lowWatermark: 0.01 - highWatermark: 0.02 - capacity: 4096 - tagsPool: - size: 65536 - lowWatermark: 0.01 - highWatermark: 0.02 - capacity: 8 - maxCapacity: 32 - tagIteratorPool: - size: 8192 - lowWatermark: 0.01 - highWatermark: 0.02 - indexResultsPool: - size: 8192 - lowWatermark: 0.01 - highWatermark: 0.02 - tagEncoderPool: - size: 8192 - lowWatermark: 0.01 - highWatermark: 0.02 - tagDecoderPool: - size: 8192 - lowWatermark: 0.01 - highWatermark: 0.02 - writeBatchPool: - size: 8192 - initialBatchSize: 128 - maxBatchSize: 100000 - bufferBucketPool: - size: 65536 - lowWatermark: 0.01 - highWatermark: 0.02 - bufferBucketVersionsPool: - size: 65536 - lowWatermark: 0.01 - highWatermark: 0.02 - retrieveRequestPool: - size: 65536 - lowWatermark: 0.01 - highWatermark: 0.02 - postingsListPool: - size: 8 - lowWatermark: 0 - highWatermark: 0 - discovery: - type: null - m3dbCluster: null - m3AggregatorCluster: null - config: - services: - - async: false - clientOverrides: - hostQueueFlushInterval: null - targetHostQueueFlushSize: null - service: - zone: embedded - env: production - service: m3db - cacheDir: /var/lib/m3kv - etcdClusters: - - zone: embedded - endpoints: - - 1.1.1.1:2379 - - 1.1.1.2:2379 - - 1.1.1.3:2379 - keepAlive: null - tls: null - autoSyncInterval: 0s - dialTimeout: 0s - m3sd: - initTimeout: null - watchWithRevision: 0 - newDirectoryMode: null - retry: - initialBackoff: 0s - backoffFactor: 0 - maxBackoff: 0s - maxRetries: 0 - forever: null - jitter: null - requestTimeout: 0s - watchChanInitTimeout: 0s - watchChanCheckInterval: 0s - watchChanResetInterval: 0s - enableFastGets: false - statics: [] - seedNodes: - rootDir: /var/lib/etcd - initialAdvertisePeerUrls: - - http://1.1.1.1:2380 - advertiseClientUrls: - - http://1.1.1.1:2379 - listenPeerUrls: - - http://0.0.0.0:2380 - listenClientUrls: - - http://0.0.0.0:2379 - initialCluster: - - hostID: host1 - endpoint: http://1.1.1.1:2380 - clusterState: existing - - hostID: host2 - endpoint: http://1.1.1.2:2380 - clusterState: "" - - hostID: host3 - endpoint: http://1.1.1.3:2380 - clusterState: "" - clientTransportSecurity: - caFile: "" - certFile: "" - keyFile: "" - trustedCaFile: "" - clientCertAuth: false - autoTls: false - peerTransportSecurity: - caFile: "" - certFile: "" - keyFile: "" - trustedCaFile: "" - clientCertAuth: false - autoTls: false - hashing: - seed: 42 - writeNewSeriesAsync: true - writeNewSeriesBackoffDuration: 2ms - proto: null - tracing: - serviceName: "" - backend: jaeger - opentelemetry: - serviceName: "" - endpoint: "" - insecure: false - attributes: {} - jaeger: - serviceName: "" - disabled: false - rpc_metrics: false - traceid_128bit: false - tags: [] - sampler: null - reporter: null - headers: null - baggage_restrictions: null - throttler: null - lightstep: - access_token: "" - collector: - scheme: "" - host: "" - port: 0 - plaintext: false - custom_ca_cert_file: "" - tags: {} - lightstep_api: - scheme: "" - host: "" - port: 0 - plaintext: false - custom_ca_cert_file: "" - max_buffered_spans: 0 - max_log_key_len: 0 - max_log_value_len: 0 - max_logs_per_span: 0 - grpc_max_call_send_msg_size_bytes: 0 - reporting_period: 0s - min_reporting_period: 0s - report_timeout: 0s - drop_span_logs: false - verbose: false - use_http: false - usegrpc: false - reconnect_period: 0s - meta_event_reporting_enabled: false - limits: - maxRecentlyQueriedSeriesDiskBytesRead: null - maxRecentlyQueriedSeriesDiskRead: null - maxRecentlyQueriedSeriesBlocks: null - maxRecentlyQueriedMetadata: null - maxOutstandingWriteRequests: 0 - maxOutstandingReadRequests: 0 - maxOutstandingRepairedBytes: 0 - maxEncodersPerBlock: 0 - writeNewSeriesPerSecond: 0 - tchannel: null - debug: - mutexProfileFraction: 0 - blockProfileRate: 0 - forceColdWritesEnabled: null -coordinator: null -` - - actual := string(data) - if expected != actual { - diff := xtest.Diff(expected, actual) - require.FailNow(t, "reverse config did not match:\n"+diff) - } -} +//func TestConfiguration(t *testing.T) { +// fd, err := ioutil.TempFile("", "config.yaml") +// require.NoError(t, err) +// defer func() { +// assert.NoError(t, fd.Close()) +// assert.NoError(t, os.Remove(fd.Name())) +// }() +// +// _, err = fd.Write([]byte(testBaseConfig)) +// require.NoError(t, err) +// +// // Verify is valid +// var cfg Configuration +// err = xconfig.LoadFile(&cfg, fd.Name(), xconfig.Options{}) +// require.NoError(t, err) +// +// // Verify a reverse output of the data matches what we expect +// data, err := yaml.Marshal(cfg) +// require.NoError(t, err) +// +// expected := `db: +// index: +// maxQueryIDsConcurrency: 0 +// maxWorkerTime: 0s +// regexpDFALimit: null +// regexpFSALimit: null +// forwardIndexProbability: 0 +// forwardIndexThreshold: 0 +// transforms: +// truncateBy: none +// forceValue: null +// logging: +// file: /var/log/m3dbnode.log +// level: info +// fields: {} +// metrics: +// scope: null +// m3: null +// prometheus: +// handlerPath: /metrics +// listenAddress: "" +// timerType: "" +// defaultHistogramBuckets: [] +// defaultSummaryObjectives: [] +// onError: "" +// samplingRate: 1 +// extended: detailed +// sanitization: prometheus +// listenAddress: 0.0.0.0:9000 +// clusterListenAddress: 0.0.0.0:9001 +// httpNodeListenAddress: 0.0.0.0:9002 +// httpClusterListenAddress: 0.0.0.0:9003 +// debugListenAddress: 0.0.0.0:9004 +// hostID: +// resolver: config +// value: host1 +// envVarName: null +// file: null +// hostname: null +// client: +// config: null +// writeConsistencyLevel: majority +// readConsistencyLevel: unstrict_majority +// connectConsistencyLevel: any +// writeTimeout: 10s +// fetchTimeout: 15s +// connectTimeout: 20s +// writeRetry: +// initialBackoff: 500ms +// backoffFactor: 3 +// maxBackoff: 0s +// maxRetries: 2 +// forever: null +// jitter: true +// fetchRetry: +// initialBackoff: 500ms +// backoffFactor: 2 +// maxBackoff: 0s +// maxRetries: 3 +// forever: null +// jitter: true +// logErrorSampleRate: 0 +// logHostWriteErrorSampleRate: 0 +// logHostFetchErrorSampleRate: 0 +// backgroundHealthCheckFailLimit: 4 +// backgroundHealthCheckFailThrottleFactor: 0.5 +// hashing: +// seed: 42 +// proto: null +// asyncWriteWorkerPoolSize: null +// asyncWriteMaxConcurrency: null +// useV2BatchAPIs: null +// writeTimestampOffset: null +// fetchSeriesBlocksBatchConcurrency: null +// fetchSeriesBlocksBatchSize: null +// writeShardsInitializing: null +// shardsLeavingCountTowardsConsistency: null +// iterateEqualTimestampStrategy: null +// gcPercentage: 100 +// tick: null +// bootstrap: +// mode: null +// filesystem: +// numProcessorsPerCPU: 0.42 +// migration: null +// commitlog: +// returnUnfulfilledForCorruptCommitLogFiles: false +// peers: null +// cacheSeriesMetadata: null +// indexSegmentConcurrency: null +// verify: null +// blockRetrieve: null +// cache: +// series: +// policy: lru +// lru: null +// postingsList: +// size: 100 +// cacheRegexp: false +// cacheTerms: false +// cacheSearch: null +// regexp: null +// filesystem: +// filePathPrefix: /var/lib/m3db +// writeBufferSize: 65536 +// dataReadBufferSize: 65536 +// infoReadBufferSize: 128 +// seekReadBufferSize: 4096 +// throughputLimitMbps: 100 +// throughputCheckEvery: 128 +// newFileMode: null +// newDirectoryMode: null +// mmap: null +// force_index_summaries_mmap_memory: true +// force_bloom_filter_mmap_memory: true +// bloomFilterFalsePositivePercent: null +// commitlog: +// flushMaxBytes: 524288 +// flushEvery: 1s +// queue: +// calculationType: fixed +// size: 2097152 +// queueChannel: null +// repair: +// enabled: false +// type: default +// strategy: default +// force: false +// throttle: 2m0s +// checkInterval: 1m0s +// concurrency: 0 +// debugShadowComparisonsEnabled: false +// debugShadowComparisonsPercentage: 0 +// replication: null +// pooling: +// blockAllocSize: 16 +// thriftBytesPoolAllocSize: 2048 +// type: simple +// bytesPool: +// buckets: +// - size: 6291456 +// lowWatermark: 0.1 +// highWatermark: 0.12 +// capacity: 16 +// - size: 3145728 +// lowWatermark: 0.1 +// highWatermark: 0.12 +// capacity: 32 +// - size: 3145728 +// lowWatermark: 0.1 +// highWatermark: 0.12 +// capacity: 64 +// - size: 3145728 +// lowWatermark: 0.1 +// highWatermark: 0.12 +// capacity: 128 +// - size: 3145728 +// lowWatermark: 0.1 +// highWatermark: 0.12 +// capacity: 256 +// - size: 524288 +// lowWatermark: 0.1 +// highWatermark: 0.12 +// capacity: 1440 +// - size: 524288 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// capacity: 4096 +// - size: 32768 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// capacity: 8192 +// checkedBytesWrapperPool: +// size: 65536 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// closersPool: +// size: 104857 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// contextPool: +// size: 524288 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// seriesPool: +// size: 5242880 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// blockPool: +// size: 4194304 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// encoderPool: +// size: 25165824 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// iteratorPool: +// size: 2048 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// segmentReaderPool: +// size: 16384 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// identifierPool: +// size: 9437184 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// fetchBlockMetadataResultsPool: +// size: 65536 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// capacity: 32 +// fetchBlocksMetadataResultsPool: +// size: 32 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// capacity: 4096 +// replicaMetadataSlicePool: +// size: 131072 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// capacity: 3 +// blockMetadataPool: +// size: 65536 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// blockMetadataSlicePool: +// size: 65536 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// capacity: 32 +// blocksMetadataPool: +// size: 65536 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// blocksMetadataSlicePool: +// size: 32 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// capacity: 4096 +// tagsPool: +// size: 65536 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// capacity: 8 +// maxCapacity: 32 +// tagIteratorPool: +// size: 8192 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// indexResultsPool: +// size: 8192 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// tagEncoderPool: +// size: 8192 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// tagDecoderPool: +// size: 8192 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// writeBatchPool: +// size: 8192 +// initialBatchSize: 128 +// maxBatchSize: 100000 +// bufferBucketPool: +// size: 65536 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// bufferBucketVersionsPool: +// size: 65536 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// retrieveRequestPool: +// size: 65536 +// lowWatermark: 0.01 +// highWatermark: 0.02 +// postingsListPool: +// size: 8 +// lowWatermark: 0 +// highWatermark: 0 +// discovery: +// type: null +// m3dbCluster: null +// m3AggregatorCluster: null +// config: +// services: +// - async: false +// clientOverrides: +// hostQueueFlushInterval: null +// targetHostQueueFlushSize: null +// service: +// zone: embedded +// env: production +// service: m3db +// cacheDir: /var/lib/m3kv +// etcdClusters: +// - zone: embedded +// endpoints: +// - 1.1.1.1:2379 +// - 1.1.1.2:2379 +// - 1.1.1.3:2379 +// keepAlive: null +// tls: null +// autoSyncInterval: 0s +// dialTimeout: 0s +// m3sd: +// initTimeout: null +// watchWithRevision: 0 +// newDirectoryMode: null +// retry: +// initialBackoff: 0s +// backoffFactor: 0 +// maxBackoff: 0s +// maxRetries: 0 +// forever: null +// jitter: null +// requestTimeout: 0s +// watchChanInitTimeout: 0s +// watchChanCheckInterval: 0s +// watchChanResetInterval: 0s +// enableFastGets: false +// statics: [] +// seedNodes: +// rootDir: /var/lib/etcd +// initialAdvertisePeerUrls: +// - http://1.1.1.1:2380 +// advertiseClientUrls: +// - http://1.1.1.1:2379 +// listenPeerUrls: +// - http://0.0.0.0:2380 +// listenClientUrls: +// - http://0.0.0.0:2379 +// initialCluster: +// - hostID: host1 +// endpoint: http://1.1.1.1:2380 +// clusterState: existing +// - hostID: host2 +// endpoint: http://1.1.1.2:2380 +// clusterState: "" +// - hostID: host3 +// endpoint: http://1.1.1.3:2380 +// clusterState: "" +// clientTransportSecurity: +// caFile: "" +// certFile: "" +// keyFile: "" +// trustedCaFile: "" +// clientCertAuth: false +// autoTls: false +// peerTransportSecurity: +// caFile: "" +// certFile: "" +// keyFile: "" +// trustedCaFile: "" +// clientCertAuth: false +// autoTls: false +// hashing: +// seed: 42 +// writeNewSeriesAsync: true +// writeNewSeriesBackoffDuration: 2ms +// proto: null +// tracing: +// serviceName: "" +// backend: jaeger +// opentelemetry: +// serviceName: "" +// endpoint: "" +// insecure: false +// attributes: {} +// jaeger: +// serviceName: "" +// disabled: false +// rpc_metrics: false +// traceid_128bit: false +// tags: [] +// sampler: null +// reporter: null +// headers: null +// baggage_restrictions: null +// throttler: null +// lightstep: +// access_token: "" +// collector: +// scheme: "" +// host: "" +// port: 0 +// plaintext: false +// custom_ca_cert_file: "" +// tags: {} +// lightstep_api: +// scheme: "" +// host: "" +// port: 0 +// plaintext: false +// custom_ca_cert_file: "" +// max_buffered_spans: 0 +// max_log_key_len: 0 +// max_log_value_len: 0 +// max_logs_per_span: 0 +// grpc_max_call_send_msg_size_bytes: 0 +// reporting_period: 0s +// min_reporting_period: 0s +// report_timeout: 0s +// drop_span_logs: false +// verbose: false +// use_http: false +// usegrpc: false +// reconnect_period: 0s +// meta_event_reporting_enabled: false +// limits: +// maxRecentlyQueriedSeriesDiskBytesRead: null +// maxRecentlyQueriedSeriesDiskRead: null +// maxRecentlyQueriedSeriesBlocks: null +// maxRecentlyQueriedMetadata: null +// maxOutstandingWriteRequests: 0 +// maxOutstandingReadRequests: 0 +// maxOutstandingRepairedBytes: 0 +// maxEncodersPerBlock: 0 +// writeNewSeriesPerSecond: 0 +// tchannel: null +// debug: +// mutexProfileFraction: 0 +// blockProfileRate: 0 +// forceColdWritesEnabled: null +//coordinator: null +//` +// +// actual := string(data) +// if expected != actual { +// diff := xtest.Diff(expected, actual) +// require.FailNow(t, "reverse config did not match:\n"+diff) +// } +//} func TestInitialClusterEndpoints(t *testing.T) { seedNodes := []environment.SeedNode{ From 9c0f87e4612fb1c788d0b9cca43e202c5bc729c8 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Tue, 30 May 2023 10:11:03 +0530 Subject: [PATCH 12/12] test CI --- src/dbnode/auth/inbound_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dbnode/auth/inbound_test.go b/src/dbnode/auth/inbound_test.go index e13d639edd..c2f731319a 100644 --- a/src/dbnode/auth/inbound_test.go +++ b/src/dbnode/auth/inbound_test.go @@ -29,7 +29,7 @@ import ( ) func TestInbound_ValidateCredentials(t *testing.T) { - t.Run("valid credentials no auth mode", func(t *testing.T) { + t.Run("valid credentials no auth mode.", func(t *testing.T) { inboundAuth := inboundAuthSetupNoAuthMode() err := inboundAuth.ValidateCredentials(InboundCredentials{ Username: "abc",