mirror of
https://github.com/arangodb/kube-arangodb.git
synced 2024-12-14 11:57:37 +00:00
Using refs in the spec as much as possible to detect unspecified fields
This commit is contained in:
parent
f49f621fc0
commit
1563e9a66a
23 changed files with 444 additions and 145 deletions
|
@ -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,18 @@ const (
|
|||
JWTSecretNameDisabled = "None"
|
||||
)
|
||||
|
||||
// GetJWTSecretName returns the value of jwtSecretName.
|
||||
func (s AuthenticationSpec) GetJWTSecretName() string {
|
||||
if s.JWTSecretName == nil {
|
||||
return ""
|
||||
}
|
||||
return *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 +59,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 +68,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.String(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.String(source.GetJWTSecretName())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,7 +89,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.StringOrNil(s.JWTSecretName)
|
||||
resetFields = append(resetFields, fieldPrefix+".jwtSecretName")
|
||||
}
|
||||
return resetFields
|
||||
|
|
|
@ -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.String("None")}.Validate(false))
|
||||
assert.Nil(t, AuthenticationSpec{JWTSecretName: util.String("foo")}.Validate(false))
|
||||
assert.Nil(t, AuthenticationSpec{JWTSecretName: util.String("foo")}.Validate(true))
|
||||
|
||||
// Not valid
|
||||
assert.Error(t, AuthenticationSpec{JWTSecretName: "Foo"}.Validate(false))
|
||||
assert.Error(t, AuthenticationSpec{JWTSecretName: util.String("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.String("None")}.IsAuthenticated())
|
||||
assert.True(t, AuthenticationSpec{JWTSecretName: util.String("foo")}.IsAuthenticated())
|
||||
assert.True(t, AuthenticationSpec{JWTSecretName: util.String("")}.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.String("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.String("None")},
|
||||
AuthenticationSpec{JWTSecretName: util.String("None")},
|
||||
AuthenticationSpec{JWTSecretName: util.String("None")},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
AuthenticationSpec{JWTSecretName: "foo"},
|
||||
AuthenticationSpec{JWTSecretName: "foo"},
|
||||
AuthenticationSpec{JWTSecretName: "foo"},
|
||||
AuthenticationSpec{JWTSecretName: util.String("foo")},
|
||||
AuthenticationSpec{JWTSecretName: util.String("foo")},
|
||||
AuthenticationSpec{JWTSecretName: util.String("foo")},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
AuthenticationSpec{JWTSecretName: "foo"},
|
||||
AuthenticationSpec{JWTSecretName: "foo2"},
|
||||
AuthenticationSpec{JWTSecretName: "foo2"},
|
||||
AuthenticationSpec{JWTSecretName: util.String("foo")},
|
||||
AuthenticationSpec{JWTSecretName: util.String("foo2")},
|
||||
AuthenticationSpec{JWTSecretName: util.String("foo2")},
|
||||
nil,
|
||||
},
|
||||
|
||||
// Invalid changes
|
||||
{
|
||||
AuthenticationSpec{JWTSecretName: "foo"},
|
||||
AuthenticationSpec{JWTSecretName: "None"},
|
||||
AuthenticationSpec{JWTSecretName: "foo"},
|
||||
AuthenticationSpec{JWTSecretName: util.String("foo")},
|
||||
AuthenticationSpec{JWTSecretName: util.String("None")},
|
||||
AuthenticationSpec{JWTSecretName: util.String("foo")},
|
||||
[]string{"test.jwtSecretName"},
|
||||
},
|
||||
{
|
||||
AuthenticationSpec{JWTSecretName: "None"},
|
||||
AuthenticationSpec{JWTSecretName: "foo"},
|
||||
AuthenticationSpec{JWTSecretName: "None"},
|
||||
AuthenticationSpec{JWTSecretName: util.String("None")},
|
||||
AuthenticationSpec{JWTSecretName: util.String("foo")},
|
||||
AuthenticationSpec{JWTSecretName: util.String("None")},
|
||||
[]string{"test.jwtSecretName"},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -102,6 +102,35 @@ func (s *DeploymentSpec) SetDefaults(deploymentName string) {
|
|||
s.SyncWorkers.SetDefaults(ServerGroupSyncWorkers, s.Sync.Enabled, s.Mode)
|
||||
}
|
||||
|
||||
// SetDefaultsFrom fills unspecified fields with a value from given source spec.
|
||||
func (s *DeploymentSpec) SetDefaultsFrom(source DeploymentSpec) {
|
||||
if s.Mode == "" {
|
||||
s.Mode = source.Mode
|
||||
}
|
||||
if s.Environment == "" {
|
||||
s.Environment = source.Environment
|
||||
}
|
||||
if s.StorageEngine == "" {
|
||||
s.StorageEngine = source.StorageEngine
|
||||
}
|
||||
if s.Image == "" {
|
||||
s.Image = source.Image
|
||||
}
|
||||
if s.ImagePullPolicy == "" {
|
||||
s.ImagePullPolicy = 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 {
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -23,17 +23,26 @@
|
|||
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 {
|
||||
if s.TokenSecretName == nil {
|
||||
return ""
|
||||
}
|
||||
return *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 +52,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.StringOrNil(source.TokenSecretName)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.String("")}.Validate())
|
||||
assert.Nil(t, MonitoringSpec{TokenSecretName: util.String("foo")}.Validate())
|
||||
assert.Nil(t, MonitoringSpec{TokenSecretName: util.String("foo")}.Validate())
|
||||
|
||||
// Not valid
|
||||
assert.Error(t, MonitoringSpec{TokenSecretName: "Foo"}.Validate())
|
||||
assert.Error(t, MonitoringSpec{TokenSecretName: util.String("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.String("foo")}).GetTokenSecretName())
|
||||
}
|
||||
|
|
|
@ -23,12 +23,27 @@
|
|||
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 {
|
||||
if s.KeySecretName == nil {
|
||||
return ""
|
||||
}
|
||||
return *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 +54,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 +70,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.StringOrNil(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 +84,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.StringOrNil(s.Encryption.KeySecretName)
|
||||
resetFields = append(resetFields, fieldPrefix+".encryption.keySecretName")
|
||||
}
|
||||
return resetFields
|
||||
|
|
|
@ -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.String("foo")}}.Validate())
|
||||
|
||||
// Not valid
|
||||
assert.Error(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "Foo"}}.Validate())
|
||||
assert.Error(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("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.String("")}}.IsEncrypted())
|
||||
assert.True(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("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.String("foo")}},
|
||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo")}},
|
||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo")}},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}},
|
||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo2"}},
|
||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo2"}},
|
||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo")}},
|
||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo2")}},
|
||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo2")}},
|
||||
nil,
|
||||
},
|
||||
|
||||
// Invalid changes
|
||||
{
|
||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}},
|
||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: ""}},
|
||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}},
|
||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo")}},
|
||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("")}},
|
||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo")}},
|
||||
[]string{"test.encryption.keySecretName"},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -26,20 +26,38 @@ 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"`
|
||||
}
|
||||
|
||||
// GetCount returns the value of count.
|
||||
func (s ServerGroupSpec) GetCount() int {
|
||||
if s.Count == nil {
|
||||
return 0
|
||||
}
|
||||
return *s.Count
|
||||
}
|
||||
|
||||
// GetStorageClassName returns the value of count.
|
||||
func (s ServerGroupSpec) GetStorageClassName() string {
|
||||
if s.StorageClassName == nil {
|
||||
return ""
|
||||
}
|
||||
return *s.StorageClassName
|
||||
}
|
||||
|
||||
// Validate the given group spec
|
||||
func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentMode, env Environment) error {
|
||||
if used {
|
||||
|
@ -56,13 +74,13 @@ func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentM
|
|||
minCount = 2
|
||||
}
|
||||
}
|
||||
if s.Count < minCount {
|
||||
if s.GetCount() < minCount {
|
||||
return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected >= %d", s.Count, minCount))
|
||||
}
|
||||
if s.Count > 1 && group == ServerGroupSingle && mode == DeploymentModeSingle {
|
||||
if s.GetCount() > 1 && group == ServerGroupSingle && mode == DeploymentModeSingle {
|
||||
return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected 1", s.Count))
|
||||
}
|
||||
} else if s.Count != 0 {
|
||||
} else if s.GetCount() != 0 {
|
||||
return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d for un-used group. Expected 0", s.Count))
|
||||
}
|
||||
return nil
|
||||
|
@ -70,16 +88,16 @@ func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentM
|
|||
|
||||
// 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.Int(1) // Single server
|
||||
} else {
|
||||
s.Count = 2 // Resilient single
|
||||
s.Count = util.Int(2) // Resilient single
|
||||
}
|
||||
default:
|
||||
s.Count = 3
|
||||
s.Count = util.Int(3)
|
||||
}
|
||||
}
|
||||
if _, found := s.Resources.Requests[v1.ResourceStorage]; !found {
|
||||
|
@ -93,18 +111,32 @@ func (s *ServerGroupSpec) SetDefaults(group ServerGroup, used bool, mode Deploym
|
|||
}
|
||||
}
|
||||
|
||||
// SetDefaultsFrom fills unspecified fields with a value from given source spec.
|
||||
func (s *ServerGroupSpec) SetDefaultsFrom(source ServerGroupSpec) {
|
||||
if s.Count == nil {
|
||||
s.Count = util.IntOrNil(source.Count)
|
||||
}
|
||||
if s.Args == nil {
|
||||
s.Args = source.Args
|
||||
}
|
||||
if s.StorageClassName == nil {
|
||||
s.StorageClassName = util.StringOrNil(source.StorageClassName)
|
||||
}
|
||||
// TODO Resources
|
||||
}
|
||||
|
||||
// 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.IntOrNil(s.Count)
|
||||
resetFields = append(resetFields, fieldPrefix+".count")
|
||||
}
|
||||
}
|
||||
if s.StorageClassName != target.StorageClassName {
|
||||
target.StorageClassName = s.StorageClassName
|
||||
if s.GetStorageClassName() != target.GetStorageClassName() {
|
||||
target.StorageClassName = util.StringOrNil(s.StorageClassName)
|
||||
resetFields = append(resetFields, fieldPrefix+".storageClassName")
|
||||
}
|
||||
return resetFields
|
||||
|
|
|
@ -73,6 +73,19 @@ func (s *SyncSpec) SetDefaults(defaultImage string, defaulPullPolicy v1.PullPoli
|
|||
s.Monitoring.SetDefaults()
|
||||
}
|
||||
|
||||
// SetDefaultsFrom fills unspecified fields with a value from given source spec.
|
||||
func (s *SyncSpec) SetDefaultsFrom(source SyncSpec) {
|
||||
if s.Image == "" {
|
||||
s.Image = source.Image
|
||||
}
|
||||
if s.ImagePullPolicy == "" {
|
||||
s.ImagePullPolicy = 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.
|
||||
|
|
|
@ -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"}
|
||||
auth := AuthenticationSpec{JWTSecretName: util.String("foo")}
|
||||
tls := TLSSpec{CASecretName: util.String("None")}
|
||||
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))
|
||||
assert.Nil(t, SyncSpec{Image: "foo", Authentication: auth, TLS: tls, Enabled: 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: "foo", Authentication: auth, TLS: tls, Enabled: true}.Validate(DeploymentModeSingle))
|
||||
assert.Error(t, SyncSpec{Image: "foo", Authentication: auth, TLS: tls, Enabled: true}.Validate(DeploymentModeResilientSingle))
|
||||
}
|
||||
|
||||
func TestSyncSpecSetDefaults(t *testing.T) {
|
||||
|
@ -58,8 +60,8 @@ func TestSyncSpecSetDefaults(t *testing.T) {
|
|||
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.Equal(t, "test-jwt", def(SyncSpec{}).Authentication.GetJWTSecretName())
|
||||
assert.Equal(t, "foo", def(SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo")}}).Authentication.GetJWTSecretName())
|
||||
}
|
||||
|
||||
func TestSyncSpecResetImmutableFields(t *testing.T) {
|
||||
|
@ -95,35 +97,35 @@ func TestSyncSpecResetImmutableFields(t *testing.T) {
|
|||
nil,
|
||||
},
|
||||
{
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("None")}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("None")}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("None")}},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo")}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo")}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo")}},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo2"}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo2"}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo")}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo2")}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo2")}},
|
||||
nil,
|
||||
},
|
||||
|
||||
// Invalid changes
|
||||
{
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo")}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("None")}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("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.String("None")}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo")}},
|
||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("None")}},
|
||||
[]string{"test.auth.jwtSecretName"},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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,14 +48,35 @@ const (
|
|||
CASecretNameDisabled = "None"
|
||||
)
|
||||
|
||||
// IsSecure returns true when a CA secret has been set, false otherwise.
|
||||
func (s TLSSpec) IsSecure() bool {
|
||||
return s.CASecretName != CASecretNameDisabled
|
||||
// GetCASecretName returns the value of caSecretName.
|
||||
func (s TLSSpec) GetCASecretName() string {
|
||||
if s.CASecretName == nil {
|
||||
return ""
|
||||
}
|
||||
return *s.CASecretName
|
||||
}
|
||||
|
||||
// GetAltNames splits the list of AltNames into DNS names, IP addresses & email addresses.
|
||||
// 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 {
|
||||
if s.TTL == nil {
|
||||
return time.Duration(0)
|
||||
}
|
||||
return *s.TTL
|
||||
}
|
||||
|
||||
// IsSecure returns true when a CA secret has been set, false otherwise.
|
||||
func (s TLSSpec) IsSecure() bool {
|
||||
return s.GetCASecretName() != CASecretNameDisabled
|
||||
}
|
||||
|
||||
// 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) {
|
||||
func (s TLSSpec) GetParsedAltNames() (dnsNames, ipAddresses, emailAddresses []string, err error) {
|
||||
for _, name := range s.AltNames {
|
||||
if net.ParseIP(name) != nil {
|
||||
ipAddresses = append(ipAddresses, name)
|
||||
|
@ -72,10 +94,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 +106,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.String(defaultCASecretName)
|
||||
}
|
||||
if s.TTL == 0 {
|
||||
s.TTL = defaultTLSTTL
|
||||
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.Duration(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.String(source.GetCASecretName())
|
||||
}
|
||||
if s.AltNames == nil {
|
||||
s.AltNames = source.AltNames
|
||||
}
|
||||
if s.TTL == nil {
|
||||
s.TTL = util.Duration(source.GetTTL())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.String("foo")}.Validate())
|
||||
assert.Nil(t, TLSSpec{CASecretName: util.String("None")}.Validate())
|
||||
assert.Nil(t, TLSSpec{CASecretName: util.String("None"), AltNames: []string{}}.Validate())
|
||||
assert.Nil(t, TLSSpec{CASecretName: util.String("None"), AltNames: []string{"foo"}}.Validate())
|
||||
assert.Nil(t, TLSSpec{CASecretName: util.String("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.String("")}.Validate())
|
||||
assert.Error(t, TLSSpec{CASecretName: util.String("Foo")}.Validate())
|
||||
assert.Error(t, TLSSpec{CASecretName: util.String("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.String("")}.IsSecure())
|
||||
assert.True(t, TLSSpec{CASecretName: util.String("foo")}.IsSecure())
|
||||
assert.False(t, TLSSpec{CASecretName: util.String("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.Equal(t, "", def(TLSSpec{}).GetCASecretName())
|
||||
assert.Equal(t, "foo", def(TLSSpec{CASecretName: util.String("foo")}).GetCASecretName())
|
||||
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, defaultTLSTTL, def(TLSSpec{}).GetTTL())
|
||||
assert.Equal(t, time.Hour, def(TLSSpec{TTL: util.Duration(time.Hour)}).GetTTL())
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
package v1alpha
|
||||
|
||||
import (
|
||||
time "time"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
@ -198,6 +200,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
|
||||
}
|
||||
|
||||
|
@ -401,11 +412,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
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,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
|
||||
)
|
||||
|
@ -108,6 +108,10 @@ 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()
|
||||
|
@ -284,10 +288,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")
|
||||
}
|
||||
|
@ -311,6 +319,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))
|
||||
}
|
||||
|
||||
// Trigger inspect
|
||||
d.inspectTrigger.Trigger()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -69,13 +69,13 @@ func createPlan(log zerolog.Logger, currentPlan api.Plan, spec api.DeploymentSpe
|
|||
// 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())...)
|
||||
}
|
||||
|
||||
// Return plan
|
||||
|
|
|
@ -139,7 +139,7 @@ func createArangodArgs(apiObject metav1.Object, deplSpec api.DeploymentSpec, gro
|
|||
optionPair{"--cluster.my-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"},
|
||||
|
@ -242,7 +242,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 {
|
||||
|
@ -344,14 +344,14 @@ 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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -146,7 +146,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)
|
||||
}
|
||||
|
|
77
pkg/util/refs.go
Normal file
77
pkg/util/refs.go
Normal file
|
@ -0,0 +1,77 @@
|
|||
//
|
||||
// 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"
|
||||
|
||||
// String returns a reference to a string with given value.
|
||||
func String(input string) *string {
|
||||
return &input
|
||||
}
|
||||
|
||||
// StringOrNil returns nil if input is nil, otherwise returns a clone of the given value.
|
||||
func StringOrNil(input *string) *string {
|
||||
if input == nil {
|
||||
return nil
|
||||
}
|
||||
return String(*input)
|
||||
}
|
||||
|
||||
// Int returns a reference to an int with given value.
|
||||
func Int(input int) *int {
|
||||
return &input
|
||||
}
|
||||
|
||||
// IntOrNil returns nil if input is nil, otherwise returns a clone of the given value.
|
||||
func IntOrNil(input *int) *int {
|
||||
if input == nil {
|
||||
return nil
|
||||
}
|
||||
return Int(*input)
|
||||
}
|
||||
|
||||
// Bool returns a reference to a bool with given value.
|
||||
func Bool(input bool) *bool {
|
||||
return &input
|
||||
}
|
||||
|
||||
// BoolOrNil returns nil if input is nil, otherwise returns a clone of the given value.
|
||||
func BoolOrNil(input *bool) *bool {
|
||||
if input == nil {
|
||||
return nil
|
||||
}
|
||||
return Bool(*input)
|
||||
}
|
||||
|
||||
// Duration returns a reference to a duration with given value.
|
||||
func Duration(input time.Duration) *time.Duration {
|
||||
return &input
|
||||
}
|
||||
|
||||
// DurationOrNil returns nil if input is nil, otherwise returns a clone of the given value.
|
||||
func DurationOrNil(input *time.Duration) *time.Duration {
|
||||
if input == nil {
|
||||
return nil
|
||||
}
|
||||
return Duration(*input)
|
||||
}
|
Loading…
Reference in a new issue