diff --git a/pkg/apis/deployment/v1alpha/authentication_spec.go b/pkg/apis/deployment/v1alpha/authentication_spec.go index 4ef4c664b..c6e5bcbac 100644 --- a/pkg/apis/deployment/v1alpha/authentication_spec.go +++ b/pkg/apis/deployment/v1alpha/authentication_spec.go @@ -25,12 +25,13 @@ package v1alpha import ( "github.com/pkg/errors" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" ) // AuthenticationSpec holds authentication specific configuration settings type AuthenticationSpec struct { - JWTSecretName string `json:"jwtSecretName,omitempty"` + JWTSecretName *string `json:"jwtSecretName,omitempty"` } const ( @@ -38,10 +39,15 @@ const ( JWTSecretNameDisabled = "None" ) +// GetJWTSecretName returns the value of jwtSecretName. +func (s AuthenticationSpec) GetJWTSecretName() string { + return util.StringOrDefault(s.JWTSecretName) +} + // IsAuthenticated returns true if authentication is enabled. // Returns false other (when JWTSecretName == "None"). func (s AuthenticationSpec) IsAuthenticated() bool { - return s.JWTSecretName != JWTSecretNameDisabled + return s.GetJWTSecretName() != JWTSecretNameDisabled } // Validate the given spec @@ -50,7 +56,7 @@ func (s AuthenticationSpec) Validate(required bool) error { return maskAny(errors.Wrap(ValidationError, "JWT secret is required")) } if s.IsAuthenticated() { - if err := k8sutil.ValidateResourceName(s.JWTSecretName); err != nil { + if err := k8sutil.ValidateResourceName(s.GetJWTSecretName()); err != nil { return maskAny(err) } } @@ -59,8 +65,17 @@ func (s AuthenticationSpec) Validate(required bool) error { // SetDefaults fills in missing defaults func (s *AuthenticationSpec) SetDefaults(defaultJWTSecretName string) { - if s.JWTSecretName == "" { - s.JWTSecretName = defaultJWTSecretName + if s.GetJWTSecretName() == "" { + // Note that we don't check for nil here, since even a specified, but empty + // string should result in the default value. + s.JWTSecretName = util.NewString(defaultJWTSecretName) + } +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *AuthenticationSpec) SetDefaultsFrom(source AuthenticationSpec) { + if s.JWTSecretName == nil { + s.JWTSecretName = util.NewStringOrNil(source.JWTSecretName) } } @@ -71,7 +86,7 @@ func (s AuthenticationSpec) ResetImmutableFields(fieldPrefix string, target *Aut var resetFields []string if s.IsAuthenticated() != target.IsAuthenticated() { // Note: You can change the name, but not from empty to non-empty (or reverse). - target.JWTSecretName = s.JWTSecretName + target.JWTSecretName = util.NewStringOrNil(s.JWTSecretName) resetFields = append(resetFields, fieldPrefix+".jwtSecretName") } return resetFields diff --git a/pkg/apis/deployment/v1alpha/authentication_spec_test.go b/pkg/apis/deployment/v1alpha/authentication_spec_test.go index 206312629..0c00354db 100644 --- a/pkg/apis/deployment/v1alpha/authentication_spec_test.go +++ b/pkg/apis/deployment/v1alpha/authentication_spec_test.go @@ -25,23 +25,24 @@ package v1alpha import ( "testing" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/stretchr/testify/assert" ) func TestAuthenticationSpecValidate(t *testing.T) { // Valid - assert.Nil(t, AuthenticationSpec{JWTSecretName: "None"}.Validate(false)) - assert.Nil(t, AuthenticationSpec{JWTSecretName: "foo"}.Validate(false)) - assert.Nil(t, AuthenticationSpec{JWTSecretName: "foo"}.Validate(true)) + assert.Nil(t, AuthenticationSpec{JWTSecretName: util.NewString("None")}.Validate(false)) + assert.Nil(t, AuthenticationSpec{JWTSecretName: util.NewString("foo")}.Validate(false)) + assert.Nil(t, AuthenticationSpec{JWTSecretName: util.NewString("foo")}.Validate(true)) // Not valid - assert.Error(t, AuthenticationSpec{JWTSecretName: "Foo"}.Validate(false)) + assert.Error(t, AuthenticationSpec{JWTSecretName: util.NewString("Foo")}.Validate(false)) } func TestAuthenticationSpecIsAuthenticated(t *testing.T) { - assert.False(t, AuthenticationSpec{JWTSecretName: "None"}.IsAuthenticated()) - assert.True(t, AuthenticationSpec{JWTSecretName: "foo"}.IsAuthenticated()) - assert.True(t, AuthenticationSpec{JWTSecretName: ""}.IsAuthenticated()) + assert.False(t, AuthenticationSpec{JWTSecretName: util.NewString("None")}.IsAuthenticated()) + assert.True(t, AuthenticationSpec{JWTSecretName: util.NewString("foo")}.IsAuthenticated()) + assert.True(t, AuthenticationSpec{JWTSecretName: util.NewString("")}.IsAuthenticated()) } func TestAuthenticationSpecSetDefaults(t *testing.T) { @@ -50,8 +51,8 @@ func TestAuthenticationSpecSetDefaults(t *testing.T) { return spec } - assert.Equal(t, "test-jwt", def(AuthenticationSpec{}).JWTSecretName) - assert.Equal(t, "foo", def(AuthenticationSpec{JWTSecretName: "foo"}).JWTSecretName) + assert.Equal(t, "test-jwt", def(AuthenticationSpec{}).GetJWTSecretName()) + assert.Equal(t, "foo", def(AuthenticationSpec{JWTSecretName: util.NewString("foo")}).GetJWTSecretName()) } func TestAuthenticationSpecResetImmutableFields(t *testing.T) { @@ -63,35 +64,35 @@ func TestAuthenticationSpecResetImmutableFields(t *testing.T) { }{ // Valid "changes" { - AuthenticationSpec{JWTSecretName: "None"}, - AuthenticationSpec{JWTSecretName: "None"}, - AuthenticationSpec{JWTSecretName: "None"}, + AuthenticationSpec{JWTSecretName: util.NewString("None")}, + AuthenticationSpec{JWTSecretName: util.NewString("None")}, + AuthenticationSpec{JWTSecretName: util.NewString("None")}, nil, }, { - AuthenticationSpec{JWTSecretName: "foo"}, - AuthenticationSpec{JWTSecretName: "foo"}, - AuthenticationSpec{JWTSecretName: "foo"}, + AuthenticationSpec{JWTSecretName: util.NewString("foo")}, + AuthenticationSpec{JWTSecretName: util.NewString("foo")}, + AuthenticationSpec{JWTSecretName: util.NewString("foo")}, nil, }, { - AuthenticationSpec{JWTSecretName: "foo"}, - AuthenticationSpec{JWTSecretName: "foo2"}, - AuthenticationSpec{JWTSecretName: "foo2"}, + AuthenticationSpec{JWTSecretName: util.NewString("foo")}, + AuthenticationSpec{JWTSecretName: util.NewString("foo2")}, + AuthenticationSpec{JWTSecretName: util.NewString("foo2")}, nil, }, // Invalid changes { - AuthenticationSpec{JWTSecretName: "foo"}, - AuthenticationSpec{JWTSecretName: "None"}, - AuthenticationSpec{JWTSecretName: "foo"}, + AuthenticationSpec{JWTSecretName: util.NewString("foo")}, + AuthenticationSpec{JWTSecretName: util.NewString("None")}, + AuthenticationSpec{JWTSecretName: util.NewString("foo")}, []string{"test.jwtSecretName"}, }, { - AuthenticationSpec{JWTSecretName: "None"}, - AuthenticationSpec{JWTSecretName: "foo"}, - AuthenticationSpec{JWTSecretName: "None"}, + AuthenticationSpec{JWTSecretName: util.NewString("None")}, + AuthenticationSpec{JWTSecretName: util.NewString("foo")}, + AuthenticationSpec{JWTSecretName: util.NewString("None")}, []string{"test.jwtSecretName"}, }, } diff --git a/pkg/apis/deployment/v1alpha/deployment_mode.go b/pkg/apis/deployment/v1alpha/deployment_mode.go index 33f9c1475..0be3a5382 100644 --- a/pkg/apis/deployment/v1alpha/deployment_mode.go +++ b/pkg/apis/deployment/v1alpha/deployment_mode.go @@ -73,3 +73,27 @@ func (m DeploymentMode) HasCoordinators() bool { func (m DeploymentMode) SupportsSync() bool { return m == DeploymentModeCluster } + +// NewMode returns a reference to a string with given value. +func NewMode(input DeploymentMode) *DeploymentMode { + return &input +} + +// NewModeOrNil returns nil if input is nil, otherwise returns a clone of the given value. +func NewModeOrNil(input *DeploymentMode) *DeploymentMode { + if input == nil { + return nil + } + return NewMode(*input) +} + +// ModeOrDefault returns the default value (or empty string) if input is nil, otherwise returns the referenced value. +func ModeOrDefault(input *DeploymentMode, defaultValue ...DeploymentMode) DeploymentMode { + if input == nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return "" + } + return *input +} diff --git a/pkg/apis/deployment/v1alpha/deployment_spec.go b/pkg/apis/deployment/v1alpha/deployment_spec.go index 42da4bdb0..5954719bc 100644 --- a/pkg/apis/deployment/v1alpha/deployment_spec.go +++ b/pkg/apis/deployment/v1alpha/deployment_spec.go @@ -23,6 +23,7 @@ package v1alpha import ( + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/pkg/errors" "k8s.io/api/core/v1" ) @@ -44,11 +45,11 @@ func validatePullPolicy(v v1.PullPolicy) error { // DeploymentSpec contains the spec part of a ArangoDeployment resource. type DeploymentSpec struct { - Mode DeploymentMode `json:"mode,omitempty"` - Environment Environment `json:"environment,omitempty"` - StorageEngine StorageEngine `json:"storageEngine,omitempty"` - Image string `json:"image,omitempty"` - ImagePullPolicy v1.PullPolicy `json:"imagePullPolicy,omitempty"` + Mode *DeploymentMode `json:"mode,omitempty"` + Environment *Environment `json:"environment,omitempty"` + StorageEngine *StorageEngine `json:"storageEngine,omitempty"` + Image *string `json:"image,omitempty"` + ImagePullPolicy *v1.PullPolicy `json:"imagePullPolicy,omitempty"` RocksDB RocksDBSpec `json:"rocksdb"` Authentication AuthenticationSpec `json:"auth"` @@ -63,6 +64,31 @@ type DeploymentSpec struct { SyncWorkers ServerGroupSpec `json:"syncworkers"` } +// GetMode returns the value of mode. +func (s DeploymentSpec) GetMode() DeploymentMode { + return ModeOrDefault(s.Mode) +} + +// GetEnvironment returns the value of environment. +func (s DeploymentSpec) GetEnvironment() Environment { + return EnvironmentOrDefault(s.Environment) +} + +// GetStorageEngine returns the value of storageEngine. +func (s DeploymentSpec) GetStorageEngine() StorageEngine { + return StorageEngineOrDefault(s.StorageEngine) +} + +// GetImage returns the value of image. +func (s DeploymentSpec) GetImage() string { + return util.StringOrDefault(s.Image) +} + +// GetImagePullPolicy returns the value of imagePullPolicy. +func (s DeploymentSpec) GetImagePullPolicy() v1.PullPolicy { + return util.PullPolicyOrDefault(s.ImagePullPolicy) +} + // IsAuthenticated returns true when authentication is enabled func (s DeploymentSpec) IsAuthenticated() bool { return s.Authentication.IsAuthenticated() @@ -96,49 +122,78 @@ func (s DeploymentSpec) GetServerGroupSpec(group ServerGroup) ServerGroupSpec { // SetDefaults fills in default values when a field is not specified. func (s *DeploymentSpec) SetDefaults(deploymentName string) { - if s.Mode == "" { - s.Mode = DeploymentModeCluster + if s.GetMode() == "" { + s.Mode = NewMode(DeploymentModeCluster) } - if s.Environment == "" { - s.Environment = EnvironmentDevelopment + if s.GetEnvironment() == "" { + s.Environment = NewEnvironment(EnvironmentDevelopment) } - if s.StorageEngine == "" { - s.StorageEngine = StorageEngineRocksDB + if s.GetStorageEngine() == "" { + s.StorageEngine = NewStorageEngine(StorageEngineRocksDB) } - if s.Image == "" && s.IsDevelopment() { - s.Image = defaultImage + if s.GetImage() == "" && s.IsDevelopment() { + s.Image = util.NewString(defaultImage) } - if s.ImagePullPolicy == "" { - s.ImagePullPolicy = v1.PullIfNotPresent + if s.GetImagePullPolicy() == "" { + s.ImagePullPolicy = util.NewPullPolicy(v1.PullIfNotPresent) } s.RocksDB.SetDefaults() s.Authentication.SetDefaults(deploymentName + "-jwt") s.TLS.SetDefaults(deploymentName + "-ca") - s.Sync.SetDefaults(s.Image, s.ImagePullPolicy, deploymentName+"-sync-jwt", deploymentName+"-sync-ca") - s.Single.SetDefaults(ServerGroupSingle, s.Mode.HasSingleServers(), s.Mode) - s.Agents.SetDefaults(ServerGroupAgents, s.Mode.HasAgents(), s.Mode) - s.DBServers.SetDefaults(ServerGroupDBServers, s.Mode.HasDBServers(), s.Mode) - s.Coordinators.SetDefaults(ServerGroupCoordinators, s.Mode.HasCoordinators(), s.Mode) - s.SyncMasters.SetDefaults(ServerGroupSyncMasters, s.Sync.Enabled, s.Mode) - s.SyncWorkers.SetDefaults(ServerGroupSyncWorkers, s.Sync.Enabled, s.Mode) + s.Sync.SetDefaults(s.GetImage(), s.GetImagePullPolicy(), deploymentName+"-sync-jwt", deploymentName+"-sync-ca") + s.Single.SetDefaults(ServerGroupSingle, s.GetMode().HasSingleServers(), s.GetMode()) + s.Agents.SetDefaults(ServerGroupAgents, s.GetMode().HasAgents(), s.GetMode()) + s.DBServers.SetDefaults(ServerGroupDBServers, s.GetMode().HasDBServers(), s.GetMode()) + s.Coordinators.SetDefaults(ServerGroupCoordinators, s.GetMode().HasCoordinators(), s.GetMode()) + s.SyncMasters.SetDefaults(ServerGroupSyncMasters, s.Sync.IsEnabled(), s.GetMode()) + s.SyncWorkers.SetDefaults(ServerGroupSyncWorkers, s.Sync.IsEnabled(), s.GetMode()) +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *DeploymentSpec) SetDefaultsFrom(source DeploymentSpec) { + if s.Mode == nil { + s.Mode = NewModeOrNil(source.Mode) + } + if s.Environment == nil { + s.Environment = NewEnvironmentOrNil(source.Environment) + } + if s.StorageEngine == nil { + s.StorageEngine = NewStorageEngineOrNil(source.StorageEngine) + } + if s.Image == nil { + s.Image = util.NewStringOrNil(source.Image) + } + if s.ImagePullPolicy == nil { + s.ImagePullPolicy = util.NewPullPolicyOrNil(source.ImagePullPolicy) + } + s.RocksDB.SetDefaultsFrom(source.RocksDB) + s.Authentication.SetDefaultsFrom(source.Authentication) + s.TLS.SetDefaultsFrom(source.TLS) + s.Sync.SetDefaultsFrom(source.Sync) + s.Single.SetDefaultsFrom(source.Single) + s.Agents.SetDefaultsFrom(source.Agents) + s.DBServers.SetDefaultsFrom(source.DBServers) + s.Coordinators.SetDefaultsFrom(source.Coordinators) + s.SyncMasters.SetDefaultsFrom(source.SyncMasters) + s.SyncWorkers.SetDefaultsFrom(source.SyncWorkers) } // Validate the specification. // Return errors when validation fails, nil on success. func (s *DeploymentSpec) Validate() error { - if err := s.Mode.Validate(); err != nil { + if err := s.GetMode().Validate(); err != nil { return maskAny(errors.Wrap(err, "spec.mode")) } - if err := s.Environment.Validate(); err != nil { + if err := s.GetEnvironment().Validate(); err != nil { return maskAny(errors.Wrap(err, "spec.environment")) } - if err := s.StorageEngine.Validate(); err != nil { + if err := s.GetStorageEngine().Validate(); err != nil { return maskAny(errors.Wrap(err, "spec.storageEngine")) } - if err := validatePullPolicy(s.ImagePullPolicy); err != nil { + if err := validatePullPolicy(s.GetImagePullPolicy()); err != nil { return maskAny(errors.Wrap(err, "spec.imagePullPolicy")) } - if s.Image == "" { + if s.GetImage() == "" { return maskAny(errors.Wrapf(ValidationError, "spec.image must be set")) } if err := s.RocksDB.Validate(); err != nil { @@ -150,25 +205,25 @@ func (s *DeploymentSpec) Validate() error { if err := s.TLS.Validate(); err != nil { return maskAny(errors.Wrap(err, "spec.tls")) } - if err := s.Sync.Validate(s.Mode); err != nil { + if err := s.Sync.Validate(s.GetMode()); err != nil { return maskAny(errors.Wrap(err, "spec.sync")) } - if err := s.Single.Validate(ServerGroupSingle, s.Mode.HasSingleServers(), s.Mode, s.Environment); err != nil { + if err := s.Single.Validate(ServerGroupSingle, s.GetMode().HasSingleServers(), s.GetMode(), s.GetEnvironment()); err != nil { return maskAny(err) } - if err := s.Agents.Validate(ServerGroupAgents, s.Mode.HasAgents(), s.Mode, s.Environment); err != nil { + if err := s.Agents.Validate(ServerGroupAgents, s.GetMode().HasAgents(), s.GetMode(), s.GetEnvironment()); err != nil { return maskAny(err) } - if err := s.DBServers.Validate(ServerGroupDBServers, s.Mode.HasDBServers(), s.Mode, s.Environment); err != nil { + if err := s.DBServers.Validate(ServerGroupDBServers, s.GetMode().HasDBServers(), s.GetMode(), s.GetEnvironment()); err != nil { return maskAny(err) } - if err := s.Coordinators.Validate(ServerGroupCoordinators, s.Mode.HasCoordinators(), s.Mode, s.Environment); err != nil { + if err := s.Coordinators.Validate(ServerGroupCoordinators, s.GetMode().HasCoordinators(), s.GetMode(), s.GetEnvironment()); err != nil { return maskAny(err) } - if err := s.SyncMasters.Validate(ServerGroupSyncMasters, s.Sync.Enabled, s.Mode, s.Environment); err != nil { + if err := s.SyncMasters.Validate(ServerGroupSyncMasters, s.Sync.IsEnabled(), s.GetMode(), s.GetEnvironment()); err != nil { return maskAny(err) } - if err := s.SyncWorkers.Validate(ServerGroupSyncWorkers, s.Sync.Enabled, s.Mode, s.Environment); err != nil { + if err := s.SyncWorkers.Validate(ServerGroupSyncWorkers, s.Sync.IsEnabled(), s.GetMode(), s.GetEnvironment()); err != nil { return maskAny(err) } return nil @@ -176,7 +231,7 @@ func (s *DeploymentSpec) Validate() error { // IsDevelopment returns true when the spec contains a Development environment. func (s DeploymentSpec) IsDevelopment() bool { - return s.Environment == EnvironmentDevelopment + return s.GetEnvironment() == EnvironmentDevelopment } // ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. @@ -184,12 +239,12 @@ func (s DeploymentSpec) IsDevelopment() bool { // Field names are relative to `spec.`. func (s DeploymentSpec) ResetImmutableFields(target *DeploymentSpec) []string { var resetFields []string - if s.Mode != target.Mode { - target.Mode = s.Mode + if s.GetMode() != target.GetMode() { + target.Mode = NewModeOrNil(s.Mode) resetFields = append(resetFields, "mode") } - if s.StorageEngine != target.StorageEngine { - target.StorageEngine = s.StorageEngine + if s.GetStorageEngine() != target.GetStorageEngine() { + target.StorageEngine = NewStorageEngineOrNil(s.StorageEngine) resetFields = append(resetFields, "storageEngine") } if l := s.RocksDB.ResetImmutableFields("rocksdb", &target.RocksDB); l != nil { diff --git a/pkg/apis/deployment/v1alpha/deployment_spec_test.go b/pkg/apis/deployment/v1alpha/deployment_spec_test.go index 822b49642..430e48c65 100644 --- a/pkg/apis/deployment/v1alpha/deployment_spec_test.go +++ b/pkg/apis/deployment/v1alpha/deployment_spec_test.go @@ -25,6 +25,7 @@ package v1alpha import ( "testing" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/stretchr/testify/assert" "k8s.io/api/core/v1" ) @@ -39,7 +40,7 @@ func TestDeploymentSpecSetDefaults(t *testing.T) { return spec } - assert.Equal(t, "arangodb/arangodb:latest", def(DeploymentSpec{}).Image) + assert.Equal(t, "arangodb/arangodb:latest", def(DeploymentSpec{}).GetImage()) } func TestDeploymentSpecResetImmutableFields(t *testing.T) { @@ -51,23 +52,23 @@ func TestDeploymentSpecResetImmutableFields(t *testing.T) { }{ // Valid "changes" { - DeploymentSpec{Image: "foo"}, - DeploymentSpec{Image: "foo2"}, - DeploymentSpec{Image: "foo2"}, + DeploymentSpec{Image: util.NewString("foo")}, + DeploymentSpec{Image: util.NewString("foo2")}, + DeploymentSpec{Image: util.NewString("foo2")}, nil, }, { - DeploymentSpec{ImagePullPolicy: v1.PullAlways}, - DeploymentSpec{ImagePullPolicy: v1.PullNever}, - DeploymentSpec{ImagePullPolicy: v1.PullNever}, + DeploymentSpec{ImagePullPolicy: util.NewPullPolicy(v1.PullAlways)}, + DeploymentSpec{ImagePullPolicy: util.NewPullPolicy(v1.PullNever)}, + DeploymentSpec{ImagePullPolicy: util.NewPullPolicy(v1.PullNever)}, nil, }, // Invalid changes { - DeploymentSpec{Mode: DeploymentModeSingle}, - DeploymentSpec{Mode: DeploymentModeCluster}, - DeploymentSpec{Mode: DeploymentModeSingle}, + DeploymentSpec{Mode: NewMode(DeploymentModeSingle)}, + DeploymentSpec{Mode: NewMode(DeploymentModeCluster)}, + DeploymentSpec{Mode: NewMode(DeploymentModeSingle)}, []string{"mode"}, }, } diff --git a/pkg/apis/deployment/v1alpha/deployment_status.go b/pkg/apis/deployment/v1alpha/deployment_status.go index 645bf60ee..4c9660aa8 100644 --- a/pkg/apis/deployment/v1alpha/deployment_status.go +++ b/pkg/apis/deployment/v1alpha/deployment_status.go @@ -47,4 +47,7 @@ type DeploymentStatus struct { // Plan to update this deployment Plan Plan `json:"plan,omitempty"` + + // AcceptedSpec contains the last specification that was accepted by the operator. + AcceptedSpec *DeploymentSpec `json:"accepted-spec,omitempty"` } diff --git a/pkg/apis/deployment/v1alpha/environment.go b/pkg/apis/deployment/v1alpha/environment.go index ccf099bb4..62bbfb9d5 100644 --- a/pkg/apis/deployment/v1alpha/environment.go +++ b/pkg/apis/deployment/v1alpha/environment.go @@ -46,3 +46,27 @@ func (e Environment) Validate() error { return maskAny(errors.Wrapf(ValidationError, "Unknown environment: '%s'", string(e))) } } + +// NewEnvironment returns a reference to a string with given value. +func NewEnvironment(input Environment) *Environment { + return &input +} + +// NewEnvironmentOrNil returns nil if input is nil, otherwise returns a clone of the given value. +func NewEnvironmentOrNil(input *Environment) *Environment { + if input == nil { + return nil + } + return NewEnvironment(*input) +} + +// EnvironmentOrDefault returns the default value (or empty string) if input is nil, otherwise returns the referenced value. +func EnvironmentOrDefault(input *Environment, defaultValue ...Environment) Environment { + if input == nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return "" + } + return *input +} diff --git a/pkg/apis/deployment/v1alpha/monitoring_spec.go b/pkg/apis/deployment/v1alpha/monitoring_spec.go index 44c9917bc..719468cd7 100644 --- a/pkg/apis/deployment/v1alpha/monitoring_spec.go +++ b/pkg/apis/deployment/v1alpha/monitoring_spec.go @@ -23,17 +23,23 @@ package v1alpha import ( + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" ) // MonitoringSpec holds monitoring specific configuration settings type MonitoringSpec struct { - TokenSecretName string `json:"tokenSecretName,omitempty"` + TokenSecretName *string `json:"tokenSecretName,omitempty"` +} + +// GetTokenSecretName returns the value of tokenSecretName. +func (s MonitoringSpec) GetTokenSecretName() string { + return util.StringOrDefault(s.TokenSecretName) } // Validate the given spec func (s MonitoringSpec) Validate() error { - if err := k8sutil.ValidateOptionalResourceName(s.TokenSecretName); err != nil { + if err := k8sutil.ValidateOptionalResourceName(s.GetTokenSecretName()); err != nil { return maskAny(err) } return nil @@ -43,3 +49,10 @@ func (s MonitoringSpec) Validate() error { func (s *MonitoringSpec) SetDefaults() { // Nothing needed } + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *MonitoringSpec) SetDefaultsFrom(source MonitoringSpec) { + if s.TokenSecretName == nil { + s.TokenSecretName = util.NewStringOrNil(source.TokenSecretName) + } +} diff --git a/pkg/apis/deployment/v1alpha/monitoring_spec_test.go b/pkg/apis/deployment/v1alpha/monitoring_spec_test.go index 1aef3e71e..8ece949d0 100644 --- a/pkg/apis/deployment/v1alpha/monitoring_spec_test.go +++ b/pkg/apis/deployment/v1alpha/monitoring_spec_test.go @@ -25,17 +25,19 @@ package v1alpha import ( "testing" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/stretchr/testify/assert" ) func TestMonitoringSpecValidate(t *testing.T) { // Valid - assert.Nil(t, MonitoringSpec{TokenSecretName: ""}.Validate()) - assert.Nil(t, MonitoringSpec{TokenSecretName: "foo"}.Validate()) - assert.Nil(t, MonitoringSpec{TokenSecretName: "foo"}.Validate()) + assert.Nil(t, MonitoringSpec{TokenSecretName: nil}.Validate()) + assert.Nil(t, MonitoringSpec{TokenSecretName: util.NewString("")}.Validate()) + assert.Nil(t, MonitoringSpec{TokenSecretName: util.NewString("foo")}.Validate()) + assert.Nil(t, MonitoringSpec{TokenSecretName: util.NewString("foo")}.Validate()) // Not valid - assert.Error(t, MonitoringSpec{TokenSecretName: "Foo"}.Validate()) + assert.Error(t, MonitoringSpec{TokenSecretName: util.NewString("Foo")}.Validate()) } func TestMonitoringSpecSetDefaults(t *testing.T) { @@ -44,6 +46,6 @@ func TestMonitoringSpecSetDefaults(t *testing.T) { return spec } - assert.Equal(t, "", def(MonitoringSpec{}).TokenSecretName) - assert.Equal(t, "foo", def(MonitoringSpec{TokenSecretName: "foo"}).TokenSecretName) + assert.Equal(t, "", def(MonitoringSpec{}).GetTokenSecretName()) + assert.Equal(t, "foo", def(MonitoringSpec{TokenSecretName: util.NewString("foo")}).GetTokenSecretName()) } diff --git a/pkg/apis/deployment/v1alpha/rocksdb_spec.go b/pkg/apis/deployment/v1alpha/rocksdb_spec.go index ad7e050bd..2f0c40249 100644 --- a/pkg/apis/deployment/v1alpha/rocksdb_spec.go +++ b/pkg/apis/deployment/v1alpha/rocksdb_spec.go @@ -23,12 +23,24 @@ package v1alpha import ( + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" ) // RocksDBEncryptionSpec holds rocksdb encryption at rest specific configuration settings type RocksDBEncryptionSpec struct { - KeySecretName string `json:"keySecretName,omitempty"` + KeySecretName *string `json:"keySecretName,omitempty"` +} + +// GetKeySecretName returns the value of keySecretName. +func (s RocksDBEncryptionSpec) GetKeySecretName() string { + return util.StringOrDefault(s.KeySecretName) +} + +// IsEncrypted returns true when an encryption key secret name is provided, +// false otherwise. +func (s RocksDBEncryptionSpec) IsEncrypted() bool { + return s.GetKeySecretName() != "" } // RocksDBSpec holds rocksdb specific configuration settings @@ -39,12 +51,12 @@ type RocksDBSpec struct { // IsEncrypted returns true when an encryption key secret name is provided, // false otherwise. func (s RocksDBSpec) IsEncrypted() bool { - return s.Encryption.KeySecretName != "" + return s.Encryption.IsEncrypted() } // Validate the given spec func (s RocksDBSpec) Validate() error { - if err := k8sutil.ValidateOptionalResourceName(s.Encryption.KeySecretName); err != nil { + if err := k8sutil.ValidateOptionalResourceName(s.Encryption.GetKeySecretName()); err != nil { return maskAny(err) } return nil @@ -55,6 +67,13 @@ func (s *RocksDBSpec) SetDefaults() { // Nothing needed } +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *RocksDBSpec) SetDefaultsFrom(source RocksDBSpec) { + if s.Encryption.KeySecretName == nil { + s.Encryption.KeySecretName = util.NewStringOrNil(source.Encryption.KeySecretName) + } +} + // ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. // It returns a list of fields that have been reset. // Field names are relative to given field prefix. @@ -62,7 +81,7 @@ func (s RocksDBSpec) ResetImmutableFields(fieldPrefix string, target *RocksDBSpe var resetFields []string if s.IsEncrypted() != target.IsEncrypted() { // Note: You can change the name, but not from empty to non-empty (or reverse). - target.Encryption.KeySecretName = s.Encryption.KeySecretName + target.Encryption.KeySecretName = util.NewStringOrNil(s.Encryption.KeySecretName) resetFields = append(resetFields, fieldPrefix+".encryption.keySecretName") } return resetFields diff --git a/pkg/apis/deployment/v1alpha/rocksdb_spec_test.go b/pkg/apis/deployment/v1alpha/rocksdb_spec_test.go index a93b82fd1..068199f51 100644 --- a/pkg/apis/deployment/v1alpha/rocksdb_spec_test.go +++ b/pkg/apis/deployment/v1alpha/rocksdb_spec_test.go @@ -25,22 +25,23 @@ package v1alpha import ( "testing" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/stretchr/testify/assert" ) func TestRocksDBSpecValidate(t *testing.T) { // Valid assert.Nil(t, RocksDBSpec{}.Validate()) - assert.Nil(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}}.Validate()) + assert.Nil(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}.Validate()) // Not valid - assert.Error(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "Foo"}}.Validate()) + assert.Error(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("Foo")}}.Validate()) } func TestRocksDBSpecIsEncrypted(t *testing.T) { assert.False(t, RocksDBSpec{}.IsEncrypted()) - assert.False(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: ""}}.IsEncrypted()) - assert.True(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}}.IsEncrypted()) + assert.False(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("")}}.IsEncrypted()) + assert.True(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}.IsEncrypted()) } func TestRocksDBSpecSetDefaults(t *testing.T) { @@ -49,7 +50,7 @@ func TestRocksDBSpecSetDefaults(t *testing.T) { return spec } - assert.Equal(t, "", def(RocksDBSpec{}).Encryption.KeySecretName) + assert.Equal(t, "", def(RocksDBSpec{}).Encryption.GetKeySecretName()) } func TestRocksDBSpecResetImmutableFields(t *testing.T) { @@ -67,23 +68,23 @@ func TestRocksDBSpecResetImmutableFields(t *testing.T) { nil, }, { - RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}}, - RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}}, - RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}}, + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}, + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}, + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}, nil, }, { - RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}}, - RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo2"}}, - RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo2"}}, + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}, + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo2")}}, + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo2")}}, nil, }, // Invalid changes { - RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}}, - RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: ""}}, - RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}}, + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}, + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("")}}, + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}, []string{"test.encryption.keySecretName"}, }, } diff --git a/pkg/apis/deployment/v1alpha/server_group_spec.go b/pkg/apis/deployment/v1alpha/server_group_spec.go index 0a8f7228d..f73bc3788 100644 --- a/pkg/apis/deployment/v1alpha/server_group_spec.go +++ b/pkg/apis/deployment/v1alpha/server_group_spec.go @@ -26,18 +26,35 @@ import ( "github.com/pkg/errors" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + + "github.com/arangodb/kube-arangodb/pkg/util" ) // ServerGroupSpec contains the specification for all servers in a specific group (e.g. all agents) type ServerGroupSpec struct { // Count holds the requested number of servers - Count int `json:"count,omitempty"` + Count *int `json:"count,omitempty"` // Args holds additional commandline arguments Args []string `json:"args,omitempty"` // StorageClassName specifies the classname for storage of the servers. - StorageClassName string `json:"storageClassName,omitempty"` + StorageClassName *string `json:"storageClassName,omitempty"` // Resources holds resource requests & limits - Resources v1.ResourceRequirements `json:"resource,omitempty"` + Resources v1.ResourceRequirements `json:"resources,omitempty"` +} + +// GetCount returns the value of count. +func (s ServerGroupSpec) GetCount() int { + return util.IntOrDefault(s.Count) +} + +// GetArgs returns the value of args. +func (s ServerGroupSpec) GetArgs() []string { + return s.Args +} + +// GetStorageClassName returns the value of storageClassName. +func (s ServerGroupSpec) GetStorageClassName() string { + return util.StringOrDefault(s.StorageClassName) } // Validate the given group spec @@ -56,30 +73,30 @@ func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentM minCount = 2 } } - if s.Count < minCount { - return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected >= %d", s.Count, minCount)) + if s.GetCount() < minCount { + return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected >= %d", s.GetCount(), minCount)) } - if s.Count > 1 && group == ServerGroupSingle && mode == DeploymentModeSingle { - return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected 1", s.Count)) + if s.GetCount() > 1 && group == ServerGroupSingle && mode == DeploymentModeSingle { + return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected 1", s.GetCount())) } - } else if s.Count != 0 { - return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d for un-used group. Expected 0", s.Count)) + } else if s.GetCount() != 0 { + return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d for un-used group. Expected 0", s.GetCount())) } return nil } // SetDefaults fills in missing defaults func (s *ServerGroupSpec) SetDefaults(group ServerGroup, used bool, mode DeploymentMode) { - if s.Count == 0 && used { + if s.GetCount() == 0 && used { switch group { case ServerGroupSingle: if mode == DeploymentModeSingle { - s.Count = 1 // Single server + s.Count = util.NewInt(1) // Single server } else { - s.Count = 2 // Resilient single + s.Count = util.NewInt(2) // Resilient single } default: - s.Count = 3 + s.Count = util.NewInt(3) } } if _, found := s.Resources.Requests[v1.ResourceStorage]; !found { @@ -93,18 +110,45 @@ func (s *ServerGroupSpec) SetDefaults(group ServerGroup, used bool, mode Deploym } } +// setDefaultsFromResourceList fills unspecified fields with a value from given source spec. +func setDefaultsFromResourceList(s *v1.ResourceList, source v1.ResourceList) { + for k, v := range source { + if *s == nil { + *s = make(v1.ResourceList) + } + if _, found := (*s)[k]; !found { + (*s)[k] = v + } + } +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *ServerGroupSpec) SetDefaultsFrom(source ServerGroupSpec) { + if s.Count == nil { + s.Count = util.NewIntOrNil(source.Count) + } + if s.Args == nil { + s.Args = source.Args + } + if s.StorageClassName == nil { + s.StorageClassName = util.NewStringOrNil(source.StorageClassName) + } + setDefaultsFromResourceList(&s.Resources.Limits, source.Resources.Limits) + setDefaultsFromResourceList(&s.Resources.Requests, source.Resources.Requests) +} + // ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. // It returns a list of fields that have been reset. func (s ServerGroupSpec) ResetImmutableFields(group ServerGroup, fieldPrefix string, target *ServerGroupSpec) []string { var resetFields []string if group == ServerGroupAgents { - if s.Count != target.Count { - target.Count = s.Count + if s.GetCount() != target.GetCount() { + target.Count = util.NewIntOrNil(s.Count) resetFields = append(resetFields, fieldPrefix+".count") } } - if s.StorageClassName != target.StorageClassName { - target.StorageClassName = s.StorageClassName + if s.GetStorageClassName() != target.GetStorageClassName() { + target.StorageClassName = util.NewStringOrNil(s.StorageClassName) resetFields = append(resetFields, fieldPrefix+".storageClassName") } return resetFields diff --git a/pkg/apis/deployment/v1alpha/server_group_spec_test.go b/pkg/apis/deployment/v1alpha/server_group_spec_test.go index 908affdd4..53134094e 100644 --- a/pkg/apis/deployment/v1alpha/server_group_spec_test.go +++ b/pkg/apis/deployment/v1alpha/server_group_spec_test.go @@ -25,50 +25,51 @@ package v1alpha import ( "testing" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/stretchr/testify/assert" ) func TestServerGroupSpecValidateCount(t *testing.T) { // Valid - assert.Nil(t, ServerGroupSpec{Count: 1}.Validate(ServerGroupSingle, true, DeploymentModeSingle, EnvironmentDevelopment)) - assert.Nil(t, ServerGroupSpec{Count: 0}.Validate(ServerGroupSingle, false, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Nil(t, ServerGroupSpec{Count: 1}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Nil(t, ServerGroupSpec{Count: 3}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Nil(t, ServerGroupSpec{Count: 1}.Validate(ServerGroupAgents, true, DeploymentModeResilientSingle, EnvironmentDevelopment)) - assert.Nil(t, ServerGroupSpec{Count: 3}.Validate(ServerGroupAgents, true, DeploymentModeResilientSingle, EnvironmentDevelopment)) - assert.Nil(t, ServerGroupSpec{Count: 1}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Nil(t, ServerGroupSpec{Count: 6}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Nil(t, ServerGroupSpec{Count: 1}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Nil(t, ServerGroupSpec{Count: 2}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Nil(t, ServerGroupSpec{Count: 3}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentProduction)) - assert.Nil(t, ServerGroupSpec{Count: 3}.Validate(ServerGroupAgents, true, DeploymentModeResilientSingle, EnvironmentProduction)) - assert.Nil(t, ServerGroupSpec{Count: 2}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentProduction)) - assert.Nil(t, ServerGroupSpec{Count: 2}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentProduction)) - assert.Nil(t, ServerGroupSpec{Count: 2}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentProduction)) - assert.Nil(t, ServerGroupSpec{Count: 2}.Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupSingle, true, DeploymentModeSingle, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(0)}.Validate(ServerGroupSingle, false, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(3)}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupAgents, true, DeploymentModeResilientSingle, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(3)}.Validate(ServerGroupAgents, true, DeploymentModeResilientSingle, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(6)}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(3)}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(3)}.Validate(ServerGroupAgents, true, DeploymentModeResilientSingle, EnvironmentProduction)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentProduction)) // Invalid - assert.Error(t, ServerGroupSpec{Count: 1}.Validate(ServerGroupSingle, false, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Error(t, ServerGroupSpec{Count: 2}.Validate(ServerGroupSingle, true, DeploymentModeSingle, EnvironmentDevelopment)) - assert.Error(t, ServerGroupSpec{Count: 1}.Validate(ServerGroupSingle, true, DeploymentModeResilientSingle, EnvironmentProduction)) - assert.Error(t, ServerGroupSpec{Count: 0}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Error(t, ServerGroupSpec{Count: 0}.Validate(ServerGroupAgents, true, DeploymentModeResilientSingle, EnvironmentDevelopment)) - assert.Error(t, ServerGroupSpec{Count: 0}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Error(t, ServerGroupSpec{Count: 0}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Error(t, ServerGroupSpec{Count: 0}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Error(t, ServerGroupSpec{Count: 0}.Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Error(t, ServerGroupSpec{Count: -1}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Error(t, ServerGroupSpec{Count: -1}.Validate(ServerGroupAgents, true, DeploymentModeResilientSingle, EnvironmentDevelopment)) - assert.Error(t, ServerGroupSpec{Count: -1}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Error(t, ServerGroupSpec{Count: -1}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Error(t, ServerGroupSpec{Count: -1}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Error(t, ServerGroupSpec{Count: -1}.Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentDevelopment)) - assert.Error(t, ServerGroupSpec{Count: 2}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentProduction)) - assert.Error(t, ServerGroupSpec{Count: 2}.Validate(ServerGroupAgents, true, DeploymentModeResilientSingle, EnvironmentProduction)) - assert.Error(t, ServerGroupSpec{Count: 1}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentProduction)) - assert.Error(t, ServerGroupSpec{Count: 1}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentProduction)) - assert.Error(t, ServerGroupSpec{Count: 1}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentProduction)) - assert.Error(t, ServerGroupSpec{Count: 1}.Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupSingle, false, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupSingle, true, DeploymentModeSingle, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupSingle, true, DeploymentModeResilientSingle, EnvironmentProduction)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(0)}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(0)}.Validate(ServerGroupAgents, true, DeploymentModeResilientSingle, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(0)}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(0)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(0)}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(0)}.Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(-1)}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(-1)}.Validate(ServerGroupAgents, true, DeploymentModeResilientSingle, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(-1)}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(-1)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(-1)}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(-1)}.Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupAgents, true, DeploymentModeResilientSingle, EnvironmentProduction)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentProduction)) } func TestServerGroupSpecDefault(t *testing.T) { @@ -77,32 +78,32 @@ func TestServerGroupSpecDefault(t *testing.T) { return spec } - assert.Equal(t, 1, def(ServerGroupSpec{}, ServerGroupSingle, true, DeploymentModeSingle).Count) - assert.Equal(t, 2, def(ServerGroupSpec{}, ServerGroupSingle, true, DeploymentModeResilientSingle).Count) - assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSingle, false, DeploymentModeCluster).Count) + assert.Equal(t, 1, def(ServerGroupSpec{}, ServerGroupSingle, true, DeploymentModeSingle).GetCount()) + assert.Equal(t, 2, def(ServerGroupSpec{}, ServerGroupSingle, true, DeploymentModeResilientSingle).GetCount()) + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSingle, false, DeploymentModeCluster).GetCount()) - assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupAgents, false, DeploymentModeSingle).Count) - assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupAgents, true, DeploymentModeResilientSingle).Count) - assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupAgents, true, DeploymentModeCluster).Count) + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupAgents, false, DeploymentModeSingle).GetCount()) + assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupAgents, true, DeploymentModeResilientSingle).GetCount()) + assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupAgents, true, DeploymentModeCluster).GetCount()) - assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupDBServers, false, DeploymentModeSingle).Count) - assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupDBServers, false, DeploymentModeResilientSingle).Count) - assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupDBServers, true, DeploymentModeCluster).Count) + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupDBServers, false, DeploymentModeSingle).GetCount()) + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupDBServers, false, DeploymentModeResilientSingle).GetCount()) + assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupDBServers, true, DeploymentModeCluster).GetCount()) - assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupCoordinators, false, DeploymentModeSingle).Count) - assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupCoordinators, false, DeploymentModeResilientSingle).Count) - assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupCoordinators, true, DeploymentModeCluster).Count) + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupCoordinators, false, DeploymentModeSingle).GetCount()) + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupCoordinators, false, DeploymentModeResilientSingle).GetCount()) + assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupCoordinators, true, DeploymentModeCluster).GetCount()) - assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncMasters, false, DeploymentModeSingle).Count) - assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncMasters, false, DeploymentModeResilientSingle).Count) - assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupSyncMasters, true, DeploymentModeCluster).Count) + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncMasters, false, DeploymentModeSingle).GetCount()) + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncMasters, false, DeploymentModeResilientSingle).GetCount()) + assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupSyncMasters, true, DeploymentModeCluster).GetCount()) - assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncWorkers, false, DeploymentModeSingle).Count) - assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncWorkers, false, DeploymentModeResilientSingle).Count) - assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupSyncWorkers, true, DeploymentModeCluster).Count) + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncWorkers, false, DeploymentModeSingle).GetCount()) + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncWorkers, false, DeploymentModeResilientSingle).GetCount()) + assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupSyncWorkers, true, DeploymentModeCluster).GetCount()) for _, g := range AllServerGroups { assert.Equal(t, 0, len(def(ServerGroupSpec{}, g, true, DeploymentModeSingle).Args)) - assert.Equal(t, "", def(ServerGroupSpec{}, g, true, DeploymentModeSingle).StorageClassName) + assert.Equal(t, "", def(ServerGroupSpec{}, g, true, DeploymentModeSingle).GetStorageClassName()) } } diff --git a/pkg/apis/deployment/v1alpha/storage_engine.go b/pkg/apis/deployment/v1alpha/storage_engine.go index 5cee17ab4..354c1a3b5 100644 --- a/pkg/apis/deployment/v1alpha/storage_engine.go +++ b/pkg/apis/deployment/v1alpha/storage_engine.go @@ -46,3 +46,27 @@ func (se StorageEngine) Validate() error { return maskAny(errors.Wrapf(ValidationError, "Unknown storage engine: '%s'", string(se))) } } + +// NewStorageEngine returns a reference to a string with given value. +func NewStorageEngine(input StorageEngine) *StorageEngine { + return &input +} + +// NewStorageEngineOrNil returns nil if input is nil, otherwise returns a clone of the given value. +func NewStorageEngineOrNil(input *StorageEngine) *StorageEngine { + if input == nil { + return nil + } + return NewStorageEngine(*input) +} + +// StorageEngineOrDefault returns the default value (or empty string) if input is nil, otherwise returns the referenced value. +func StorageEngineOrDefault(input *StorageEngine, defaultValue ...StorageEngine) StorageEngine { + if input == nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return "" + } + return *input +} diff --git a/pkg/apis/deployment/v1alpha/sync_spec.go b/pkg/apis/deployment/v1alpha/sync_spec.go index 07b83fc11..56aad0f64 100644 --- a/pkg/apis/deployment/v1alpha/sync_spec.go +++ b/pkg/apis/deployment/v1alpha/sync_spec.go @@ -25,31 +25,48 @@ package v1alpha import ( "github.com/pkg/errors" "k8s.io/api/core/v1" + + "github.com/arangodb/kube-arangodb/pkg/util" ) // SyncSpec holds dc2dc replication specific configuration settings type SyncSpec struct { - Enabled bool `json:"enabled,omitempty"` - Image string `json:"image,omitempty"` - ImagePullPolicy v1.PullPolicy `json:"imagePullPolicy,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Image *string `json:"image,omitempty"` + ImagePullPolicy *v1.PullPolicy `json:"imagePullPolicy,omitempty"` Authentication AuthenticationSpec `json:"auth"` TLS TLSSpec `json:"tls"` Monitoring MonitoringSpec `json:"monitoring"` } +// IsEnabled returns the value of enabled. +func (s SyncSpec) IsEnabled() bool { + return util.BoolOrDefault(s.Enabled) +} + +// GetImage returns the value of image. +func (s SyncSpec) GetImage() string { + return util.StringOrDefault(s.Image) +} + +// GetImagePullPolicy returns the value of imagePullPolicy. +func (s SyncSpec) GetImagePullPolicy() v1.PullPolicy { + return util.PullPolicyOrDefault(s.ImagePullPolicy) +} + // Validate the given spec func (s SyncSpec) Validate(mode DeploymentMode) error { - if s.Enabled && !mode.SupportsSync() { + if s.IsEnabled() && !mode.SupportsSync() { return maskAny(errors.Wrapf(ValidationError, "Cannot enable sync with mode: '%s'", mode)) } - if s.Image == "" { + if s.GetImage() == "" { return maskAny(errors.Wrapf(ValidationError, "image must be set")) } - if err := s.Authentication.Validate(s.Enabled); err != nil { + if err := s.Authentication.Validate(s.IsEnabled()); err != nil { return maskAny(err) } - if s.Enabled { + if s.IsEnabled() { if err := s.TLS.Validate(); err != nil { return maskAny(err) } @@ -62,17 +79,33 @@ func (s SyncSpec) Validate(mode DeploymentMode) error { // SetDefaults fills in missing defaults func (s *SyncSpec) SetDefaults(defaultImage string, defaulPullPolicy v1.PullPolicy, defaultJWTSecretName, defaultCASecretName string) { - if s.Image == "" { - s.Image = defaultImage + if s.GetImage() == "" { + s.Image = util.NewString(defaultImage) } - if s.ImagePullPolicy == "" { - s.ImagePullPolicy = defaulPullPolicy + if s.GetImagePullPolicy() == "" { + s.ImagePullPolicy = util.NewPullPolicy(defaulPullPolicy) } s.Authentication.SetDefaults(defaultJWTSecretName) s.TLS.SetDefaults(defaultCASecretName) s.Monitoring.SetDefaults() } +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *SyncSpec) SetDefaultsFrom(source SyncSpec) { + if s.Enabled == nil { + s.Enabled = util.NewBoolOrNil(source.Enabled) + } + if s.Image == nil { + s.Image = util.NewStringOrNil(source.Image) + } + if s.ImagePullPolicy == nil { + s.ImagePullPolicy = util.NewPullPolicyOrNil(source.ImagePullPolicy) + } + s.Authentication.SetDefaultsFrom(source.Authentication) + s.TLS.SetDefaultsFrom(source.TLS) + s.Monitoring.SetDefaultsFrom(source.Monitoring) +} + // ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. // It returns a list of fields that have been reset. // Field names are relative to given field prefix. diff --git a/pkg/apis/deployment/v1alpha/sync_spec_test.go b/pkg/apis/deployment/v1alpha/sync_spec_test.go index 33a024908..567edf19c 100644 --- a/pkg/apis/deployment/v1alpha/sync_spec_test.go +++ b/pkg/apis/deployment/v1alpha/sync_spec_test.go @@ -25,24 +25,26 @@ package v1alpha import ( "testing" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/stretchr/testify/assert" "k8s.io/api/core/v1" ) func TestSyncSpecValidate(t *testing.T) { // Valid - auth := AuthenticationSpec{JWTSecretName: "foo"} - assert.Nil(t, SyncSpec{Image: "foo", Authentication: auth}.Validate(DeploymentModeSingle)) - assert.Nil(t, SyncSpec{Image: "foo", Authentication: auth}.Validate(DeploymentModeResilientSingle)) - assert.Nil(t, SyncSpec{Image: "foo", Authentication: auth}.Validate(DeploymentModeCluster)) - assert.Nil(t, SyncSpec{Image: "foo", Authentication: auth, Enabled: true}.Validate(DeploymentModeCluster)) + auth := AuthenticationSpec{JWTSecretName: util.NewString("foo")} + tls := TLSSpec{CASecretName: util.NewString("None")} + assert.Nil(t, SyncSpec{Image: util.NewString("foo"), Authentication: auth}.Validate(DeploymentModeSingle)) + assert.Nil(t, SyncSpec{Image: util.NewString("foo"), Authentication: auth}.Validate(DeploymentModeResilientSingle)) + assert.Nil(t, SyncSpec{Image: util.NewString("foo"), Authentication: auth}.Validate(DeploymentModeCluster)) + assert.Nil(t, SyncSpec{Image: util.NewString("foo"), Authentication: auth, TLS: tls, Enabled: util.NewBool(true)}.Validate(DeploymentModeCluster)) // Not valid - assert.Error(t, SyncSpec{Image: "", Authentication: auth}.Validate(DeploymentModeSingle)) - assert.Error(t, SyncSpec{Image: "", Authentication: auth}.Validate(DeploymentModeResilientSingle)) - assert.Error(t, SyncSpec{Image: "", Authentication: auth}.Validate(DeploymentModeCluster)) - assert.Error(t, SyncSpec{Image: "foo", Authentication: auth, Enabled: true}.Validate(DeploymentModeSingle)) - assert.Error(t, SyncSpec{Image: "foo", Authentication: auth, Enabled: true}.Validate(DeploymentModeResilientSingle)) + assert.Error(t, SyncSpec{Image: util.NewString(""), Authentication: auth}.Validate(DeploymentModeSingle)) + assert.Error(t, SyncSpec{Image: util.NewString(""), Authentication: auth}.Validate(DeploymentModeResilientSingle)) + assert.Error(t, SyncSpec{Image: util.NewString(""), Authentication: auth}.Validate(DeploymentModeCluster)) + assert.Error(t, SyncSpec{Image: util.NewString("foo"), Authentication: auth, TLS: tls, Enabled: util.NewBool(true)}.Validate(DeploymentModeSingle)) + assert.Error(t, SyncSpec{Image: util.NewString("foo"), Authentication: auth, TLS: tls, Enabled: util.NewBool(true)}.Validate(DeploymentModeResilientSingle)) } func TestSyncSpecSetDefaults(t *testing.T) { @@ -51,15 +53,15 @@ func TestSyncSpecSetDefaults(t *testing.T) { return spec } - assert.False(t, def(SyncSpec{}).Enabled) - assert.False(t, def(SyncSpec{Enabled: false}).Enabled) - assert.True(t, def(SyncSpec{Enabled: true}).Enabled) - assert.Equal(t, "test-image", def(SyncSpec{}).Image) - assert.Equal(t, "foo", def(SyncSpec{Image: "foo"}).Image) - assert.Equal(t, v1.PullAlways, def(SyncSpec{}).ImagePullPolicy) - assert.Equal(t, v1.PullNever, def(SyncSpec{ImagePullPolicy: v1.PullNever}).ImagePullPolicy) - assert.Equal(t, "test-jwt", def(SyncSpec{}).Authentication.JWTSecretName) - assert.Equal(t, "foo", def(SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}}).Authentication.JWTSecretName) + assert.False(t, def(SyncSpec{}).IsEnabled()) + assert.False(t, def(SyncSpec{Enabled: util.NewBool(false)}).IsEnabled()) + assert.True(t, def(SyncSpec{Enabled: util.NewBool(true)}).IsEnabled()) + assert.Equal(t, "test-image", def(SyncSpec{}).GetImage()) + assert.Equal(t, "foo", def(SyncSpec{Image: util.NewString("foo")}).GetImage()) + assert.Equal(t, v1.PullAlways, def(SyncSpec{}).GetImagePullPolicy()) + assert.Equal(t, v1.PullNever, def(SyncSpec{ImagePullPolicy: util.NewPullPolicy(v1.PullNever)}).GetImagePullPolicy()) + assert.Equal(t, "test-jwt", def(SyncSpec{}).Authentication.GetJWTSecretName()) + assert.Equal(t, "foo", def(SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("foo")}}).Authentication.GetJWTSecretName()) } func TestSyncSpecResetImmutableFields(t *testing.T) { @@ -71,59 +73,59 @@ func TestSyncSpecResetImmutableFields(t *testing.T) { }{ // Valid "changes" { - SyncSpec{Enabled: false}, - SyncSpec{Enabled: true}, - SyncSpec{Enabled: true}, + SyncSpec{Enabled: util.NewBool(false)}, + SyncSpec{Enabled: util.NewBool(true)}, + SyncSpec{Enabled: util.NewBool(true)}, nil, }, { - SyncSpec{Enabled: true}, - SyncSpec{Enabled: false}, - SyncSpec{Enabled: false}, + SyncSpec{Enabled: util.NewBool(true)}, + SyncSpec{Enabled: util.NewBool(false)}, + SyncSpec{Enabled: util.NewBool(false)}, nil, }, { - SyncSpec{Image: "foo"}, - SyncSpec{Image: "foo2"}, - SyncSpec{Image: "foo2"}, + SyncSpec{Image: util.NewString("foo")}, + SyncSpec{Image: util.NewString("foo2")}, + SyncSpec{Image: util.NewString("foo2")}, nil, }, { - SyncSpec{ImagePullPolicy: v1.PullAlways}, - SyncSpec{ImagePullPolicy: v1.PullNever}, - SyncSpec{ImagePullPolicy: v1.PullNever}, + SyncSpec{ImagePullPolicy: util.NewPullPolicy(v1.PullAlways)}, + SyncSpec{ImagePullPolicy: util.NewPullPolicy(v1.PullNever)}, + SyncSpec{ImagePullPolicy: util.NewPullPolicy(v1.PullNever)}, nil, }, { - SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}}, - SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}}, - SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}}, + SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("None")}}, + SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("None")}}, + SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("None")}}, nil, }, { - SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}}, - SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}}, - SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}}, + SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("foo")}}, + SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("foo")}}, + SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("foo")}}, nil, }, { - SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}}, - SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo2"}}, - SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo2"}}, + SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("foo")}}, + SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("foo2")}}, + SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("foo2")}}, nil, }, // Invalid changes { - SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}}, - SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}}, - SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}}, + SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("foo")}}, + SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("None")}}, + SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("foo")}}, []string{"test.auth.jwtSecretName"}, }, { - SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}}, - SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}}, - SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}}, + SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("None")}}, + SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("foo")}}, + SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.NewString("None")}}, []string{"test.auth.jwtSecretName"}, }, } diff --git a/pkg/apis/deployment/v1alpha/tls_spec.go b/pkg/apis/deployment/v1alpha/tls_spec.go index a197cc019..4298f4c60 100644 --- a/pkg/apis/deployment/v1alpha/tls_spec.go +++ b/pkg/apis/deployment/v1alpha/tls_spec.go @@ -27,6 +27,7 @@ import ( "net" "time" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" "github.com/arangodb/kube-arangodb/pkg/util/validation" ) @@ -37,9 +38,9 @@ const ( // TLSSpec holds TLS specific configuration settings type TLSSpec struct { - CASecretName string `json:"caSecretName,omitempty"` - AltNames []string `json:"altNames,omitempty"` - TTL time.Duration `json:"ttl,omitempty"` + CASecretName *string `json:"caSecretName,omitempty"` + AltNames []string `json:"altNames,omitempty"` + TTL *time.Duration `json:"ttl,omitempty"` } const ( @@ -47,15 +48,30 @@ const ( CASecretNameDisabled = "None" ) +// GetCASecretName returns the value of caSecretName. +func (s TLSSpec) GetCASecretName() string { + return util.StringOrDefault(s.CASecretName) +} + +// GetAltNames returns the value of altNames. +func (s TLSSpec) GetAltNames() []string { + return s.AltNames +} + +// GetTTL returns the value of ttl. +func (s TLSSpec) GetTTL() time.Duration { + return util.DurationOrDefault(s.TTL) +} + // IsSecure returns true when a CA secret has been set, false otherwise. func (s TLSSpec) IsSecure() bool { - return s.CASecretName != CASecretNameDisabled + return s.GetCASecretName() != CASecretNameDisabled } -// GetAltNames splits the list of AltNames into DNS names, IP addresses & email addresses. +// GetParsedAltNames splits the list of AltNames into DNS names, IP addresses & email addresses. // When an entry is not valid for any of those categories, an error is returned. -func (s TLSSpec) GetAltNames() (dnsNames, ipAddresses, emailAddresses []string, err error) { - for _, name := range s.AltNames { +func (s TLSSpec) GetParsedAltNames() (dnsNames, ipAddresses, emailAddresses []string, err error) { + for _, name := range s.GetAltNames() { if net.ParseIP(name) != nil { ipAddresses = append(ipAddresses, name) } else if validation.IsValidDNSName(name) { @@ -72,10 +88,10 @@ func (s TLSSpec) GetAltNames() (dnsNames, ipAddresses, emailAddresses []string, // Validate the given spec func (s TLSSpec) Validate() error { if s.IsSecure() { - if err := k8sutil.ValidateOptionalResourceName(s.CASecretName); err != nil { + if err := k8sutil.ValidateResourceName(s.GetCASecretName()); err != nil { return maskAny(err) } - if _, _, _, err := s.GetAltNames(); err != nil { + if _, _, _, err := s.GetParsedAltNames(); err != nil { return maskAny(err) } } @@ -84,10 +100,27 @@ func (s TLSSpec) Validate() error { // SetDefaults fills in missing defaults func (s *TLSSpec) SetDefaults(defaultCASecretName string) { - if s.CASecretName == "" { - s.CASecretName = defaultCASecretName + if s.GetCASecretName() == "" { + // Note that we don't check for nil here, since even a specified, but empty + // string should result in the default value. + s.CASecretName = util.NewString(defaultCASecretName) + } + if s.GetTTL() == 0 { + // Note that we don't check for nil here, since even a specified, but zero + // should result in the default value. + s.TTL = util.NewDuration(defaultTLSTTL) + } +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *TLSSpec) SetDefaultsFrom(source TLSSpec) { + if s.CASecretName == nil { + s.CASecretName = util.NewStringOrNil(source.CASecretName) + } + if s.AltNames == nil { + s.AltNames = source.AltNames } - if s.TTL == 0 { - s.TTL = defaultTLSTTL + if s.TTL == nil { + s.TTL = util.NewDurationOrNil(source.TTL) } } diff --git a/pkg/apis/deployment/v1alpha/tls_spec_test.go b/pkg/apis/deployment/v1alpha/tls_spec_test.go index e43c0fdca..15d006ab8 100644 --- a/pkg/apis/deployment/v1alpha/tls_spec_test.go +++ b/pkg/apis/deployment/v1alpha/tls_spec_test.go @@ -26,27 +26,29 @@ import ( "testing" "time" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/stretchr/testify/assert" ) func TestTLSSpecValidate(t *testing.T) { // Valid - assert.Nil(t, TLSSpec{CASecretName: ""}.Validate()) - assert.Nil(t, TLSSpec{CASecretName: "foo"}.Validate()) - assert.Nil(t, TLSSpec{CASecretName: "None"}.Validate()) - assert.Nil(t, TLSSpec{AltNames: []string{}}.Validate()) - assert.Nil(t, TLSSpec{AltNames: []string{"foo"}}.Validate()) - assert.Nil(t, TLSSpec{AltNames: []string{"email@example.com", "127.0.0.1"}}.Validate()) + assert.Nil(t, TLSSpec{CASecretName: util.NewString("foo")}.Validate()) + assert.Nil(t, TLSSpec{CASecretName: util.NewString("None")}.Validate()) + assert.Nil(t, TLSSpec{CASecretName: util.NewString("None"), AltNames: []string{}}.Validate()) + assert.Nil(t, TLSSpec{CASecretName: util.NewString("None"), AltNames: []string{"foo"}}.Validate()) + assert.Nil(t, TLSSpec{CASecretName: util.NewString("None"), AltNames: []string{"email@example.com", "127.0.0.1"}}.Validate()) // Not valid - assert.Error(t, TLSSpec{CASecretName: "Foo"}.Validate()) - assert.Error(t, TLSSpec{AltNames: []string{"@@"}}.Validate()) + assert.Error(t, TLSSpec{CASecretName: nil}.Validate()) + assert.Error(t, TLSSpec{CASecretName: util.NewString("")}.Validate()) + assert.Error(t, TLSSpec{CASecretName: util.NewString("Foo")}.Validate()) + assert.Error(t, TLSSpec{CASecretName: util.NewString("foo"), AltNames: []string{"@@"}}.Validate()) } func TestTLSSpecIsSecure(t *testing.T) { - assert.True(t, TLSSpec{CASecretName: ""}.IsSecure()) - assert.True(t, TLSSpec{CASecretName: "foo"}.IsSecure()) - assert.False(t, TLSSpec{CASecretName: "None"}.IsSecure()) + assert.True(t, TLSSpec{CASecretName: util.NewString("")}.IsSecure()) + assert.True(t, TLSSpec{CASecretName: util.NewString("foo")}.IsSecure()) + assert.False(t, TLSSpec{CASecretName: util.NewString("None")}.IsSecure()) } func TestTLSSpecSetDefaults(t *testing.T) { @@ -55,10 +57,10 @@ func TestTLSSpecSetDefaults(t *testing.T) { return spec } - assert.Equal(t, "", def(TLSSpec{}).CASecretName) - assert.Equal(t, "foo", def(TLSSpec{CASecretName: "foo"}).CASecretName) - assert.Len(t, def(TLSSpec{}).AltNames, 0) - assert.Len(t, def(TLSSpec{AltNames: []string{"foo.local"}}).AltNames, 1) - assert.Equal(t, defaultTLSTTL, def(TLSSpec{}).TTL) - assert.Equal(t, time.Hour, def(TLSSpec{TTL: time.Hour}).TTL) + assert.Equal(t, "", def(TLSSpec{}).GetCASecretName()) + assert.Equal(t, "foo", def(TLSSpec{CASecretName: util.NewString("foo")}).GetCASecretName()) + assert.Len(t, def(TLSSpec{}).GetAltNames(), 0) + assert.Len(t, def(TLSSpec{AltNames: []string{"foo.local"}}).GetAltNames(), 1) + assert.Equal(t, defaultTLSTTL, def(TLSSpec{}).GetTTL()) + assert.Equal(t, time.Hour, def(TLSSpec{TTL: util.NewDuration(time.Hour)}).GetTTL()) } diff --git a/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go b/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go index a02a1227d..b843241da 100644 --- a/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go +++ b/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go @@ -25,6 +25,9 @@ package v1alpha import ( + time "time" + + core_v1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -119,6 +122,15 @@ func (in *ArangoDeploymentList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AuthenticationSpec) DeepCopyInto(out *AuthenticationSpec) { *out = *in + if in.JWTSecretName != nil { + in, out := &in.JWTSecretName, &out.JWTSecretName + if *in == nil { + *out = nil + } else { + *out = new(string) + **out = **in + } + } return } @@ -153,8 +165,53 @@ func (in *Condition) DeepCopy() *Condition { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) { *out = *in - out.RocksDB = in.RocksDB - out.Authentication = in.Authentication + if in.Mode != nil { + in, out := &in.Mode, &out.Mode + if *in == nil { + *out = nil + } else { + *out = new(DeploymentMode) + **out = **in + } + } + if in.Environment != nil { + in, out := &in.Environment, &out.Environment + if *in == nil { + *out = nil + } else { + *out = new(Environment) + **out = **in + } + } + if in.StorageEngine != nil { + in, out := &in.StorageEngine, &out.StorageEngine + if *in == nil { + *out = nil + } else { + *out = new(StorageEngine) + **out = **in + } + } + if in.Image != nil { + in, out := &in.Image, &out.Image + if *in == nil { + *out = nil + } else { + *out = new(string) + **out = **in + } + } + if in.ImagePullPolicy != nil { + in, out := &in.ImagePullPolicy, &out.ImagePullPolicy + if *in == nil { + *out = nil + } else { + *out = new(core_v1.PullPolicy) + **out = **in + } + } + in.RocksDB.DeepCopyInto(&out.RocksDB) + in.Authentication.DeepCopyInto(&out.Authentication) in.TLS.DeepCopyInto(&out.TLS) in.Sync.DeepCopyInto(&out.Sync) in.Single.DeepCopyInto(&out.Single) @@ -199,6 +256,15 @@ func (in *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.AcceptedSpec != nil { + in, out := &in.AcceptedSpec, &out.AcceptedSpec + if *in == nil { + *out = nil + } else { + *out = new(DeploymentSpec) + (*in).DeepCopyInto(*out) + } + } return } @@ -312,6 +378,15 @@ func (in *MemberStatus) DeepCopy() *MemberStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MonitoringSpec) DeepCopyInto(out *MonitoringSpec) { *out = *in + if in.TokenSecretName != nil { + in, out := &in.TokenSecretName, &out.TokenSecretName + if *in == nil { + *out = nil + } else { + *out = new(string) + **out = **in + } + } return } @@ -328,6 +403,15 @@ func (in *MonitoringSpec) DeepCopy() *MonitoringSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RocksDBEncryptionSpec) DeepCopyInto(out *RocksDBEncryptionSpec) { *out = *in + if in.KeySecretName != nil { + in, out := &in.KeySecretName, &out.KeySecretName + if *in == nil { + *out = nil + } else { + *out = new(string) + **out = **in + } + } return } @@ -344,7 +428,7 @@ func (in *RocksDBEncryptionSpec) DeepCopy() *RocksDBEncryptionSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RocksDBSpec) DeepCopyInto(out *RocksDBSpec) { *out = *in - out.Encryption = in.Encryption + in.Encryption.DeepCopyInto(&out.Encryption) return } @@ -361,11 +445,29 @@ func (in *RocksDBSpec) DeepCopy() *RocksDBSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServerGroupSpec) DeepCopyInto(out *ServerGroupSpec) { *out = *in + if in.Count != nil { + in, out := &in.Count, &out.Count + if *in == nil { + *out = nil + } else { + *out = new(int) + **out = **in + } + } if in.Args != nil { in, out := &in.Args, &out.Args *out = make([]string, len(*in)) copy(*out, *in) } + if in.StorageClassName != nil { + in, out := &in.StorageClassName, &out.StorageClassName + if *in == nil { + *out = nil + } else { + *out = new(string) + **out = **in + } + } in.Resources.DeepCopyInto(&out.Resources) return } @@ -383,9 +485,36 @@ func (in *ServerGroupSpec) DeepCopy() *ServerGroupSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SyncSpec) DeepCopyInto(out *SyncSpec) { *out = *in - out.Authentication = in.Authentication + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + if *in == nil { + *out = nil + } else { + *out = new(bool) + **out = **in + } + } + if in.Image != nil { + in, out := &in.Image, &out.Image + if *in == nil { + *out = nil + } else { + *out = new(string) + **out = **in + } + } + if in.ImagePullPolicy != nil { + in, out := &in.ImagePullPolicy, &out.ImagePullPolicy + if *in == nil { + *out = nil + } else { + *out = new(core_v1.PullPolicy) + **out = **in + } + } + in.Authentication.DeepCopyInto(&out.Authentication) in.TLS.DeepCopyInto(&out.TLS) - out.Monitoring = in.Monitoring + in.Monitoring.DeepCopyInto(&out.Monitoring) return } @@ -402,11 +531,29 @@ func (in *SyncSpec) DeepCopy() *SyncSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { *out = *in + if in.CASecretName != nil { + in, out := &in.CASecretName, &out.CASecretName + if *in == nil { + *out = nil + } else { + *out = new(string) + **out = **in + } + } if in.AltNames != nil { in, out := &in.AltNames, &out.AltNames *out = make([]string, len(*in)) copy(*out, *in) } + if in.TTL != nil { + in, out := &in.TTL, &out.TTL + if *in == nil { + *out = nil + } else { + *out = new(time.Duration) + **out = **in + } + } return } diff --git a/pkg/deployment/action_context.go b/pkg/deployment/action_context.go index 1ce049edd..537250579 100644 --- a/pkg/deployment/action_context.go +++ b/pkg/deployment/action_context.go @@ -83,7 +83,7 @@ type actionContext struct { // Gets the specified mode of deployment func (ac *actionContext) GetMode() api.DeploymentMode { - return ac.deployment.apiObject.Spec.Mode + return ac.deployment.apiObject.Spec.GetMode() } // GetDatabaseClient returns a cached client for the entire database (cluster coordinators or single server), diff --git a/pkg/deployment/cluster_scaling_integration.go b/pkg/deployment/cluster_scaling_integration.go index b8e39bde9..ee847b9f9 100644 --- a/pkg/deployment/cluster_scaling_integration.go +++ b/pkg/deployment/cluster_scaling_integration.go @@ -27,10 +27,12 @@ import ( "sync" "time" - api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha" - "github.com/arangodb/kube-arangodb/pkg/util/arangod" "github.com/rs/zerolog" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha" + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/arangod" ) // clusterScalingIntegration is a helper to communicate with the clusters @@ -134,10 +136,10 @@ func (ci *clusterScalingIntegration) inspectCluster(ctx context.Context) error { return maskAny(err) } if coordinatorsChanged { - current.Spec.Coordinators.Count = req.GetCoordinators() + current.Spec.Coordinators.Count = util.NewInt(req.GetCoordinators()) } if dbserversChanged { - current.Spec.DBServers.Count = req.GetDBServers() + current.Spec.DBServers.Count = util.NewInt(req.GetDBServers()) } if err := ci.depl.updateCRSpec(current.Spec); err != nil { log.Warn().Err(err).Msg("Failed to update current deployment") @@ -163,8 +165,8 @@ func (ci *clusterScalingIntegration) updateClusterServerCount(ctx context.Contex if err != nil { return false, maskAny(err) } - coordinatorCount := spec.Coordinators.Count - dbserverCount := spec.DBServers.Count + coordinatorCount := spec.Coordinators.GetCount() + dbserverCount := spec.DBServers.GetCount() if err := arangod.SetNumberOfServers(ctx, c.Connection(), coordinatorCount, dbserverCount); err != nil { log.Debug().Err(err).Msg("Failed to set number of servers") return false, maskAny(err) diff --git a/pkg/deployment/deployment.go b/pkg/deployment/deployment.go index e842964b4..02cc8b4c0 100644 --- a/pkg/deployment/deployment.go +++ b/pkg/deployment/deployment.go @@ -71,7 +71,7 @@ type deploymentEvent struct { } const ( - deploymentEventQueueSize = 100 + deploymentEventQueueSize = 256 minInspectionInterval = time.Second // Ensure we inspect the generated resources no less than with this interval maxInspectionInterval = time.Minute // Ensure we inspect the generated resources no less than with this interval ) @@ -109,10 +109,14 @@ func New(config Config, deps Dependencies, apiObject *api.ArangoDeployment) (*De eventsCli: deps.KubeCli.Core().Events(apiObject.GetNamespace()), clientCache: newClientCache(deps.KubeCli, apiObject), } + if d.status.AcceptedSpec == nil { + // We've validated the spec, so let's use it from now. + d.status.AcceptedSpec = apiObject.Spec.DeepCopy() + } go d.run() go d.listenForPodEvents() - if apiObject.Spec.Mode == api.DeploymentModeCluster { + if apiObject.Spec.GetMode() == api.DeploymentModeCluster { ci := newClusterScalingIntegration(d) d.clusterScalingIntegration = ci go ci.ListenForClusterEvents(d.stopCh) @@ -247,10 +251,14 @@ func (d *Deployment) handleArangoDeploymentUpdatedEvent(event *deploymentEvent) return maskAny(err) } + specBefore := d.apiObject.Spec + if d.status.AcceptedSpec != nil { + specBefore = *d.status.AcceptedSpec + } newAPIObject := current.DeepCopy() - newAPIObject.Spec.SetDefaults(newAPIObject.GetName()) + newAPIObject.Spec.SetDefaultsFrom(specBefore) newAPIObject.Status = d.status - resetFields := d.apiObject.Spec.ResetImmutableFields(&newAPIObject.Spec) + resetFields := specBefore.ResetImmutableFields(&newAPIObject.Spec) if len(resetFields) > 0 { log.Debug().Strs("fields", resetFields).Msg("Found modified immutable fields") } @@ -274,6 +282,11 @@ func (d *Deployment) handleArangoDeploymentUpdatedEvent(event *deploymentEvent) if err := d.updateCRSpec(newAPIObject.Spec); err != nil { return maskAny(fmt.Errorf("failed to update ArangoDeployment spec: %v", err)) } + // Save updated accepted spec + d.status.AcceptedSpec = newAPIObject.Spec.DeepCopy() + if err := d.updateCRStatus(); err != nil { + return maskAny(fmt.Errorf("failed to update ArangoDeployment status: %v", err)) + } // Notify cluster of desired server count if ci := d.clusterScalingIntegration; ci != nil { diff --git a/pkg/deployment/images.go b/pkg/deployment/images.go index dede0d0d4..517c4cec5 100644 --- a/pkg/deployment/images.go +++ b/pkg/deployment/images.go @@ -83,9 +83,9 @@ func (d *Deployment) ensureImages(apiObject *api.ArangoDeployment) (bool, error) func (ib *imagesBuilder) Run(ctx context.Context) (bool, error) { result := false // Check ArangoDB image - if _, found := ib.Status.Images.GetByImage(ib.Spec.Image); !found { + if _, found := ib.Status.Images.GetByImage(ib.Spec.GetImage()); !found { // We need to find the image ID for the ArangoDB image - retrySoon, err := ib.fetchArangoDBImageIDAndVersion(ctx, ib.Spec.Image) + retrySoon, err := ib.fetchArangoDBImageIDAndVersion(ctx, ib.Spec.GetImage()) if err != nil { return retrySoon, maskAny(err) } @@ -168,7 +168,7 @@ func (ib *imagesBuilder) fetchArangoDBImageIDAndVersion(ctx context.Context, ima "--server.authentication=false", fmt.Sprintf("--server.endpoint=tcp://[::]:%d", k8sutil.ArangoPort), } - if err := k8sutil.CreateArangodPod(ib.KubeCli, true, ib.APIObject, role, id, podName, "", image, ib.Spec.ImagePullPolicy, args, nil, nil, nil, "", ""); err != nil { + if err := k8sutil.CreateArangodPod(ib.KubeCli, true, ib.APIObject, role, id, podName, "", image, ib.Spec.GetImagePullPolicy(), args, nil, nil, nil, "", ""); err != nil { log.Debug().Err(err).Msg("Failed to create image ID pod") return true, maskAny(err) } diff --git a/pkg/deployment/members.go b/pkg/deployment/members.go index d8140bbed..114af5330 100644 --- a/pkg/deployment/members.go +++ b/pkg/deployment/members.go @@ -40,7 +40,7 @@ func (d *Deployment) createInitialMembers(apiObject *api.ArangoDeployment) error // Go over all groups and create members if err := apiObject.ForeachServerGroup(func(group api.ServerGroup, spec api.ServerGroupSpec, status *api.MemberStatusList) error { - for len(*status) < spec.Count { + for len(*status) < spec.GetCount() { if err := d.createMember(group, apiObject); err != nil { return maskAny(err) } diff --git a/pkg/deployment/plan_builder.go b/pkg/deployment/plan_builder.go index 5c1bc9ac1..adceb7df2 100644 --- a/pkg/deployment/plan_builder.go +++ b/pkg/deployment/plan_builder.go @@ -90,18 +90,18 @@ func createPlan(log zerolog.Logger, apiObject metav1.Object, var plan api.Plan // Check for scale up/down - switch spec.Mode { + switch spec.GetMode() { case api.DeploymentModeSingle: // Never scale down case api.DeploymentModeResilientSingle: // Only scale singles - plan = append(plan, createScalePlan(log, status.Members.Single, api.ServerGroupSingle, spec.Single.Count)...) + plan = append(plan, createScalePlan(log, status.Members.Single, api.ServerGroupSingle, spec.Single.GetCount())...) case api.DeploymentModeCluster: // Scale dbservers, coordinators, syncmasters & syncworkers - plan = append(plan, createScalePlan(log, status.Members.DBServers, api.ServerGroupDBServers, spec.DBServers.Count)...) - plan = append(plan, createScalePlan(log, status.Members.Coordinators, api.ServerGroupCoordinators, spec.Coordinators.Count)...) - plan = append(plan, createScalePlan(log, status.Members.SyncMasters, api.ServerGroupSyncMasters, spec.SyncMasters.Count)...) - plan = append(plan, createScalePlan(log, status.Members.SyncWorkers, api.ServerGroupSyncWorkers, spec.SyncWorkers.Count)...) + plan = append(plan, createScalePlan(log, status.Members.DBServers, api.ServerGroupDBServers, spec.DBServers.GetCount())...) + plan = append(plan, createScalePlan(log, status.Members.Coordinators, api.ServerGroupCoordinators, spec.Coordinators.GetCount())...) + plan = append(plan, createScalePlan(log, status.Members.SyncMasters, api.ServerGroupSyncMasters, spec.SyncMasters.GetCount())...) + plan = append(plan, createScalePlan(log, status.Members.SyncWorkers, api.ServerGroupSyncWorkers, spec.SyncWorkers.GetCount())...) } // Check for the need to rotate one or more members @@ -150,7 +150,7 @@ func createPlan(log zerolog.Logger, apiObject metav1.Object, func podNeedsUpgrading(p v1.Pod, spec api.DeploymentSpec, images api.ImageInfoList) upgradeDecision { if len(p.Spec.Containers) == 1 { c := p.Spec.Containers[0] - specImageInfo, found := images.GetByImage(spec.Image) + specImageInfo, found := images.GetByImage(spec.GetImage()) if !found { return upgradeDecision{UpgradeNeeded: false} } @@ -199,7 +199,7 @@ func podNeedsRotation(p v1.Pod, apiObject metav1.Object, spec api.DeploymentSpec } // Check image pull policy c := p.Spec.Containers[0] - if c.ImagePullPolicy != spec.ImagePullPolicy { + if c.ImagePullPolicy != spec.GetImagePullPolicy() { return true, "Image pull policy changed" } // Check arguments diff --git a/pkg/deployment/plan_builder_test.go b/pkg/deployment/plan_builder_test.go index 07f796ee5..9810fffd2 100644 --- a/pkg/deployment/plan_builder_test.go +++ b/pkg/deployment/plan_builder_test.go @@ -31,13 +31,14 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha" + "github.com/arangodb/kube-arangodb/pkg/util" ) // TestCreatePlanSingleScale creates a `single` deployment to test the creating of scaling plan. func TestCreatePlanSingleScale(t *testing.T) { log := zerolog.Nop() spec := api.DeploymentSpec{ - Mode: api.DeploymentModeSingle, + Mode: api.NewMode(api.DeploymentModeSingle), } spec.SetDefaults("test") depl := &api.ArangoDeployment{ @@ -85,10 +86,10 @@ func TestCreatePlanSingleScale(t *testing.T) { func TestCreatePlanResilientSingleScale(t *testing.T) { log := zerolog.Nop() spec := api.DeploymentSpec{ - Mode: api.DeploymentModeResilientSingle, + Mode: api.NewMode(api.DeploymentModeResilientSingle), } spec.SetDefaults("test") - spec.Single.Count = 2 + spec.Single.Count = util.NewInt(2) depl := &api.ArangoDeployment{ ObjectMeta: metav1.ObjectMeta{ Name: "test_depl", @@ -150,7 +151,7 @@ func TestCreatePlanResilientSingleScale(t *testing.T) { func TestCreatePlanClusterScale(t *testing.T) { log := zerolog.Nop() spec := api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), } spec.SetDefaults("test") depl := &api.ArangoDeployment{ @@ -231,8 +232,8 @@ func TestCreatePlanClusterScale(t *testing.T) { PodName: "coordinator2", }, } - spec.DBServers.Count = 1 - spec.Coordinators.Count = 1 + spec.DBServers.Count = util.NewInt(1) + spec.Coordinators.Count = util.NewInt(1) newPlan, changed = createPlan(log, depl, nil, spec, status, nil) assert.True(t, changed) require.Len(t, newPlan, 5) // Note: Downscaling is done 1 at a time diff --git a/pkg/deployment/pod_creator.go b/pkg/deployment/pod_creator.go index 3f0e7811b..a60b25ca3 100644 --- a/pkg/deployment/pod_creator.go +++ b/pkg/deployment/pod_creator.go @@ -92,7 +92,7 @@ func createArangodArgs(apiObject metav1.Object, deplSpec api.DeploymentSpec, gro // Storage engine options = append(options, - optionPair{"--server.storage-engine", string(deplSpec.StorageEngine)}, + optionPair{"--server.storage-engine", string(deplSpec.GetStorageEngine())}, ) // Logging @@ -151,7 +151,7 @@ func createArangodArgs(apiObject metav1.Object, deplSpec api.DeploymentSpec, gro optionPair{"--agency.disaster-recovery-id", id}, optionPair{"--agency.activate", "true"}, optionPair{"--agency.my-address", myTCPURL}, - optionPair{"--agency.size", strconv.Itoa(deplSpec.Agents.Count)}, + optionPair{"--agency.size", strconv.Itoa(deplSpec.Agents.GetCount())}, optionPair{"--agency.supervision", "true"}, optionPair{"--foxx.queues", "false"}, optionPair{"--server.statistics", "false"}, @@ -185,7 +185,7 @@ func createArangodArgs(apiObject metav1.Object, deplSpec api.DeploymentSpec, gro optionPair{"--foxx.queues", "true"}, optionPair{"--server.statistics", "true"}, ) - if deplSpec.Mode == api.DeploymentModeResilientSingle { + if deplSpec.GetMode() == api.DeploymentModeResilientSingle { addAgentEndpoints = true options = append(options, optionPair{"--replication.automatic-failover", "true"}, @@ -246,7 +246,7 @@ func (d *Deployment) createLivenessProbe(apiObject *api.ArangoDeployment, group return nil, nil case api.ServerGroupSyncMasters, api.ServerGroupSyncWorkers: authorization := "" - if apiObject.Spec.Sync.Monitoring.TokenSecretName != "" { + if apiObject.Spec.Sync.Monitoring.GetTokenSecretName() != "" { // Use monitoring token token, err := d.getSyncMonitoringToken(apiObject) if err != nil { @@ -323,9 +323,9 @@ func (d *Deployment) ensurePods(apiObject *api.ArangoDeployment) error { // Create pod if group.IsArangod() { // Find image ID - info, found := apiObject.Status.Images.GetByImage(apiObject.Spec.Image) + info, found := apiObject.Status.Images.GetByImage(apiObject.Spec.GetImage()) if !found { - log.Debug().Str("image", apiObject.Spec.Image).Msg("Image ID is not known yet for image") + log.Debug().Str("image", apiObject.Spec.GetImage()).Msg("Image ID is not known yet for image") return nil } // Prepare arguments @@ -357,25 +357,25 @@ func (d *Deployment) ensurePods(apiObject *api.ArangoDeployment) error { } rocksdbEncryptionSecretName := "" if apiObject.Spec.RocksDB.IsEncrypted() { - rocksdbEncryptionSecretName = apiObject.Spec.RocksDB.Encryption.KeySecretName + rocksdbEncryptionSecretName = apiObject.Spec.RocksDB.Encryption.GetKeySecretName() if err := k8sutil.ValidateEncryptionKeySecret(kubecli.CoreV1(), rocksdbEncryptionSecretName, ns); err != nil { return maskAny(errors.Wrapf(err, "RocksDB encryption key secret validation failed")) } } if apiObject.Spec.IsAuthenticated() { env[constants.EnvArangodJWTSecret] = k8sutil.EnvValue{ - SecretName: apiObject.Spec.Authentication.JWTSecretName, + SecretName: apiObject.Spec.Authentication.GetJWTSecretName(), SecretKey: constants.SecretKeyJWT, } } - if err := k8sutil.CreateArangodPod(kubecli, apiObject.Spec.IsDevelopment(), apiObject, role, m.ID, m.PodName, m.PersistentVolumeClaimName, info.ImageID, apiObject.Spec.ImagePullPolicy, args, env, livenessProbe, readinessProbe, tlsKeyfileSecretName, rocksdbEncryptionSecretName); err != nil { + if err := k8sutil.CreateArangodPod(kubecli, apiObject.Spec.IsDevelopment(), apiObject, role, m.ID, m.PodName, m.PersistentVolumeClaimName, info.ImageID, apiObject.Spec.GetImagePullPolicy(), args, env, livenessProbe, readinessProbe, tlsKeyfileSecretName, rocksdbEncryptionSecretName); err != nil { return maskAny(err) } } else if group.IsArangosync() { // Find image ID - info, found := apiObject.Status.Images.GetByImage(apiObject.Spec.Sync.Image) + info, found := apiObject.Status.Images.GetByImage(apiObject.Spec.Sync.GetImage()) if !found { - log.Debug().Str("image", apiObject.Spec.Sync.Image).Msg("Image ID is not known yet for image") + log.Debug().Str("image", apiObject.Spec.Sync.GetImage()).Msg("Image ID is not known yet for image") return nil } // Prepare arguments @@ -389,7 +389,7 @@ func (d *Deployment) ensurePods(apiObject *api.ArangoDeployment) error { if group == api.ServerGroupSyncWorkers { affinityWithRole = api.ServerGroupDBServers.AsRole() } - if err := k8sutil.CreateArangoSyncPod(kubecli, apiObject.Spec.IsDevelopment(), apiObject, role, m.ID, m.PodName, info.ImageID, apiObject.Spec.Sync.ImagePullPolicy, args, env, livenessProbe, affinityWithRole); err != nil { + if err := k8sutil.CreateArangoSyncPod(kubecli, apiObject.Spec.IsDevelopment(), apiObject, role, m.ID, m.PodName, info.ImageID, apiObject.Spec.Sync.GetImagePullPolicy(), args, env, livenessProbe, affinityWithRole); err != nil { return maskAny(err) } } diff --git a/pkg/deployment/pod_creator_agent_args_test.go b/pkg/deployment/pod_creator_agent_args_test.go index 9303e59a2..c7b89240c 100644 --- a/pkg/deployment/pod_creator_agent_args_test.go +++ b/pkg/deployment/pod_creator_agent_args_test.go @@ -29,6 +29,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha" + "github.com/arangodb/kube-arangodb/pkg/util" ) // TestCreateArangodArgsAgent tests createArangodArgs for agent. @@ -41,7 +42,7 @@ func TestCreateArangodArgsAgent(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), }, } apiObject.Spec.SetDefaults("test") @@ -84,7 +85,7 @@ func TestCreateArangodArgsAgent(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), }, } apiObject.Spec.SetDefaults("test") @@ -128,9 +129,9 @@ func TestCreateArangodArgsAgent(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), TLS: api.TLSSpec{ - CASecretName: "None", + CASecretName: util.NewString("None"), }, }, } @@ -172,12 +173,12 @@ func TestCreateArangodArgsAgent(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), }, } apiObject.Spec.SetDefaults("test") - apiObject.Spec.Authentication.JWTSecretName = "None" - apiObject.Spec.StorageEngine = api.StorageEngineMMFiles + apiObject.Spec.Authentication.JWTSecretName = util.NewString("None") + apiObject.Spec.StorageEngine = api.NewStorageEngine(api.StorageEngineMMFiles) agents := api.MemberStatusList{ api.MemberStatus{ID: "a1"}, api.MemberStatus{ID: "a2"}, @@ -216,7 +217,7 @@ func TestCreateArangodArgsAgent(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), }, } apiObject.Spec.SetDefaults("test") diff --git a/pkg/deployment/pod_creator_coordinator_args_test.go b/pkg/deployment/pod_creator_coordinator_args_test.go index 79a71a6d6..becf97967 100644 --- a/pkg/deployment/pod_creator_coordinator_args_test.go +++ b/pkg/deployment/pod_creator_coordinator_args_test.go @@ -29,6 +29,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha" + "github.com/arangodb/kube-arangodb/pkg/util" ) // TestCreateArangodArgsCoordinator tests createArangodArgs for coordinator. @@ -41,7 +42,7 @@ func TestCreateArangodArgsCoordinator(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), }, } apiObject.Spec.SetDefaults("test") @@ -82,7 +83,7 @@ func TestCreateArangodArgsCoordinator(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), }, } apiObject.Spec.SetDefaults("test") @@ -124,9 +125,9 @@ func TestCreateArangodArgsCoordinator(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), TLS: api.TLSSpec{ - CASecretName: "None", + CASecretName: util.NewString("None"), }, }, } @@ -166,11 +167,11 @@ func TestCreateArangodArgsCoordinator(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), }, } apiObject.Spec.SetDefaults("test") - apiObject.Spec.Authentication.JWTSecretName = "None" + apiObject.Spec.Authentication.JWTSecretName = util.NewString("None") agents := api.MemberStatusList{ api.MemberStatus{ID: "a1"}, api.MemberStatus{ID: "a2"}, @@ -207,12 +208,12 @@ func TestCreateArangodArgsCoordinator(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), }, } apiObject.Spec.SetDefaults("test") apiObject.Spec.Coordinators.Args = []string{"--foo1", "--foo2"} - apiObject.Spec.StorageEngine = api.StorageEngineMMFiles + apiObject.Spec.StorageEngine = api.NewStorageEngine(api.StorageEngineMMFiles) agents := api.MemberStatusList{ api.MemberStatus{ID: "a1"}, api.MemberStatus{ID: "a2"}, diff --git a/pkg/deployment/pod_creator_dbserver_args_test.go b/pkg/deployment/pod_creator_dbserver_args_test.go index e91bfe8d7..e211f2792 100644 --- a/pkg/deployment/pod_creator_dbserver_args_test.go +++ b/pkg/deployment/pod_creator_dbserver_args_test.go @@ -29,6 +29,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha" + "github.com/arangodb/kube-arangodb/pkg/util" ) // TestCreateArangodArgsDBServer tests createArangodArgs for dbserver. @@ -41,7 +42,7 @@ func TestCreateArangodArgsDBServer(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), }, } apiObject.Spec.SetDefaults("test") @@ -82,7 +83,7 @@ func TestCreateArangodArgsDBServer(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), }, } apiObject.Spec.SetDefaults("test") @@ -124,9 +125,9 @@ func TestCreateArangodArgsDBServer(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), TLS: api.TLSSpec{ - CASecretName: "None", + CASecretName: util.NewString("None"), }, }, } @@ -166,11 +167,11 @@ func TestCreateArangodArgsDBServer(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), }, } apiObject.Spec.SetDefaults("test") - apiObject.Spec.Authentication.JWTSecretName = "None" + apiObject.Spec.Authentication.JWTSecretName = util.NewString("None") agents := api.MemberStatusList{ api.MemberStatus{ID: "a1"}, api.MemberStatus{ID: "a2"}, @@ -207,11 +208,11 @@ func TestCreateArangodArgsDBServer(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), }, } apiObject.Spec.SetDefaults("test") - apiObject.Spec.StorageEngine = api.StorageEngineMMFiles + apiObject.Spec.StorageEngine = api.NewStorageEngine(api.StorageEngineMMFiles) apiObject.Spec.DBServers.Args = []string{"--foo1", "--foo2"} agents := api.MemberStatusList{ api.MemberStatus{ID: "a1"}, diff --git a/pkg/deployment/pod_creator_single_args_test.go b/pkg/deployment/pod_creator_single_args_test.go index cec80cfb2..b8166b0fa 100644 --- a/pkg/deployment/pod_creator_single_args_test.go +++ b/pkg/deployment/pod_creator_single_args_test.go @@ -28,6 +28,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/stretchr/testify/assert" ) @@ -37,7 +38,7 @@ func TestCreateArangodArgsSingle(t *testing.T) { { apiObject := &api.ArangoDeployment{ Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeSingle, + Mode: api.NewMode(api.DeploymentModeSingle), }, } apiObject.Spec.SetDefaults("test") @@ -64,7 +65,7 @@ func TestCreateArangodArgsSingle(t *testing.T) { { apiObject := &api.ArangoDeployment{ Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeSingle, + Mode: api.NewMode(api.DeploymentModeSingle), }, } apiObject.Spec.SetDefaults("test") @@ -92,9 +93,9 @@ func TestCreateArangodArgsSingle(t *testing.T) { { apiObject := &api.ArangoDeployment{ Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeSingle, + Mode: api.NewMode(api.DeploymentModeSingle), TLS: api.TLSSpec{ - CASecretName: "None", + CASecretName: util.NewString("None"), }, }, } @@ -120,8 +121,8 @@ func TestCreateArangodArgsSingle(t *testing.T) { { apiObject := &api.ArangoDeployment{ Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeSingle, - StorageEngine: api.StorageEngineMMFiles, + Mode: api.NewMode(api.DeploymentModeSingle), + StorageEngine: api.NewStorageEngine(api.StorageEngineMMFiles), }, } apiObject.Spec.SetDefaults("test") @@ -148,10 +149,10 @@ func TestCreateArangodArgsSingle(t *testing.T) { { apiObject := &api.ArangoDeployment{ Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeSingle, + Mode: api.NewMode(api.DeploymentModeSingle), }, } - apiObject.Spec.Authentication.JWTSecretName = "None" + apiObject.Spec.Authentication.JWTSecretName = util.NewString("None") apiObject.Spec.SetDefaults("test") cmdline := createArangodArgs(apiObject, apiObject.Spec, api.ServerGroupSingle, nil, "id1", false) assert.Equal(t, @@ -175,7 +176,7 @@ func TestCreateArangodArgsSingle(t *testing.T) { { apiObject := &api.ArangoDeployment{ Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeSingle, + Mode: api.NewMode(api.DeploymentModeSingle), }, } apiObject.Spec.Single.Args = []string{"--foo1", "--foo2"} @@ -209,7 +210,7 @@ func TestCreateArangodArgsSingle(t *testing.T) { Namespace: "ns", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeResilientSingle, + Mode: api.NewMode(api.DeploymentModeResilientSingle), }, } apiObject.Spec.SetDefaults("test") diff --git a/pkg/deployment/pvcs.go b/pkg/deployment/pvcs.go index a398992ed..b901a0aef 100644 --- a/pkg/deployment/pvcs.go +++ b/pkg/deployment/pvcs.go @@ -38,7 +38,7 @@ func (d *Deployment) ensurePVCs(apiObject *api.ArangoDeployment) error { if err := apiObject.ForeachServerGroup(func(group api.ServerGroup, spec api.ServerGroupSpec, status *api.MemberStatusList) error { for _, m := range *status { if m.PersistentVolumeClaimName != "" { - storageClassName := spec.StorageClassName + storageClassName := spec.GetStorageClassName() role := group.AsRole() resources := spec.Resources if err := k8sutil.CreatePersistentVolumeClaim(kubecli, m.PersistentVolumeClaimName, deploymentName, ns, storageClassName, role, resources, owner); err != nil { diff --git a/pkg/deployment/secrets.go b/pkg/deployment/secrets.go index 40566ded0..83407571c 100644 --- a/pkg/deployment/secrets.go +++ b/pkg/deployment/secrets.go @@ -36,7 +36,7 @@ import ( // createSecrets creates all secrets needed to run the given deployment func (d *Deployment) createSecrets(apiObject *api.ArangoDeployment) error { if apiObject.Spec.IsAuthenticated() { - if err := d.ensureJWTSecret(apiObject.Spec.Authentication.JWTSecretName); err != nil { + if err := d.ensureJWTSecret(apiObject.Spec.Authentication.GetJWTSecretName()); err != nil { return maskAny(err) } } @@ -45,7 +45,7 @@ func (d *Deployment) createSecrets(apiObject *api.ArangoDeployment) error { return maskAny(err) } } - if apiObject.Spec.Sync.Enabled { + if apiObject.Spec.Sync.IsEnabled() { if err := d.ensureCACertificateSecret(apiObject.Spec.Sync.TLS); err != nil { return maskAny(err) } @@ -88,7 +88,7 @@ func (d *Deployment) ensureJWTSecret(secretName string) error { func (d *Deployment) ensureCACertificateSecret(spec api.TLSSpec) error { kubecli := d.deps.KubeCli ns := d.apiObject.GetNamespace() - if _, err := kubecli.CoreV1().Secrets(ns).Get(spec.CASecretName, metav1.GetOptions{}); k8sutil.IsNotFound(err) { + if _, err := kubecli.CoreV1().Secrets(ns).Get(spec.GetCASecretName(), metav1.GetOptions{}); k8sutil.IsNotFound(err) { // Secret not found, create it owner := d.apiObject.AsOwner() deploymentName := d.apiObject.GetName() @@ -112,7 +112,7 @@ func (d *Deployment) getJWTSecret(apiObject *api.ArangoDeployment) (string, erro return "", nil } kubecli := d.deps.KubeCli - secretName := apiObject.Spec.Authentication.JWTSecretName + secretName := apiObject.Spec.Authentication.GetJWTSecretName() s, err := k8sutil.GetJWTSecret(kubecli.CoreV1(), secretName, apiObject.GetNamespace()) if err != nil { d.deps.Log.Debug().Err(err).Str("secret-name", secretName).Msg("Failed to get JWT secret") @@ -124,7 +124,7 @@ func (d *Deployment) getJWTSecret(apiObject *api.ArangoDeployment) (string, erro // getSyncJWTSecret loads the JWT secret used for syncmasters from a Secret configured in apiObject.Spec.Sync.Authentication.JWTSecretName. func (d *Deployment) getSyncJWTSecret(apiObject *api.ArangoDeployment) (string, error) { kubecli := d.deps.KubeCli - secretName := apiObject.Spec.Sync.Authentication.JWTSecretName + secretName := apiObject.Spec.Sync.Authentication.GetJWTSecretName() s, err := k8sutil.GetJWTSecret(kubecli.CoreV1(), secretName, apiObject.GetNamespace()) if err != nil { d.deps.Log.Debug().Err(err).Str("secret-name", secretName).Msg("Failed to get sync JWT secret") @@ -136,7 +136,7 @@ func (d *Deployment) getSyncJWTSecret(apiObject *api.ArangoDeployment) (string, // getSyncMonitoringToken loads the token secret used for monitoring sync masters & workers. func (d *Deployment) getSyncMonitoringToken(apiObject *api.ArangoDeployment) (string, error) { kubecli := d.deps.KubeCli - secretName := apiObject.Spec.Sync.Monitoring.TokenSecretName + secretName := apiObject.Spec.Sync.Monitoring.GetTokenSecretName() s, err := kubecli.CoreV1().Secrets(apiObject.GetNamespace()).Get(secretName, metav1.GetOptions{}) if err != nil { d.deps.Log.Debug().Err(err).Str("secret-name", secretName).Msg("Failed to get monitoring token secret") diff --git a/pkg/deployment/services.go b/pkg/deployment/services.go index 7ea58c87a..6063dcb6d 100644 --- a/pkg/deployment/services.go +++ b/pkg/deployment/services.go @@ -39,7 +39,7 @@ func (d *Deployment) createServices(apiObject *api.ArangoDeployment) error { log.Debug().Err(err).Msg("Failed to create headless service") return maskAny(err) } - single := apiObject.Spec.Mode.HasSingleServers() + single := apiObject.Spec.GetMode().HasSingleServers() if svcName, err := k8sutil.CreateDatabaseClientService(kubecli, apiObject, single, owner); err != nil { log.Debug().Err(err).Msg("Failed to create database client service") return maskAny(err) @@ -49,7 +49,7 @@ func (d *Deployment) createServices(apiObject *api.ArangoDeployment) error { return maskAny(err) } } - if apiObject.Spec.Sync.Enabled { + if apiObject.Spec.Sync.IsEnabled() { if svcName, err := k8sutil.CreateSyncMasterClientService(kubecli, apiObject, owner); err != nil { log.Debug().Err(err).Msg("Failed to create syncmaster client service") return maskAny(err) diff --git a/pkg/deployment/tls.go b/pkg/deployment/tls.go index 24da23789..837b890e1 100644 --- a/pkg/deployment/tls.go +++ b/pkg/deployment/tls.go @@ -44,8 +44,8 @@ const ( // createCACertificate creates a CA certificate and stores it in a secret with name // specified in the given spec. func createCACertificate(log zerolog.Logger, cli v1.CoreV1Interface, spec api.TLSSpec, deploymentName, namespace string, ownerRef *metav1.OwnerReference) error { - log = log.With().Str("secret", spec.CASecretName).Logger() - dnsNames, ipAddresses, emailAddress, err := spec.GetAltNames() + log = log.With().Str("secret", spec.GetCASecretName()).Logger() + dnsNames, ipAddresses, emailAddress, err := spec.GetParsedAltNames() if err != nil { log.Debug().Err(err).Msg("Failed to get alternate names") return maskAny(err) @@ -65,7 +65,7 @@ func createCACertificate(log zerolog.Logger, cli v1.CoreV1Interface, spec api.TL log.Debug().Err(err).Msg("Failed to create CA certificate") return maskAny(err) } - if err := k8sutil.CreateCASecret(cli, spec.CASecretName, namespace, cert, priv, ownerRef); err != nil { + if err := k8sutil.CreateCASecret(cli, spec.GetCASecretName(), namespace, cert, priv, ownerRef); err != nil { if k8sutil.IsAlreadyExists(err) { log.Debug().Msg("CA Secret already exists") } else { @@ -82,14 +82,14 @@ func createCACertificate(log zerolog.Logger, cli v1.CoreV1Interface, spec api.TL func createServerCertificate(log zerolog.Logger, cli v1.CoreV1Interface, serverNames []string, spec api.TLSSpec, secretName, namespace string, ownerRef *metav1.OwnerReference) error { log = log.With().Str("secret", secretName).Logger() // Load alt names - dnsNames, ipAddresses, emailAddress, err := spec.GetAltNames() + dnsNames, ipAddresses, emailAddress, err := spec.GetParsedAltNames() if err != nil { log.Debug().Err(err).Msg("Failed to get alternate names") return maskAny(err) } // Load CA certificate - caCert, caKey, err := k8sutil.GetCASecret(cli, spec.CASecretName, namespace) + caCert, caKey, err := k8sutil.GetCASecret(cli, spec.GetCASecretName(), namespace) if err != nil { log.Debug().Err(err).Msg("Failed to load CA certificate") return maskAny(err) @@ -105,7 +105,7 @@ func createServerCertificate(log zerolog.Logger, cli v1.CoreV1Interface, serverN Hosts: append(append(serverNames, dnsNames...), ipAddresses...), EmailAddresses: emailAddress, ValidFrom: time.Now(), - ValidFor: spec.TTL, + ValidFor: spec.GetTTL(), IsCA: false, ECDSACurve: tlsECDSACurve, } diff --git a/pkg/util/arangod/client.go b/pkg/util/arangod/client.go index 706165cb6..5645d327d 100644 --- a/pkg/util/arangod/client.go +++ b/pkg/util/arangod/client.go @@ -147,7 +147,7 @@ func createArangodClientForDNSName(ctx context.Context, cli corev1.CoreV1Interfa // Authentication is enabled. // Should we skip using it? if ctx.Value(skipAuthenticationKey{}) == nil { - s, err := k8sutil.GetJWTSecret(cli, apiObject.Spec.Authentication.JWTSecretName, apiObject.GetNamespace()) + s, err := k8sutil.GetJWTSecret(cli, apiObject.Spec.Authentication.GetJWTSecretName(), apiObject.GetNamespace()) if err != nil { return nil, maskAny(err) } diff --git a/pkg/util/k8sutil/test/events_test.go b/pkg/util/k8sutil/test/events_test.go index 813161013..fe983bd6e 100644 --- a/pkg/util/k8sutil/test/events_test.go +++ b/pkg/util/k8sutil/test/events_test.go @@ -41,7 +41,7 @@ var apiObjectForTest = api.ArangoDeployment{ Namespace: "Wonka", }, Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, + Mode: api.NewMode(api.DeploymentModeCluster), }, } diff --git a/pkg/util/refs.go b/pkg/util/refs.go new file mode 100644 index 000000000..e0ab792fa --- /dev/null +++ b/pkg/util/refs.go @@ -0,0 +1,149 @@ +// +// DISCLAIMER +// +// Copyright 2018 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// +// Author Ewout Prangsma +// + +package util + +import ( + "time" + + "k8s.io/api/core/v1" +) + +// NewString returns a reference to a string with given value. +func NewString(input string) *string { + return &input +} + +// NewStringOrNil returns nil if input is nil, otherwise returns a clone of the given value. +func NewStringOrNil(input *string) *string { + if input == nil { + return nil + } + return NewString(*input) +} + +// StringOrDefault returns the default value (or empty string) if input is nil, otherwise returns the referenced value. +func StringOrDefault(input *string, defaultValue ...string) string { + if input == nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return "" + } + return *input +} + +// NewInt returns a reference to an int with given value. +func NewInt(input int) *int { + return &input +} + +// NewIntOrNil returns nil if input is nil, otherwise returns a clone of the given value. +func NewIntOrNil(input *int) *int { + if input == nil { + return nil + } + return NewInt(*input) +} + +// IntOrDefault returns the default value (or 0) if input is nil, otherwise returns the referenced value. +func IntOrDefault(input *int, defaultValue ...int) int { + if input == nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return 0 + } + return *input +} + +// NewBool returns a reference to a bool with given value. +func NewBool(input bool) *bool { + return &input +} + +// NewBoolOrNil returns nil if input is nil, otherwise returns a clone of the given value. +func NewBoolOrNil(input *bool) *bool { + if input == nil { + return nil + } + return NewBool(*input) +} + +// BoolOrDefault returns the default value (or false) if input is nil, otherwise returns the referenced value. +func BoolOrDefault(input *bool, defaultValue ...bool) bool { + if input == nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return false + } + return *input +} + +// NewDuration returns a reference to a duration with given value. +func NewDuration(input time.Duration) *time.Duration { + return &input +} + +// NewDurationOrNil returns nil if input is nil, otherwise returns a clone of the given value. +func NewDurationOrNil(input *time.Duration) *time.Duration { + if input == nil { + return nil + } + return NewDuration(*input) +} + +// DurationOrDefault returns the default value (or 0) if input is nil, otherwise returns the referenced value. +func DurationOrDefault(input *time.Duration, defaultValue ...time.Duration) time.Duration { + if input == nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return 0 + } + return *input +} + +// NewPullPolicy returns a reference to a pull policy with given value. +func NewPullPolicy(input v1.PullPolicy) *v1.PullPolicy { + return &input +} + +// NewPullPolicyOrNil returns nil if input is nil, otherwise returns a clone of the given value. +func NewPullPolicyOrNil(input *v1.PullPolicy) *v1.PullPolicy { + if input == nil { + return nil + } + return NewPullPolicy(*input) +} + +// PullPolicyOrDefault returns the default value (or 0) if input is nil, otherwise returns the referenced value. +func PullPolicyOrDefault(input *v1.PullPolicy, defaultValue ...v1.PullPolicy) v1.PullPolicy { + if input == nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return "" + } + return *input +} diff --git a/tests/auth_test.go b/tests/auth_test.go index ac201810f..63bc9e16f 100644 --- a/tests/auth_test.go +++ b/tests/auth_test.go @@ -10,6 +10,7 @@ import ( api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha" "github.com/arangodb/kube-arangodb/pkg/client" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util/arangod" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" ) @@ -24,7 +25,7 @@ func TestAuthenticationSingleDefaultSecret(t *testing.T) { // Prepare deployment config depl := newDeployment("test-auth-sng-def-" + uniuri.NewLen(4)) - depl.Spec.Mode = api.DeploymentModeSingle + depl.Spec.Mode = api.NewMode(api.DeploymentModeSingle) depl.Spec.SetDefaults(depl.GetName()) // Create deployment @@ -39,7 +40,7 @@ func TestAuthenticationSingleDefaultSecret(t *testing.T) { } // Secret must now exist - if _, err := waitUntilSecret(kubecli, depl.Spec.Authentication.JWTSecretName, ns, nil, time.Second); err != nil { + if _, err := waitUntilSecret(kubecli, depl.Spec.Authentication.GetJWTSecretName(), ns, nil, time.Second); err != nil { t.Fatalf("JWT secret '%s' not found: %v", depl.Spec.Authentication.JWTSecretName, err) } @@ -56,7 +57,7 @@ func TestAuthenticationSingleDefaultSecret(t *testing.T) { removeDeployment(c, depl.GetName(), ns) // Secret must no longer exist - if err := waitUntilSecretNotFound(kubecli, depl.Spec.Authentication.JWTSecretName, ns, time.Minute); err != nil { + if err := waitUntilSecretNotFound(kubecli, depl.Spec.Authentication.GetJWTSecretName(), ns, time.Minute); err != nil { t.Fatalf("JWT secret '%s' still found: %v", depl.Spec.Authentication.JWTSecretName, err) } } @@ -71,12 +72,12 @@ func TestAuthenticationSingleCustomSecret(t *testing.T) { // Prepare deployment config depl := newDeployment("test-auth-sng-cst-" + uniuri.NewLen(4)) - depl.Spec.Mode = api.DeploymentModeSingle - depl.Spec.Authentication.JWTSecretName = strings.ToLower(uniuri.New()) + depl.Spec.Mode = api.NewMode(api.DeploymentModeSingle) + depl.Spec.Authentication.JWTSecretName = util.NewString(strings.ToLower(uniuri.New())) depl.Spec.SetDefaults(depl.GetName()) // Create secret - if err := k8sutil.CreateJWTSecret(kubecli.CoreV1(), depl.Spec.Authentication.JWTSecretName, ns, "foo", nil); err != nil { + if err := k8sutil.CreateJWTSecret(kubecli.CoreV1(), depl.Spec.Authentication.GetJWTSecretName(), ns, "foo", nil); err != nil { t.Fatalf("Create JWT secret failed: %v", err) } @@ -104,12 +105,12 @@ func TestAuthenticationSingleCustomSecret(t *testing.T) { removeDeployment(c, depl.GetName(), ns) // Secret must still exist - if _, err := waitUntilSecret(kubecli, depl.Spec.Authentication.JWTSecretName, ns, nil, time.Second); err != nil { + if _, err := waitUntilSecret(kubecli, depl.Spec.Authentication.GetJWTSecretName(), ns, nil, time.Second); err != nil { t.Fatalf("JWT secret '%s' not found: %v", depl.Spec.Authentication.JWTSecretName, err) } // Cleanup secret - removeSecret(kubecli, depl.Spec.Authentication.JWTSecretName, ns) + removeSecret(kubecli, depl.Spec.Authentication.GetJWTSecretName(), ns) } // TestAuthenticationNoneSingle creating a single server @@ -122,8 +123,8 @@ func TestAuthenticationNoneSingle(t *testing.T) { // Prepare deployment config depl := newDeployment("test-auth-none-sng-" + uniuri.NewLen(4)) - depl.Spec.Mode = api.DeploymentModeSingle - depl.Spec.Authentication.JWTSecretName = api.JWTSecretNameDisabled + depl.Spec.Mode = api.NewMode(api.DeploymentModeSingle) + depl.Spec.Authentication.JWTSecretName = util.NewString(api.JWTSecretNameDisabled) depl.Spec.SetDefaults(depl.GetName()) // Create deployment @@ -160,7 +161,7 @@ func TestAuthenticationClusterDefaultSecret(t *testing.T) { // Prepare deployment config depl := newDeployment("test-auth-cls-def-" + uniuri.NewLen(4)) - depl.Spec.Mode = api.DeploymentModeCluster + depl.Spec.Mode = api.NewMode(api.DeploymentModeCluster) depl.Spec.SetDefaults(depl.GetName()) // Create deployment @@ -175,7 +176,7 @@ func TestAuthenticationClusterDefaultSecret(t *testing.T) { } // Secret must now exist - if _, err := waitUntilSecret(kubecli, depl.Spec.Authentication.JWTSecretName, ns, nil, time.Second); err != nil { + if _, err := waitUntilSecret(kubecli, depl.Spec.Authentication.GetJWTSecretName(), ns, nil, time.Second); err != nil { t.Fatalf("JWT secret '%s' not found: %v", depl.Spec.Authentication.JWTSecretName, err) } @@ -192,7 +193,7 @@ func TestAuthenticationClusterDefaultSecret(t *testing.T) { removeDeployment(c, depl.GetName(), ns) // Secret must no longer exist - if err := waitUntilSecretNotFound(kubecli, depl.Spec.Authentication.JWTSecretName, ns, time.Minute); err != nil { + if err := waitUntilSecretNotFound(kubecli, depl.Spec.Authentication.GetJWTSecretName(), ns, time.Minute); err != nil { t.Fatalf("JWT secret '%s' still found: %v", depl.Spec.Authentication.JWTSecretName, err) } } @@ -207,12 +208,12 @@ func TestAuthenticationClusterCustomSecret(t *testing.T) { // Prepare deployment config depl := newDeployment("test-auth-cls-cst-" + uniuri.NewLen(4)) - depl.Spec.Mode = api.DeploymentModeCluster - depl.Spec.Authentication.JWTSecretName = strings.ToLower(uniuri.New()) + depl.Spec.Mode = api.NewMode(api.DeploymentModeCluster) + depl.Spec.Authentication.JWTSecretName = util.NewString(strings.ToLower(uniuri.New())) depl.Spec.SetDefaults(depl.GetName()) // Create secret - if err := k8sutil.CreateJWTSecret(kubecli.CoreV1(), depl.Spec.Authentication.JWTSecretName, ns, "foo", nil); err != nil { + if err := k8sutil.CreateJWTSecret(kubecli.CoreV1(), depl.Spec.Authentication.GetJWTSecretName(), ns, "foo", nil); err != nil { t.Fatalf("Create JWT secret failed: %v", err) } @@ -240,12 +241,12 @@ func TestAuthenticationClusterCustomSecret(t *testing.T) { removeDeployment(c, depl.GetName(), ns) // Secret must still exist - if _, err := waitUntilSecret(kubecli, depl.Spec.Authentication.JWTSecretName, ns, nil, time.Second); err != nil { + if _, err := waitUntilSecret(kubecli, depl.Spec.Authentication.GetJWTSecretName(), ns, nil, time.Second); err != nil { t.Fatalf("JWT secret '%s' not found: %v", depl.Spec.Authentication.JWTSecretName, err) } // Cleanup secret - removeSecret(kubecli, depl.Spec.Authentication.JWTSecretName, ns) + removeSecret(kubecli, depl.Spec.Authentication.GetJWTSecretName(), ns) } // TestAuthenticationNoneCluster creating a cluster @@ -258,8 +259,8 @@ func TestAuthenticationNoneCluster(t *testing.T) { // Prepare deployment config depl := newDeployment("test-auth-none-cls-" + uniuri.NewLen(4)) - depl.Spec.Mode = api.DeploymentModeCluster - depl.Spec.Authentication.JWTSecretName = api.JWTSecretNameDisabled + depl.Spec.Mode = api.NewMode(api.DeploymentModeCluster) + depl.Spec.Authentication.JWTSecretName = util.NewString(api.JWTSecretNameDisabled) depl.Spec.SetDefaults(depl.GetName()) // Create deployment diff --git a/tests/rocksdb_encryption_test.go b/tests/rocksdb_encryption_test.go index 50c40d2e6..425aa7952 100644 --- a/tests/rocksdb_encryption_test.go +++ b/tests/rocksdb_encryption_test.go @@ -10,6 +10,7 @@ import ( api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha" "github.com/arangodb/kube-arangodb/pkg/client" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" ) @@ -24,15 +25,15 @@ func TestRocksDBEncryptionSingle(t *testing.T) { // Prepare deployment config depl := newDeployment("test-rocksdb-enc-sng-" + uniuri.NewLen(4)) - depl.Spec.Mode = api.DeploymentModeSingle - depl.Spec.Image = image - depl.Spec.StorageEngine = api.StorageEngineRocksDB - depl.Spec.RocksDB.Encryption.KeySecretName = strings.ToLower(uniuri.New()) + depl.Spec.Mode = api.NewMode(api.DeploymentModeSingle) + depl.Spec.Image = util.NewString(image) + depl.Spec.StorageEngine = api.NewStorageEngine(api.StorageEngineRocksDB) + depl.Spec.RocksDB.Encryption.KeySecretName = util.NewString(strings.ToLower(uniuri.New())) // Create encryption key secret key := make([]byte, 32) rand.Read(key) - if err := k8sutil.CreateEncryptionKeySecret(kubecli.CoreV1(), depl.Spec.RocksDB.Encryption.KeySecretName, ns, key); err != nil { + if err := k8sutil.CreateEncryptionKeySecret(kubecli.CoreV1(), depl.Spec.RocksDB.Encryption.GetKeySecretName(), ns, key); err != nil { t.Fatalf("Create encryption key secret failed: %v", err) } @@ -59,5 +60,5 @@ func TestRocksDBEncryptionSingle(t *testing.T) { // Cleanup removeDeployment(c, depl.GetName(), ns) - removeSecret(kubecli, depl.Spec.RocksDB.Encryption.KeySecretName, ns) + removeSecret(kubecli, depl.Spec.RocksDB.Encryption.GetKeySecretName(), ns) } diff --git a/tests/scale_test.go b/tests/scale_test.go index f96178188..99c278305 100644 --- a/tests/scale_test.go +++ b/tests/scale_test.go @@ -3,12 +3,14 @@ package tests import ( "context" "testing" + "time" "github.com/dchest/uniuri" driver "github.com/arangodb/go-driver" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha" "github.com/arangodb/kube-arangodb/pkg/client" + "github.com/arangodb/kube-arangodb/pkg/util" ) // TestScaleCluster tests scaling up/down the number of DBServers & coordinators @@ -21,8 +23,8 @@ func TestScaleClusterNonTLS(t *testing.T) { // Prepare deployment config depl := newDeployment("test-scale-non-tls" + uniuri.NewLen(4)) - depl.Spec.Mode = api.DeploymentModeCluster - depl.Spec.TLS = api.TLSSpec{"None", nil, 50} + depl.Spec.Mode = api.NewMode(api.DeploymentModeCluster) + depl.Spec.TLS = api.TLSSpec{util.NewString("None"), nil, util.NewDuration(time.Second * 50)} depl.Spec.SetDefaults(depl.GetName()) // this must be last // Create deployment @@ -49,8 +51,8 @@ func TestScaleClusterNonTLS(t *testing.T) { // Add 2 DBServers, 1 coordinator updated, err := updateDeployment(c, depl.GetName(), ns, func(spec *api.DeploymentSpec) { - spec.DBServers.Count = 5 - spec.Coordinators.Count = 4 + spec.DBServers.Count = util.NewInt(5) + spec.Coordinators.Count = util.NewInt(4) }) if err != nil { t.Fatalf("Failed to update deployment: %v", err) @@ -65,8 +67,8 @@ func TestScaleClusterNonTLS(t *testing.T) { // Remove 3 DBServers, 2 coordinator updated, err = updateDeployment(c, depl.GetName(), ns, func(spec *api.DeploymentSpec) { - spec.DBServers.Count = 3 - spec.Coordinators.Count = 2 + spec.DBServers.Count = util.NewInt(3) + spec.Coordinators.Count = util.NewInt(2) }) if err != nil { t.Fatalf("Failed to update deployment: %v", err) @@ -91,7 +93,7 @@ func TestScaleCluster(t *testing.T) { // Prepare deployment config depl := newDeployment("test-scale" + uniuri.NewLen(4)) - depl.Spec.Mode = api.DeploymentModeCluster + depl.Spec.Mode = api.NewMode(api.DeploymentModeCluster) depl.Spec.TLS = api.TLSSpec{} // should auto-generate cert depl.Spec.SetDefaults(depl.GetName()) // this must be last @@ -119,8 +121,8 @@ func TestScaleCluster(t *testing.T) { // Add 2 DBServers, 1 coordinator updated, err := updateDeployment(c, depl.GetName(), ns, func(spec *api.DeploymentSpec) { - spec.DBServers.Count = 5 - spec.Coordinators.Count = 4 + spec.DBServers.Count = util.NewInt(5) + spec.Coordinators.Count = util.NewInt(4) }) if err != nil { t.Fatalf("Failed to update deployment: %v", err) @@ -135,8 +137,8 @@ func TestScaleCluster(t *testing.T) { // Remove 3 DBServers, 2 coordinator updated, err = updateDeployment(c, depl.GetName(), ns, func(spec *api.DeploymentSpec) { - spec.DBServers.Count = 3 - spec.Coordinators.Count = 2 + spec.DBServers.Count = util.NewInt(3) + spec.Coordinators.Count = util.NewInt(2) }) if err != nil { t.Fatalf("Failed to update deployment: %v", err) diff --git a/tests/simple_test.go b/tests/simple_test.go index 99f68004c..da378e2a9 100644 --- a/tests/simple_test.go +++ b/tests/simple_test.go @@ -21,7 +21,7 @@ func TestSimpleSingle(t *testing.T) { // Prepare deployment config depl := newDeployment("test-sng-" + uniuri.NewLen(4)) - depl.Spec.Mode = api.DeploymentModeSingle + depl.Spec.Mode = api.NewMode(api.DeploymentModeSingle) // Create deployment _, err := c.DatabaseV1alpha().ArangoDeployments(ns).Create(depl) @@ -62,7 +62,7 @@ func TestSimpleResilientSingle(t *testing.T) { // Prepare deployment config depl := newDeployment("test-rs-" + uniuri.NewLen(4)) - depl.Spec.Mode = api.DeploymentModeResilientSingle + depl.Spec.Mode = api.NewMode(api.DeploymentModeResilientSingle) // Create deployment _, err := c.DatabaseV1alpha().ArangoDeployments(ns).Create(depl) @@ -103,7 +103,7 @@ func TestSimpleCluster(t *testing.T) { // Prepare deployment config depl := newDeployment("test-cls-" + uniuri.NewLen(4)) - depl.Spec.Mode = api.DeploymentModeCluster + depl.Spec.Mode = api.NewMode(api.DeploymentModeCluster) // Create deployment _, err := c.DatabaseV1alpha().ArangoDeployments(ns).Create(depl) diff --git a/tests/test_util.go b/tests/test_util.go index 9f78e276b..eaac48f02 100644 --- a/tests/test_util.go +++ b/tests/test_util.go @@ -239,13 +239,13 @@ func clusterHealthEqualsSpec(h driver.ClusterHealth, spec api.DeploymentSpec) er } } } - if spec.Agents.Count == agents && - spec.DBServers.Count == goodDBServers && - spec.Coordinators.Count == goodCoordinators { + if spec.Agents.GetCount() == agents && + spec.DBServers.GetCount() == goodDBServers && + spec.Coordinators.GetCount() == goodCoordinators { return nil } return fmt.Errorf("Expected %d,%d,%d got %d,%d,%d", - spec.Agents.Count, spec.DBServers.Count, spec.Coordinators.Count, + spec.Agents.GetCount(), spec.DBServers.GetCount(), spec.Coordinators.GetCount(), agents, goodDBServers, goodCoordinators, ) }