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 (
|
import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuthenticationSpec holds authentication specific configuration settings
|
// AuthenticationSpec holds authentication specific configuration settings
|
||||||
type AuthenticationSpec struct {
|
type AuthenticationSpec struct {
|
||||||
JWTSecretName string `json:"jwtSecretName,omitempty"`
|
JWTSecretName *string `json:"jwtSecretName,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -38,10 +39,18 @@ const (
|
||||||
JWTSecretNameDisabled = "None"
|
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.
|
// IsAuthenticated returns true if authentication is enabled.
|
||||||
// Returns false other (when JWTSecretName == "None").
|
// Returns false other (when JWTSecretName == "None").
|
||||||
func (s AuthenticationSpec) IsAuthenticated() bool {
|
func (s AuthenticationSpec) IsAuthenticated() bool {
|
||||||
return s.JWTSecretName != JWTSecretNameDisabled
|
return s.GetJWTSecretName() != JWTSecretNameDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the given spec
|
// Validate the given spec
|
||||||
|
@ -50,7 +59,7 @@ func (s AuthenticationSpec) Validate(required bool) error {
|
||||||
return maskAny(errors.Wrap(ValidationError, "JWT secret is required"))
|
return maskAny(errors.Wrap(ValidationError, "JWT secret is required"))
|
||||||
}
|
}
|
||||||
if s.IsAuthenticated() {
|
if s.IsAuthenticated() {
|
||||||
if err := k8sutil.ValidateResourceName(s.JWTSecretName); err != nil {
|
if err := k8sutil.ValidateResourceName(s.GetJWTSecretName()); err != nil {
|
||||||
return maskAny(err)
|
return maskAny(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,8 +68,17 @@ func (s AuthenticationSpec) Validate(required bool) error {
|
||||||
|
|
||||||
// SetDefaults fills in missing defaults
|
// SetDefaults fills in missing defaults
|
||||||
func (s *AuthenticationSpec) SetDefaults(defaultJWTSecretName string) {
|
func (s *AuthenticationSpec) SetDefaults(defaultJWTSecretName string) {
|
||||||
if s.JWTSecretName == "" {
|
if s.GetJWTSecretName() == "" {
|
||||||
s.JWTSecretName = defaultJWTSecretName
|
// 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
|
var resetFields []string
|
||||||
if s.IsAuthenticated() != target.IsAuthenticated() {
|
if s.IsAuthenticated() != target.IsAuthenticated() {
|
||||||
// Note: You can change the name, but not from empty to non-empty (or reverse).
|
// 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")
|
resetFields = append(resetFields, fieldPrefix+".jwtSecretName")
|
||||||
}
|
}
|
||||||
return resetFields
|
return resetFields
|
||||||
|
|
|
@ -25,23 +25,24 @@ package v1alpha
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAuthenticationSpecValidate(t *testing.T) {
|
func TestAuthenticationSpecValidate(t *testing.T) {
|
||||||
// Valid
|
// Valid
|
||||||
assert.Nil(t, AuthenticationSpec{JWTSecretName: "None"}.Validate(false))
|
assert.Nil(t, AuthenticationSpec{JWTSecretName: util.String("None")}.Validate(false))
|
||||||
assert.Nil(t, AuthenticationSpec{JWTSecretName: "foo"}.Validate(false))
|
assert.Nil(t, AuthenticationSpec{JWTSecretName: util.String("foo")}.Validate(false))
|
||||||
assert.Nil(t, AuthenticationSpec{JWTSecretName: "foo"}.Validate(true))
|
assert.Nil(t, AuthenticationSpec{JWTSecretName: util.String("foo")}.Validate(true))
|
||||||
|
|
||||||
// Not valid
|
// 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) {
|
func TestAuthenticationSpecIsAuthenticated(t *testing.T) {
|
||||||
assert.False(t, AuthenticationSpec{JWTSecretName: "None"}.IsAuthenticated())
|
assert.False(t, AuthenticationSpec{JWTSecretName: util.String("None")}.IsAuthenticated())
|
||||||
assert.True(t, AuthenticationSpec{JWTSecretName: "foo"}.IsAuthenticated())
|
assert.True(t, AuthenticationSpec{JWTSecretName: util.String("foo")}.IsAuthenticated())
|
||||||
assert.True(t, AuthenticationSpec{JWTSecretName: ""}.IsAuthenticated())
|
assert.True(t, AuthenticationSpec{JWTSecretName: util.String("")}.IsAuthenticated())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthenticationSpecSetDefaults(t *testing.T) {
|
func TestAuthenticationSpecSetDefaults(t *testing.T) {
|
||||||
|
@ -50,8 +51,8 @@ func TestAuthenticationSpecSetDefaults(t *testing.T) {
|
||||||
return spec
|
return spec
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, "test-jwt", def(AuthenticationSpec{}).JWTSecretName)
|
assert.Equal(t, "test-jwt", def(AuthenticationSpec{}).GetJWTSecretName())
|
||||||
assert.Equal(t, "foo", def(AuthenticationSpec{JWTSecretName: "foo"}).JWTSecretName)
|
assert.Equal(t, "foo", def(AuthenticationSpec{JWTSecretName: util.String("foo")}).GetJWTSecretName())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthenticationSpecResetImmutableFields(t *testing.T) {
|
func TestAuthenticationSpecResetImmutableFields(t *testing.T) {
|
||||||
|
@ -63,35 +64,35 @@ func TestAuthenticationSpecResetImmutableFields(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
// Valid "changes"
|
// Valid "changes"
|
||||||
{
|
{
|
||||||
AuthenticationSpec{JWTSecretName: "None"},
|
AuthenticationSpec{JWTSecretName: util.String("None")},
|
||||||
AuthenticationSpec{JWTSecretName: "None"},
|
AuthenticationSpec{JWTSecretName: util.String("None")},
|
||||||
AuthenticationSpec{JWTSecretName: "None"},
|
AuthenticationSpec{JWTSecretName: util.String("None")},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
AuthenticationSpec{JWTSecretName: "foo"},
|
AuthenticationSpec{JWTSecretName: util.String("foo")},
|
||||||
AuthenticationSpec{JWTSecretName: "foo"},
|
AuthenticationSpec{JWTSecretName: util.String("foo")},
|
||||||
AuthenticationSpec{JWTSecretName: "foo"},
|
AuthenticationSpec{JWTSecretName: util.String("foo")},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
AuthenticationSpec{JWTSecretName: "foo"},
|
AuthenticationSpec{JWTSecretName: util.String("foo")},
|
||||||
AuthenticationSpec{JWTSecretName: "foo2"},
|
AuthenticationSpec{JWTSecretName: util.String("foo2")},
|
||||||
AuthenticationSpec{JWTSecretName: "foo2"},
|
AuthenticationSpec{JWTSecretName: util.String("foo2")},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Invalid changes
|
// Invalid changes
|
||||||
{
|
{
|
||||||
AuthenticationSpec{JWTSecretName: "foo"},
|
AuthenticationSpec{JWTSecretName: util.String("foo")},
|
||||||
AuthenticationSpec{JWTSecretName: "None"},
|
AuthenticationSpec{JWTSecretName: util.String("None")},
|
||||||
AuthenticationSpec{JWTSecretName: "foo"},
|
AuthenticationSpec{JWTSecretName: util.String("foo")},
|
||||||
[]string{"test.jwtSecretName"},
|
[]string{"test.jwtSecretName"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
AuthenticationSpec{JWTSecretName: "None"},
|
AuthenticationSpec{JWTSecretName: util.String("None")},
|
||||||
AuthenticationSpec{JWTSecretName: "foo"},
|
AuthenticationSpec{JWTSecretName: util.String("foo")},
|
||||||
AuthenticationSpec{JWTSecretName: "None"},
|
AuthenticationSpec{JWTSecretName: util.String("None")},
|
||||||
[]string{"test.jwtSecretName"},
|
[]string{"test.jwtSecretName"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,6 +102,35 @@ func (s *DeploymentSpec) SetDefaults(deploymentName string) {
|
||||||
s.SyncWorkers.SetDefaults(ServerGroupSyncWorkers, s.Sync.Enabled, s.Mode)
|
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.
|
// Validate the specification.
|
||||||
// Return errors when validation fails, nil on success.
|
// Return errors when validation fails, nil on success.
|
||||||
func (s *DeploymentSpec) Validate() error {
|
func (s *DeploymentSpec) Validate() error {
|
||||||
|
|
|
@ -47,4 +47,7 @@ type DeploymentStatus struct {
|
||||||
|
|
||||||
// Plan to update this deployment
|
// Plan to update this deployment
|
||||||
Plan Plan `json:"plan,omitempty"`
|
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
|
package v1alpha
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MonitoringSpec holds monitoring specific configuration settings
|
// MonitoringSpec holds monitoring specific configuration settings
|
||||||
type MonitoringSpec struct {
|
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
|
// Validate the given spec
|
||||||
func (s MonitoringSpec) Validate() error {
|
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 maskAny(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -43,3 +52,10 @@ func (s MonitoringSpec) Validate() error {
|
||||||
func (s *MonitoringSpec) SetDefaults() {
|
func (s *MonitoringSpec) SetDefaults() {
|
||||||
// Nothing needed
|
// 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 (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMonitoringSpecValidate(t *testing.T) {
|
func TestMonitoringSpecValidate(t *testing.T) {
|
||||||
// Valid
|
// Valid
|
||||||
assert.Nil(t, MonitoringSpec{TokenSecretName: ""}.Validate())
|
assert.Nil(t, MonitoringSpec{TokenSecretName: nil}.Validate())
|
||||||
assert.Nil(t, MonitoringSpec{TokenSecretName: "foo"}.Validate())
|
assert.Nil(t, MonitoringSpec{TokenSecretName: util.String("")}.Validate())
|
||||||
assert.Nil(t, MonitoringSpec{TokenSecretName: "foo"}.Validate())
|
assert.Nil(t, MonitoringSpec{TokenSecretName: util.String("foo")}.Validate())
|
||||||
|
assert.Nil(t, MonitoringSpec{TokenSecretName: util.String("foo")}.Validate())
|
||||||
|
|
||||||
// Not valid
|
// Not valid
|
||||||
assert.Error(t, MonitoringSpec{TokenSecretName: "Foo"}.Validate())
|
assert.Error(t, MonitoringSpec{TokenSecretName: util.String("Foo")}.Validate())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMonitoringSpecSetDefaults(t *testing.T) {
|
func TestMonitoringSpecSetDefaults(t *testing.T) {
|
||||||
|
@ -44,6 +46,6 @@ func TestMonitoringSpecSetDefaults(t *testing.T) {
|
||||||
return spec
|
return spec
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, "", def(MonitoringSpec{}).TokenSecretName)
|
assert.Equal(t, "", def(MonitoringSpec{}).GetTokenSecretName())
|
||||||
assert.Equal(t, "foo", def(MonitoringSpec{TokenSecretName: "foo"}).TokenSecretName)
|
assert.Equal(t, "foo", def(MonitoringSpec{TokenSecretName: util.String("foo")}).GetTokenSecretName())
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,27 @@
|
||||||
package v1alpha
|
package v1alpha
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RocksDBEncryptionSpec holds rocksdb encryption at rest specific configuration settings
|
// RocksDBEncryptionSpec holds rocksdb encryption at rest specific configuration settings
|
||||||
type RocksDBEncryptionSpec struct {
|
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
|
// RocksDBSpec holds rocksdb specific configuration settings
|
||||||
|
@ -39,12 +54,12 @@ type RocksDBSpec struct {
|
||||||
// IsEncrypted returns true when an encryption key secret name is provided,
|
// IsEncrypted returns true when an encryption key secret name is provided,
|
||||||
// false otherwise.
|
// false otherwise.
|
||||||
func (s RocksDBSpec) IsEncrypted() bool {
|
func (s RocksDBSpec) IsEncrypted() bool {
|
||||||
return s.Encryption.KeySecretName != ""
|
return s.Encryption.IsEncrypted()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the given spec
|
// Validate the given spec
|
||||||
func (s RocksDBSpec) Validate() error {
|
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 maskAny(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -55,6 +70,13 @@ func (s *RocksDBSpec) SetDefaults() {
|
||||||
// Nothing needed
|
// 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.
|
// 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.
|
// It returns a list of fields that have been reset.
|
||||||
// Field names are relative to given field prefix.
|
// Field names are relative to given field prefix.
|
||||||
|
@ -62,7 +84,7 @@ func (s RocksDBSpec) ResetImmutableFields(fieldPrefix string, target *RocksDBSpe
|
||||||
var resetFields []string
|
var resetFields []string
|
||||||
if s.IsEncrypted() != target.IsEncrypted() {
|
if s.IsEncrypted() != target.IsEncrypted() {
|
||||||
// Note: You can change the name, but not from empty to non-empty (or reverse).
|
// 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")
|
resetFields = append(resetFields, fieldPrefix+".encryption.keySecretName")
|
||||||
}
|
}
|
||||||
return resetFields
|
return resetFields
|
||||||
|
|
|
@ -25,22 +25,23 @@ package v1alpha
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRocksDBSpecValidate(t *testing.T) {
|
func TestRocksDBSpecValidate(t *testing.T) {
|
||||||
// Valid
|
// Valid
|
||||||
assert.Nil(t, RocksDBSpec{}.Validate())
|
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
|
// 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) {
|
func TestRocksDBSpecIsEncrypted(t *testing.T) {
|
||||||
assert.False(t, RocksDBSpec{}.IsEncrypted())
|
assert.False(t, RocksDBSpec{}.IsEncrypted())
|
||||||
assert.False(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: ""}}.IsEncrypted())
|
assert.False(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("")}}.IsEncrypted())
|
||||||
assert.True(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}}.IsEncrypted())
|
assert.True(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo")}}.IsEncrypted())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRocksDBSpecSetDefaults(t *testing.T) {
|
func TestRocksDBSpecSetDefaults(t *testing.T) {
|
||||||
|
@ -49,7 +50,7 @@ func TestRocksDBSpecSetDefaults(t *testing.T) {
|
||||||
return spec
|
return spec
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, "", def(RocksDBSpec{}).Encryption.KeySecretName)
|
assert.Equal(t, "", def(RocksDBSpec{}).Encryption.GetKeySecretName())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRocksDBSpecResetImmutableFields(t *testing.T) {
|
func TestRocksDBSpecResetImmutableFields(t *testing.T) {
|
||||||
|
@ -67,23 +68,23 @@ func TestRocksDBSpecResetImmutableFields(t *testing.T) {
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}},
|
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo")}},
|
||||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}},
|
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo")}},
|
||||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}},
|
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo")}},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}},
|
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo")}},
|
||||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo2"}},
|
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo2")}},
|
||||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo2"}},
|
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo2")}},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Invalid changes
|
// Invalid changes
|
||||||
{
|
{
|
||||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}},
|
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo")}},
|
||||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: ""}},
|
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("")}},
|
||||||
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: "foo"}},
|
RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.String("foo")}},
|
||||||
[]string{"test.encryption.keySecretName"},
|
[]string{"test.encryption.keySecretName"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,20 +26,38 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"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)
|
// ServerGroupSpec contains the specification for all servers in a specific group (e.g. all agents)
|
||||||
type ServerGroupSpec struct {
|
type ServerGroupSpec struct {
|
||||||
// Count holds the requested number of servers
|
// Count holds the requested number of servers
|
||||||
Count int `json:"count,omitempty"`
|
Count *int `json:"count,omitempty"`
|
||||||
// Args holds additional commandline arguments
|
// Args holds additional commandline arguments
|
||||||
Args []string `json:"args,omitempty"`
|
Args []string `json:"args,omitempty"`
|
||||||
// StorageClassName specifies the classname for storage of the servers.
|
// 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 holds resource requests & limits
|
||||||
Resources v1.ResourceRequirements `json:"resource,omitempty"`
|
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
|
// Validate the given group spec
|
||||||
func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentMode, env Environment) error {
|
func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentMode, env Environment) error {
|
||||||
if used {
|
if used {
|
||||||
|
@ -56,13 +74,13 @@ func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentM
|
||||||
minCount = 2
|
minCount = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.Count < minCount {
|
if s.GetCount() < minCount {
|
||||||
return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected >= %d", s.Count, 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))
|
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 maskAny(errors.Wrapf(ValidationError, "Invalid count value %d for un-used group. Expected 0", s.Count))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -70,16 +88,16 @@ func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentM
|
||||||
|
|
||||||
// SetDefaults fills in missing defaults
|
// SetDefaults fills in missing defaults
|
||||||
func (s *ServerGroupSpec) SetDefaults(group ServerGroup, used bool, mode DeploymentMode) {
|
func (s *ServerGroupSpec) SetDefaults(group ServerGroup, used bool, mode DeploymentMode) {
|
||||||
if s.Count == 0 && used {
|
if s.GetCount() == 0 && used {
|
||||||
switch group {
|
switch group {
|
||||||
case ServerGroupSingle:
|
case ServerGroupSingle:
|
||||||
if mode == DeploymentModeSingle {
|
if mode == DeploymentModeSingle {
|
||||||
s.Count = 1 // Single server
|
s.Count = util.Int(1) // Single server
|
||||||
} else {
|
} else {
|
||||||
s.Count = 2 // Resilient single
|
s.Count = util.Int(2) // Resilient single
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
s.Count = 3
|
s.Count = util.Int(3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _, found := s.Resources.Requests[v1.ResourceStorage]; !found {
|
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.
|
// 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.
|
// It returns a list of fields that have been reset.
|
||||||
func (s ServerGroupSpec) ResetImmutableFields(group ServerGroup, fieldPrefix string, target *ServerGroupSpec) []string {
|
func (s ServerGroupSpec) ResetImmutableFields(group ServerGroup, fieldPrefix string, target *ServerGroupSpec) []string {
|
||||||
var resetFields []string
|
var resetFields []string
|
||||||
if group == ServerGroupAgents {
|
if group == ServerGroupAgents {
|
||||||
if s.Count != target.Count {
|
if s.GetCount() != target.GetCount() {
|
||||||
target.Count = s.Count
|
target.Count = util.IntOrNil(s.Count)
|
||||||
resetFields = append(resetFields, fieldPrefix+".count")
|
resetFields = append(resetFields, fieldPrefix+".count")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.StorageClassName != target.StorageClassName {
|
if s.GetStorageClassName() != target.GetStorageClassName() {
|
||||||
target.StorageClassName = s.StorageClassName
|
target.StorageClassName = util.StringOrNil(s.StorageClassName)
|
||||||
resetFields = append(resetFields, fieldPrefix+".storageClassName")
|
resetFields = append(resetFields, fieldPrefix+".storageClassName")
|
||||||
}
|
}
|
||||||
return resetFields
|
return resetFields
|
||||||
|
|
|
@ -73,6 +73,19 @@ func (s *SyncSpec) SetDefaults(defaultImage string, defaulPullPolicy v1.PullPoli
|
||||||
s.Monitoring.SetDefaults()
|
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.
|
// 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.
|
// It returns a list of fields that have been reset.
|
||||||
// Field names are relative to given field prefix.
|
// Field names are relative to given field prefix.
|
||||||
|
|
|
@ -25,24 +25,26 @@ package v1alpha
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSyncSpecValidate(t *testing.T) {
|
func TestSyncSpecValidate(t *testing.T) {
|
||||||
// Valid
|
// 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(DeploymentModeSingle))
|
||||||
assert.Nil(t, SyncSpec{Image: "foo", Authentication: auth}.Validate(DeploymentModeResilientSingle))
|
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}.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
|
// Not valid
|
||||||
assert.Error(t, SyncSpec{Image: "", Authentication: auth}.Validate(DeploymentModeSingle))
|
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(DeploymentModeResilientSingle))
|
||||||
assert.Error(t, SyncSpec{Image: "", Authentication: auth}.Validate(DeploymentModeCluster))
|
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, TLS: tls, 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(DeploymentModeResilientSingle))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSyncSpecSetDefaults(t *testing.T) {
|
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, "foo", def(SyncSpec{Image: "foo"}).Image)
|
||||||
assert.Equal(t, v1.PullAlways, def(SyncSpec{}).ImagePullPolicy)
|
assert.Equal(t, v1.PullAlways, def(SyncSpec{}).ImagePullPolicy)
|
||||||
assert.Equal(t, v1.PullNever, def(SyncSpec{ImagePullPolicy: v1.PullNever}).ImagePullPolicy)
|
assert.Equal(t, v1.PullNever, def(SyncSpec{ImagePullPolicy: v1.PullNever}).ImagePullPolicy)
|
||||||
assert.Equal(t, "test-jwt", def(SyncSpec{}).Authentication.JWTSecretName)
|
assert.Equal(t, "test-jwt", def(SyncSpec{}).Authentication.GetJWTSecretName())
|
||||||
assert.Equal(t, "foo", def(SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}}).Authentication.JWTSecretName)
|
assert.Equal(t, "foo", def(SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo")}}).Authentication.GetJWTSecretName())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSyncSpecResetImmutableFields(t *testing.T) {
|
func TestSyncSpecResetImmutableFields(t *testing.T) {
|
||||||
|
@ -95,35 +97,35 @@ func TestSyncSpecResetImmutableFields(t *testing.T) {
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}},
|
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("None")}},
|
||||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}},
|
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("None")}},
|
||||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}},
|
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("None")}},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}},
|
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo")}},
|
||||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}},
|
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo")}},
|
||||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}},
|
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo")}},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}},
|
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo")}},
|
||||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo2"}},
|
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo2")}},
|
||||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo2"}},
|
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo2")}},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Invalid changes
|
// Invalid changes
|
||||||
{
|
{
|
||||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}},
|
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo")}},
|
||||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}},
|
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("None")}},
|
||||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}},
|
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo")}},
|
||||||
[]string{"test.auth.jwtSecretName"},
|
[]string{"test.auth.jwtSecretName"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}},
|
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("None")}},
|
||||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "foo"}},
|
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("foo")}},
|
||||||
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: "None"}},
|
SyncSpec{Authentication: AuthenticationSpec{JWTSecretName: util.String("None")}},
|
||||||
[]string{"test.auth.jwtSecretName"},
|
[]string{"test.auth.jwtSecretName"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/validation"
|
"github.com/arangodb/kube-arangodb/pkg/util/validation"
|
||||||
)
|
)
|
||||||
|
@ -37,9 +38,9 @@ const (
|
||||||
|
|
||||||
// TLSSpec holds TLS specific configuration settings
|
// TLSSpec holds TLS specific configuration settings
|
||||||
type TLSSpec struct {
|
type TLSSpec struct {
|
||||||
CASecretName string `json:"caSecretName,omitempty"`
|
CASecretName *string `json:"caSecretName,omitempty"`
|
||||||
AltNames []string `json:"altNames,omitempty"`
|
AltNames []string `json:"altNames,omitempty"`
|
||||||
TTL time.Duration `json:"ttl,omitempty"`
|
TTL *time.Duration `json:"ttl,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -47,14 +48,35 @@ const (
|
||||||
CASecretNameDisabled = "None"
|
CASecretNameDisabled = "None"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsSecure returns true when a CA secret has been set, false otherwise.
|
// GetCASecretName returns the value of caSecretName.
|
||||||
func (s TLSSpec) IsSecure() bool {
|
func (s TLSSpec) GetCASecretName() string {
|
||||||
return s.CASecretName != CASecretNameDisabled
|
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.
|
// 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 {
|
for _, name := range s.AltNames {
|
||||||
if net.ParseIP(name) != nil {
|
if net.ParseIP(name) != nil {
|
||||||
ipAddresses = append(ipAddresses, name)
|
ipAddresses = append(ipAddresses, name)
|
||||||
|
@ -72,10 +94,10 @@ func (s TLSSpec) GetAltNames() (dnsNames, ipAddresses, emailAddresses []string,
|
||||||
// Validate the given spec
|
// Validate the given spec
|
||||||
func (s TLSSpec) Validate() error {
|
func (s TLSSpec) Validate() error {
|
||||||
if s.IsSecure() {
|
if s.IsSecure() {
|
||||||
if err := k8sutil.ValidateOptionalResourceName(s.CASecretName); err != nil {
|
if err := k8sutil.ValidateResourceName(s.GetCASecretName()); err != nil {
|
||||||
return maskAny(err)
|
return maskAny(err)
|
||||||
}
|
}
|
||||||
if _, _, _, err := s.GetAltNames(); err != nil {
|
if _, _, _, err := s.GetParsedAltNames(); err != nil {
|
||||||
return maskAny(err)
|
return maskAny(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,10 +106,27 @@ func (s TLSSpec) Validate() error {
|
||||||
|
|
||||||
// SetDefaults fills in missing defaults
|
// SetDefaults fills in missing defaults
|
||||||
func (s *TLSSpec) SetDefaults(defaultCASecretName string) {
|
func (s *TLSSpec) SetDefaults(defaultCASecretName string) {
|
||||||
if s.CASecretName == "" {
|
if s.GetCASecretName() == "" {
|
||||||
s.CASecretName = defaultCASecretName
|
// 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 {
|
if s.GetTTL() == 0 {
|
||||||
s.TTL = defaultTLSTTL
|
// 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"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTLSSpecValidate(t *testing.T) {
|
func TestTLSSpecValidate(t *testing.T) {
|
||||||
// Valid
|
// Valid
|
||||||
assert.Nil(t, TLSSpec{CASecretName: ""}.Validate())
|
assert.Nil(t, TLSSpec{CASecretName: util.String("foo")}.Validate())
|
||||||
assert.Nil(t, TLSSpec{CASecretName: "foo"}.Validate())
|
assert.Nil(t, TLSSpec{CASecretName: util.String("None")}.Validate())
|
||||||
assert.Nil(t, TLSSpec{CASecretName: "None"}.Validate())
|
assert.Nil(t, TLSSpec{CASecretName: util.String("None"), AltNames: []string{}}.Validate())
|
||||||
assert.Nil(t, TLSSpec{AltNames: []string{}}.Validate())
|
assert.Nil(t, TLSSpec{CASecretName: util.String("None"), AltNames: []string{"foo"}}.Validate())
|
||||||
assert.Nil(t, TLSSpec{AltNames: []string{"foo"}}.Validate())
|
assert.Nil(t, TLSSpec{CASecretName: util.String("None"), AltNames: []string{"email@example.com", "127.0.0.1"}}.Validate())
|
||||||
assert.Nil(t, TLSSpec{AltNames: []string{"email@example.com", "127.0.0.1"}}.Validate())
|
|
||||||
|
|
||||||
// Not valid
|
// Not valid
|
||||||
assert.Error(t, TLSSpec{CASecretName: "Foo"}.Validate())
|
assert.Error(t, TLSSpec{CASecretName: nil}.Validate())
|
||||||
assert.Error(t, TLSSpec{AltNames: []string{"@@"}}.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) {
|
func TestTLSSpecIsSecure(t *testing.T) {
|
||||||
assert.True(t, TLSSpec{CASecretName: ""}.IsSecure())
|
assert.True(t, TLSSpec{CASecretName: util.String("")}.IsSecure())
|
||||||
assert.True(t, TLSSpec{CASecretName: "foo"}.IsSecure())
|
assert.True(t, TLSSpec{CASecretName: util.String("foo")}.IsSecure())
|
||||||
assert.False(t, TLSSpec{CASecretName: "None"}.IsSecure())
|
assert.False(t, TLSSpec{CASecretName: util.String("None")}.IsSecure())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTLSSpecSetDefaults(t *testing.T) {
|
func TestTLSSpecSetDefaults(t *testing.T) {
|
||||||
|
@ -55,10 +57,10 @@ func TestTLSSpecSetDefaults(t *testing.T) {
|
||||||
return spec
|
return spec
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, "", def(TLSSpec{}).CASecretName)
|
assert.Equal(t, "", def(TLSSpec{}).GetCASecretName())
|
||||||
assert.Equal(t, "foo", def(TLSSpec{CASecretName: "foo"}).CASecretName)
|
assert.Equal(t, "foo", def(TLSSpec{CASecretName: util.String("foo")}).GetCASecretName())
|
||||||
assert.Len(t, def(TLSSpec{}).AltNames, 0)
|
assert.Len(t, def(TLSSpec{}).AltNames, 0)
|
||||||
assert.Len(t, def(TLSSpec{AltNames: []string{"foo.local"}}).AltNames, 1)
|
assert.Len(t, def(TLSSpec{AltNames: []string{"foo.local"}}).AltNames, 1)
|
||||||
assert.Equal(t, defaultTLSTTL, def(TLSSpec{}).TTL)
|
assert.Equal(t, defaultTLSTTL, def(TLSSpec{}).GetTTL())
|
||||||
assert.Equal(t, time.Hour, def(TLSSpec{TTL: time.Hour}).TTL)
|
assert.Equal(t, time.Hour, def(TLSSpec{TTL: util.Duration(time.Hour)}).GetTTL())
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
package v1alpha
|
package v1alpha
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
time "time"
|
||||||
|
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
@ -198,6 +200,15 @@ func (in *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) {
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
(*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
|
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.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *TLSSpec) DeepCopyInto(out *TLSSpec) {
|
func (in *TLSSpec) DeepCopyInto(out *TLSSpec) {
|
||||||
*out = *in
|
*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 {
|
if in.AltNames != nil {
|
||||||
in, out := &in.AltNames, &out.AltNames
|
in, out := &in.AltNames, &out.AltNames
|
||||||
*out = make([]string, len(*in))
|
*out = make([]string, len(*in))
|
||||||
copy(*out, *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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ type deploymentEvent struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
deploymentEventQueueSize = 100
|
deploymentEventQueueSize = 256
|
||||||
minInspectionInterval = time.Second // Ensure we inspect the generated resources no less than with this interval
|
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
|
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()),
|
eventsCli: deps.KubeCli.Core().Events(apiObject.GetNamespace()),
|
||||||
clientCache: newClientCache(deps.KubeCli, apiObject),
|
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.run()
|
||||||
go d.listenForPodEvents()
|
go d.listenForPodEvents()
|
||||||
|
@ -284,10 +288,14 @@ func (d *Deployment) handleArangoDeploymentUpdatedEvent(event *deploymentEvent)
|
||||||
return maskAny(err)
|
return maskAny(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
specBefore := d.apiObject.Spec
|
||||||
|
if d.status.AcceptedSpec != nil {
|
||||||
|
specBefore = *d.status.AcceptedSpec
|
||||||
|
}
|
||||||
newAPIObject := current.DeepCopy()
|
newAPIObject := current.DeepCopy()
|
||||||
newAPIObject.Spec.SetDefaults(newAPIObject.GetName())
|
newAPIObject.Spec.SetDefaultsFrom(specBefore)
|
||||||
newAPIObject.Status = d.status
|
newAPIObject.Status = d.status
|
||||||
resetFields := d.apiObject.Spec.ResetImmutableFields(&newAPIObject.Spec)
|
resetFields := specBefore.ResetImmutableFields(&newAPIObject.Spec)
|
||||||
if len(resetFields) > 0 {
|
if len(resetFields) > 0 {
|
||||||
log.Debug().Strs("fields", resetFields).Msg("Found modified immutable fields")
|
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 {
|
if err := d.updateCRSpec(newAPIObject.Spec); err != nil {
|
||||||
return maskAny(fmt.Errorf("failed to update ArangoDeployment spec: %v", err))
|
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
|
// Trigger inspect
|
||||||
d.inspectTrigger.Trigger()
|
d.inspectTrigger.Trigger()
|
||||||
|
|
|
@ -40,7 +40,7 @@ func (d *Deployment) createInitialMembers(apiObject *api.ArangoDeployment) error
|
||||||
|
|
||||||
// Go over all groups and create members
|
// Go over all groups and create members
|
||||||
if err := apiObject.ForeachServerGroup(func(group api.ServerGroup, spec api.ServerGroupSpec, status *api.MemberStatusList) error {
|
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 {
|
if err := d.createMember(group, apiObject); err != nil {
|
||||||
return maskAny(err)
|
return maskAny(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,13 +69,13 @@ func createPlan(log zerolog.Logger, currentPlan api.Plan, spec api.DeploymentSpe
|
||||||
// Never scale down
|
// Never scale down
|
||||||
case api.DeploymentModeResilientSingle:
|
case api.DeploymentModeResilientSingle:
|
||||||
// Only scale singles
|
// 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:
|
case api.DeploymentModeCluster:
|
||||||
// Scale dbservers, coordinators, syncmasters & syncworkers
|
// 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.DBServers, api.ServerGroupDBServers, spec.DBServers.GetCount())...)
|
||||||
plan = append(plan, createScalePlan(log, status.Members.Coordinators, api.ServerGroupCoordinators, spec.Coordinators.Count)...)
|
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.Count)...)
|
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.Count)...)
|
plan = append(plan, createScalePlan(log, status.Members.SyncWorkers, api.ServerGroupSyncWorkers, spec.SyncWorkers.GetCount())...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return plan
|
// Return plan
|
||||||
|
|
|
@ -139,7 +139,7 @@ func createArangodArgs(apiObject metav1.Object, deplSpec api.DeploymentSpec, gro
|
||||||
optionPair{"--cluster.my-id", id},
|
optionPair{"--cluster.my-id", id},
|
||||||
optionPair{"--agency.activate", "true"},
|
optionPair{"--agency.activate", "true"},
|
||||||
optionPair{"--agency.my-address", myTCPURL},
|
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{"--agency.supervision", "true"},
|
||||||
optionPair{"--foxx.queues", "false"},
|
optionPair{"--foxx.queues", "false"},
|
||||||
optionPair{"--server.statistics", "false"},
|
optionPair{"--server.statistics", "false"},
|
||||||
|
@ -242,7 +242,7 @@ func (d *Deployment) createLivenessProbe(apiObject *api.ArangoDeployment, group
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case api.ServerGroupSyncMasters, api.ServerGroupSyncWorkers:
|
case api.ServerGroupSyncMasters, api.ServerGroupSyncWorkers:
|
||||||
authorization := ""
|
authorization := ""
|
||||||
if apiObject.Spec.Sync.Monitoring.TokenSecretName != "" {
|
if apiObject.Spec.Sync.Monitoring.GetTokenSecretName() != "" {
|
||||||
// Use monitoring token
|
// Use monitoring token
|
||||||
token, err := d.getSyncMonitoringToken(apiObject)
|
token, err := d.getSyncMonitoringToken(apiObject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -344,14 +344,14 @@ func (d *Deployment) ensurePods(apiObject *api.ArangoDeployment) error {
|
||||||
}
|
}
|
||||||
rocksdbEncryptionSecretName := ""
|
rocksdbEncryptionSecretName := ""
|
||||||
if apiObject.Spec.RocksDB.IsEncrypted() {
|
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 {
|
if err := k8sutil.ValidateEncryptionKeySecret(kubecli.CoreV1(), rocksdbEncryptionSecretName, ns); err != nil {
|
||||||
return maskAny(errors.Wrapf(err, "RocksDB encryption key secret validation failed"))
|
return maskAny(errors.Wrapf(err, "RocksDB encryption key secret validation failed"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if apiObject.Spec.IsAuthenticated() {
|
if apiObject.Spec.IsAuthenticated() {
|
||||||
env[constants.EnvArangodJWTSecret] = k8sutil.EnvValue{
|
env[constants.EnvArangodJWTSecret] = k8sutil.EnvValue{
|
||||||
SecretName: apiObject.Spec.Authentication.JWTSecretName,
|
SecretName: apiObject.Spec.Authentication.GetJWTSecretName(),
|
||||||
SecretKey: constants.SecretKeyJWT,
|
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 {
|
if err := apiObject.ForeachServerGroup(func(group api.ServerGroup, spec api.ServerGroupSpec, status *api.MemberStatusList) error {
|
||||||
for _, m := range *status {
|
for _, m := range *status {
|
||||||
if m.PersistentVolumeClaimName != "" {
|
if m.PersistentVolumeClaimName != "" {
|
||||||
storageClassName := spec.StorageClassName
|
storageClassName := spec.GetStorageClassName()
|
||||||
role := group.AsRole()
|
role := group.AsRole()
|
||||||
resources := spec.Resources
|
resources := spec.Resources
|
||||||
if err := k8sutil.CreatePersistentVolumeClaim(kubecli, m.PersistentVolumeClaimName, deploymentName, ns, storageClassName, role, resources, owner); err != nil {
|
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
|
// createSecrets creates all secrets needed to run the given deployment
|
||||||
func (d *Deployment) createSecrets(apiObject *api.ArangoDeployment) error {
|
func (d *Deployment) createSecrets(apiObject *api.ArangoDeployment) error {
|
||||||
if apiObject.Spec.IsAuthenticated() {
|
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)
|
return maskAny(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ func (d *Deployment) ensureJWTSecret(secretName string) error {
|
||||||
func (d *Deployment) ensureCACertificateSecret(spec api.TLSSpec) error {
|
func (d *Deployment) ensureCACertificateSecret(spec api.TLSSpec) error {
|
||||||
kubecli := d.deps.KubeCli
|
kubecli := d.deps.KubeCli
|
||||||
ns := d.apiObject.GetNamespace()
|
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
|
// Secret not found, create it
|
||||||
owner := d.apiObject.AsOwner()
|
owner := d.apiObject.AsOwner()
|
||||||
deploymentName := d.apiObject.GetName()
|
deploymentName := d.apiObject.GetName()
|
||||||
|
@ -112,7 +112,7 @@ func (d *Deployment) getJWTSecret(apiObject *api.ArangoDeployment) (string, erro
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
kubecli := d.deps.KubeCli
|
kubecli := d.deps.KubeCli
|
||||||
secretName := apiObject.Spec.Authentication.JWTSecretName
|
secretName := apiObject.Spec.Authentication.GetJWTSecretName()
|
||||||
s, err := k8sutil.GetJWTSecret(kubecli.CoreV1(), secretName, apiObject.GetNamespace())
|
s, err := k8sutil.GetJWTSecret(kubecli.CoreV1(), secretName, apiObject.GetNamespace())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.deps.Log.Debug().Err(err).Str("secret-name", secretName).Msg("Failed to get JWT secret")
|
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.
|
// 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) {
|
func (d *Deployment) getSyncJWTSecret(apiObject *api.ArangoDeployment) (string, error) {
|
||||||
kubecli := d.deps.KubeCli
|
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())
|
s, err := k8sutil.GetJWTSecret(kubecli.CoreV1(), secretName, apiObject.GetNamespace())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.deps.Log.Debug().Err(err).Str("secret-name", secretName).Msg("Failed to get sync JWT secret")
|
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.
|
// getSyncMonitoringToken loads the token secret used for monitoring sync masters & workers.
|
||||||
func (d *Deployment) getSyncMonitoringToken(apiObject *api.ArangoDeployment) (string, error) {
|
func (d *Deployment) getSyncMonitoringToken(apiObject *api.ArangoDeployment) (string, error) {
|
||||||
kubecli := d.deps.KubeCli
|
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{})
|
s, err := kubecli.CoreV1().Secrets(apiObject.GetNamespace()).Get(secretName, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.deps.Log.Debug().Err(err).Str("secret-name", secretName).Msg("Failed to get monitoring token secret")
|
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
|
// createCACertificate creates a CA certificate and stores it in a secret with name
|
||||||
// specified in the given spec.
|
// specified in the given spec.
|
||||||
func createCACertificate(log zerolog.Logger, cli v1.CoreV1Interface, spec api.TLSSpec, deploymentName, namespace string, ownerRef *metav1.OwnerReference) error {
|
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()
|
log = log.With().Str("secret", spec.GetCASecretName()).Logger()
|
||||||
dnsNames, ipAddresses, emailAddress, err := spec.GetAltNames()
|
dnsNames, ipAddresses, emailAddress, err := spec.GetParsedAltNames()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug().Err(err).Msg("Failed to get alternate names")
|
log.Debug().Err(err).Msg("Failed to get alternate names")
|
||||||
return maskAny(err)
|
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")
|
log.Debug().Err(err).Msg("Failed to create CA certificate")
|
||||||
return maskAny(err)
|
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) {
|
if k8sutil.IsAlreadyExists(err) {
|
||||||
log.Debug().Msg("CA Secret already exists")
|
log.Debug().Msg("CA Secret already exists")
|
||||||
} else {
|
} 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 {
|
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()
|
log = log.With().Str("secret", secretName).Logger()
|
||||||
// Load alt names
|
// Load alt names
|
||||||
dnsNames, ipAddresses, emailAddress, err := spec.GetAltNames()
|
dnsNames, ipAddresses, emailAddress, err := spec.GetParsedAltNames()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug().Err(err).Msg("Failed to get alternate names")
|
log.Debug().Err(err).Msg("Failed to get alternate names")
|
||||||
return maskAny(err)
|
return maskAny(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load CA certificate
|
// Load CA certificate
|
||||||
caCert, caKey, err := k8sutil.GetCASecret(cli, spec.CASecretName, namespace)
|
caCert, caKey, err := k8sutil.GetCASecret(cli, spec.GetCASecretName(), namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug().Err(err).Msg("Failed to load CA certificate")
|
log.Debug().Err(err).Msg("Failed to load CA certificate")
|
||||||
return maskAny(err)
|
return maskAny(err)
|
||||||
|
@ -105,7 +105,7 @@ func createServerCertificate(log zerolog.Logger, cli v1.CoreV1Interface, serverN
|
||||||
Hosts: append(append(serverNames, dnsNames...), ipAddresses...),
|
Hosts: append(append(serverNames, dnsNames...), ipAddresses...),
|
||||||
EmailAddresses: emailAddress,
|
EmailAddresses: emailAddress,
|
||||||
ValidFrom: time.Now(),
|
ValidFrom: time.Now(),
|
||||||
ValidFor: spec.TTL,
|
ValidFor: spec.GetTTL(),
|
||||||
IsCA: false,
|
IsCA: false,
|
||||||
ECDSACurve: tlsECDSACurve,
|
ECDSACurve: tlsECDSACurve,
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,7 +146,7 @@ func createArangodClientForDNSName(ctx context.Context, cli corev1.CoreV1Interfa
|
||||||
// Authentication is enabled.
|
// Authentication is enabled.
|
||||||
// Should we skip using it?
|
// Should we skip using it?
|
||||||
if ctx.Value(skipAuthenticationKey{}) == nil {
|
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 {
|
if err != nil {
|
||||||
return nil, maskAny(err)
|
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