From a699c1a216a215ba8967b4f8ddc366b1f234856c Mon Sep 17 00:00:00 2001 From: lamai93 Date: Mon, 14 Jan 2019 14:30:39 +0100 Subject: [PATCH 1/9] Randomize root password or use provided secret. --- pkg/apis/deployment/v1alpha/conditions.go | 2 + .../deployment/v1alpha/deployment_spec.go | 16 +++ .../v1alpha/zz_generated.deepcopy.go | 5 + pkg/deployment/bootstrap.go | 129 ++++++++++++++++++ pkg/deployment/deployment_inspector.go | 6 + pkg/util/k8sutil/secrets.go | 22 +++ 6 files changed, 180 insertions(+) create mode 100644 pkg/deployment/bootstrap.go diff --git a/pkg/apis/deployment/v1alpha/conditions.go b/pkg/apis/deployment/v1alpha/conditions.go index c15698ad6..5e784506a 100644 --- a/pkg/apis/deployment/v1alpha/conditions.go +++ b/pkg/apis/deployment/v1alpha/conditions.go @@ -53,6 +53,8 @@ const ( ConditionTypeSecretsChanged ConditionType = "SecretsChanged" // ConditionTypeMemberOfCluster indicates that the member is a known member of the ArangoDB cluster. ConditionTypeMemberOfCluster ConditionType = "MemberOfCluster" + // ConditionTypeBoostrapCompleted indicates that the initial cluster boostrap has been completed. + ConditionTypeBoostrapCompleted ConditionType = "BootstrapCompleted" ) // Condition represents one current condition of a deployment or deployment member. diff --git a/pkg/apis/deployment/v1alpha/deployment_spec.go b/pkg/apis/deployment/v1alpha/deployment_spec.go index d36c8b568..e2659188c 100644 --- a/pkg/apis/deployment/v1alpha/deployment_spec.go +++ b/pkg/apis/deployment/v1alpha/deployment_spec.go @@ -26,6 +26,7 @@ import ( "reflect" "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" "github.com/pkg/errors" "k8s.io/api/core/v1" ) @@ -70,6 +71,8 @@ type DeploymentSpec struct { SyncWorkers ServerGroupSpec `json:"syncworkers"` Chaos ChaosSpec `json:"chaos"` + + RootUserAccessSecretName *string `json:"rootUserAccessSecretName,omitempty"` } // Equal compares two DeploymentSpec @@ -130,6 +133,10 @@ func (s DeploymentSpec) IsSecure() bool { return s.TLS.IsSecure() } +func (s DeploymentSpec) GetRootUserAccessSecretName() string { + return util.StringOrDefault(s.RootUserAccessSecretName) +} + // GetServerGroupSpec returns the server group spec (from this // deployment spec) for the given group. func (s DeploymentSpec) GetServerGroupSpec(group ServerGroup) ServerGroupSpec { @@ -180,6 +187,9 @@ func (s *DeploymentSpec) SetDefaults(deploymentName string) { s.SyncMasters.SetDefaults(ServerGroupSyncMasters, s.Sync.IsEnabled(), s.GetMode()) s.SyncWorkers.SetDefaults(ServerGroupSyncWorkers, s.Sync.IsEnabled(), s.GetMode()) s.Chaos.SetDefaults() + if s.GetRootUserAccessSecretName() == "" { + s.RootUserAccessSecretName = util.NewString(deploymentName + "-root-access") + } } // SetDefaultsFrom fills unspecified fields with a value from given source spec. @@ -205,6 +215,9 @@ func (s *DeploymentSpec) SetDefaultsFrom(source DeploymentSpec) { if s.DisableIPv6 == nil { s.DisableIPv6 = util.NewBoolOrNil(source.DisableIPv6) } + if s.RootUserAccessSecretName == nil { + s.RootUserAccessSecretName = util.NewStringOrNil(source.RootUserAccessSecretName) + } s.License.SetDefaultsFrom(source.License) s.ExternalAccess.SetDefaultsFrom(source.ExternalAccess) s.RocksDB.SetDefaultsFrom(source.RocksDB) @@ -277,6 +290,9 @@ func (s *DeploymentSpec) Validate() error { if err := s.License.Validate(); err != nil { return maskAny(errors.Wrap(err, "spec.licenseKey")) } + if err := k8sutil.ValidateResourceName(s.GetRootUserAccessSecretName()); err != nil { + return maskAny(err) + } return nil } diff --git a/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go b/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go index 666fb48cc..eacadb77f 100644 --- a/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go +++ b/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go @@ -257,6 +257,11 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) { in.SyncMasters.DeepCopyInto(&out.SyncMasters) in.SyncWorkers.DeepCopyInto(&out.SyncWorkers) in.Chaos.DeepCopyInto(&out.Chaos) + if in.RootUserAccessSecretName != nil { + in, out := &in.RootUserAccessSecretName, &out.RootUserAccessSecretName + *out = new(string) + **out = **in + } return } diff --git a/pkg/deployment/bootstrap.go b/pkg/deployment/bootstrap.go new file mode 100644 index 000000000..22fe2c46b --- /dev/null +++ b/pkg/deployment/bootstrap.go @@ -0,0 +1,129 @@ +// +// 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 deployment + +import ( + "crypto/rand" + "encoding/hex" + "fmt" + + api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + driver "github.com/arangodb/go-driver" + + "github.com/arangodb/kube-arangodb/pkg/util/constants" +) + +const ( + rootUserName = "root" +) + +// EnsureBootstrap executes the bootstrap once as soon as the deployment becomes ready +func (d *Deployment) EnsureBootstrap() error { + + status, version := d.GetStatus() + + if status.Conditions.IsTrue(api.ConditionTypeReady) { + if status.Conditions.IsTrue(api.ConditionTypeBoostrapCompleted) { + return nil // Nothing to do, already bootstrapped + } + + d.deps.Log.Info().Msgf("Bootstrap deployment %s", d.Name()) + err := d.runBootstrap() + if err != nil { + status.Conditions.Update(api.ConditionTypeBoostrapCompleted, true, "Bootstrap failed", err.Error()) + } else { + status.Conditions.Update(api.ConditionTypeBoostrapCompleted, true, "Bootstrap successful", "The bootstrap process has been completed") + } + + if err = d.UpdateStatus(status, version); err != nil { + return maskAny(err) + } + + d.deps.Log.Info().Msgf("Bootstrap completed for %s", d.Name()) + } + + return nil +} + +func (d *Deployment) ensureRootUserPassword() (string, error) { + + spec := d.GetSpec() + secrets := d.GetKubeCli().CoreV1().Secrets(d.Namespace()) + if auth, err := secrets.Get(spec.GetRootUserAccessSecretName(), metav1.GetOptions{}); k8sutil.IsNotFound(err) { + // Create new one + tokenData := make([]byte, 32) + rand.Read(tokenData) + token := hex.EncodeToString(tokenData) + owner := d.GetAPIObject().AsOwner() + + if err := k8sutil.CreateBasicAuthSecret(secrets, spec.GetRootUserAccessSecretName(), rootUserName, token, &owner); err != nil { + return "", err + } + + return token, nil + } else if err == nil { + user, ok := auth.Data[constants.SecretUsername] + if ok && string(user) == rootUserName { + pass, ok := auth.Data[constants.SecretPassword] + if ok { + return string(pass), nil + } + } + return "", fmt.Errorf("invalid secret format") + } else { + return "", err + } +} + +func (d *Deployment) runBootstrap() error { + + // execute the boostrap code + // make sure that the bootstrap code is idempotent + client, err := d.clientCache.GetDatabase(nil) + if err != nil { + return maskAny(err) + } + + password, err := d.ensureRootUserPassword() + if err != nil { + return maskAny(err) + } + + // Obtain the root user + root, err := client.User(nil, rootUserName) + if err != nil { + return maskAny(err) + } + + err = root.Update(nil, driver.UserOptions{ + Password: password, + }) + if err != nil { + return maskAny(err) + } + + return nil +} diff --git a/pkg/deployment/deployment_inspector.go b/pkg/deployment/deployment_inspector.go index e795ac845..af2ef9ed8 100644 --- a/pkg/deployment/deployment_inspector.go +++ b/pkg/deployment/deployment_inspector.go @@ -161,6 +161,12 @@ func (d *Deployment) inspectDeployment(lastInterval util.Interval) util.Interval d.CreateEvent(k8sutil.NewErrorEvent("AccessPackage creation failed", err, d.apiObject)) } + // Ensure deployment bootstrap + if err := d.EnsureBootstrap(); err != nil { + hasError = true + d.CreateEvent(k8sutil.NewErrorEvent("Bootstrap failed", err, d.apiObject)) + } + // Inspect deployment for obsolete members if err := d.resources.CleanupRemovedMembers(); err != nil { hasError = true diff --git a/pkg/util/k8sutil/secrets.go b/pkg/util/k8sutil/secrets.go index 6c32f837a..2c8b94769 100644 --- a/pkg/util/k8sutil/secrets.go +++ b/pkg/util/k8sutil/secrets.go @@ -248,6 +248,28 @@ func CreateTokenSecret(secrets SecretInterface, secretName, token string, ownerR return nil } +// CreateBasicAuthSecret creates a secret with given name in given namespace +// with a given username and password as value. +func CreateBasicAuthSecret(secrets SecretInterface, secretName, username, password string, ownerRef *metav1.OwnerReference) error { + // Create secret + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + }, + Data: map[string][]byte{ + constants.SecretUsername: []byte(username), + constants.SecretPassword: []byte(password), + }, + } + // Attach secret to owner + addOwnerRefToObject(secret, ownerRef) + if _, err := secrets.Create(secret); err != nil { + // Failed to create secret + return maskAny(err) + } + return nil +} + // GetBasicAuthSecret loads a secret with given name in the given namespace // and extracts the `username` & `password` field. // If the secret does not exists or one of the fields is missing, From db386d9c6999a546dabdb8ca9d76725b5f2e6d8f Mon Sep 17 00:00:00 2001 From: lamai93 Date: Tue, 15 Jan 2019 10:36:15 +0100 Subject: [PATCH 2/9] Only bootstrap new deployments. --- pkg/deployment/bootstrap.go | 11 ++++++++--- pkg/deployment/resources/pod_inspector.go | 4 ++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pkg/deployment/bootstrap.go b/pkg/deployment/bootstrap.go index 22fe2c46b..a83eeb979 100644 --- a/pkg/deployment/bootstrap.go +++ b/pkg/deployment/bootstrap.go @@ -46,6 +46,10 @@ func (d *Deployment) EnsureBootstrap() error { status, version := d.GetStatus() if status.Conditions.IsTrue(api.ConditionTypeReady) { + if _, hasBootstrap := status.Conditions.Get(api.ConditionTypeBoostrapCompleted); !hasBootstrap { + return nil // The cluster was not initialised with ConditionTypeBoostrapCompleted == false + } + if status.Conditions.IsTrue(api.ConditionTypeBoostrapCompleted) { return nil // Nothing to do, already bootstrapped } @@ -68,6 +72,7 @@ func (d *Deployment) EnsureBootstrap() error { return nil } +// ensureRootUserPassword ensures the root user secret and returns the password specified or generated func (d *Deployment) ensureRootUserPassword() (string, error) { spec := d.GetSpec() @@ -98,6 +103,7 @@ func (d *Deployment) ensureRootUserPassword() (string, error) { } } +// runBootstrap is run for a deployment once func (d *Deployment) runBootstrap() error { // execute the boostrap code @@ -118,10 +124,9 @@ func (d *Deployment) runBootstrap() error { return maskAny(err) } - err = root.Update(nil, driver.UserOptions{ + if err = root.Update(nil, driver.UserOptions{ Password: password, - }) - if err != nil { + }); err != nil { return maskAny(err) } diff --git a/pkg/deployment/resources/pod_inspector.go b/pkg/deployment/resources/pod_inspector.go index 44ef74f0b..eb7c834d5 100644 --- a/pkg/deployment/resources/pod_inspector.go +++ b/pkg/deployment/resources/pod_inspector.go @@ -233,6 +233,10 @@ func (r *Resources) InspectPods(ctx context.Context) (util.Interval, error) { }) // Update overall conditions + if _, hasReady := status.Conditions.Get(api.ConditionTypeReady); !hasReady { + // Ready was never set, set BootstrapComplete to false + status.Conditions.Update(api.ConditionTypeBoostrapCompleted, false, "Bootstrap waiting", "Waiting for deployment") + } spec := r.context.GetSpec() allMembersReady := status.Members.AllMembersReady(spec.GetMode(), spec.Sync.IsEnabled()) status.Conditions.Update(api.ConditionTypeReady, allMembersReady, "", "") From 546444b640193cbe53addeb4e0c8add08a7e52a2 Mon Sep 17 00:00:00 2001 From: lamai93 Date: Tue, 15 Jan 2019 16:29:26 +0100 Subject: [PATCH 3/9] Reworked bootstrap mechanics. --- pkg/apis/deployment/v1alpha/bootstrap.go | 123 ++++++++++++++++++ .../deployment/v1alpha/deployment_spec.go | 17 +-- .../v1alpha/zz_generated.deepcopy.go | 51 +++++++- pkg/deployment/bootstrap.go | 55 +++++--- 4 files changed, 208 insertions(+), 38 deletions(-) create mode 100644 pkg/apis/deployment/v1alpha/bootstrap.go diff --git a/pkg/apis/deployment/v1alpha/bootstrap.go b/pkg/apis/deployment/v1alpha/bootstrap.go new file mode 100644 index 000000000..1d0057012 --- /dev/null +++ b/pkg/apis/deployment/v1alpha/bootstrap.go @@ -0,0 +1,123 @@ +// +// 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 +// + +package v1alpha + +import ( + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" +) + +const ( + // UserNameRoot root user name + UserNameRoot = "root" +) + +const ( + // PasswordSecretNameNone is magic value for no action + PasswordSecretNameNone = "None" + // PasswordSecretNameAuto is magic value for autogenerate name + PasswordSecretNameAuto = "Auto" +) + +// PasswordSecretNameList is a map from username to secretnames +type PasswordSecretNameList map[string]string + +// BootstrapSpec contains information for cluster bootstrapping +type BootstrapSpec struct { + // PasswordSecretNames contains a map of username to password-secret-name + PasswordSecretNames PasswordSecretNameList `json:"passwordSecretNames,omitempty"` +} + +// GetSecretName returns the secret name given by the specs. Or None if not set. +// Except for root user the default is Auto. +func (s PasswordSecretNameList) GetSecretName(user string) string { + if s != nil { + if password, ok := s[user]; ok { + return password + } + } + return "" +} + +// getSecretNameForUserPassword returns the default secret name for the given user +func getSecretNameForUserPassword(deploymentname, username string) string { + return deploymentname + "-" + username + "-password" +} + +// Validate the specification. +func (b *BootstrapSpec) Validate() error { + for _, secretname := range b.PasswordSecretNames { + if secretname == PasswordSecretNameNone { + continue + } + if err := k8sutil.ValidateResourceName(secretname); err != nil { + return maskAny(err) + } + } + + return nil +} + +// SetDefaults fills in default values when a field is not specified. +func (b *BootstrapSpec) SetDefaults(deploymentname string) { + if b.PasswordSecretNames == nil { + b.PasswordSecretNames = map[string]string{ + UserNameRoot: getSecretNameForUserPassword(deploymentname, UserNameRoot), + } + } else { + // Check if root is specified + if secretname, ok := b.PasswordSecretNames[UserNameRoot]; ok { + if secretname == PasswordSecretNameAuto { + b.PasswordSecretNames[UserNameRoot] = getSecretNameForUserPassword(deploymentname, UserNameRoot) + } + } else { + // implicit default + b.PasswordSecretNames[UserNameRoot] = getSecretNameForUserPassword(deploymentname, UserNameRoot) + } + + // Now fill in values for all users + for user, secretname := range b.PasswordSecretNames { + if user != UserNameRoot { + if secretname == PasswordSecretNameAuto { + b.PasswordSecretNames[user] = getSecretNameForUserPassword(deploymentname, user) + } + } + } + } +} + +// NewPasswordSecretNameListOrNil returns nil if input is nil, otherwise returns a clone of the given value. +func NewPasswordSecretNameListOrNil(list PasswordSecretNameList) PasswordSecretNameList { + if list == nil { + return nil + } + var newList = make(PasswordSecretNameList) + for k, v := range list { + newList[k] = v + } + return newList +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (b *BootstrapSpec) SetDefaultsFrom(source BootstrapSpec) { + if b.PasswordSecretNames == nil { + b.PasswordSecretNames = NewPasswordSecretNameListOrNil(source.PasswordSecretNames) + } +} diff --git a/pkg/apis/deployment/v1alpha/deployment_spec.go b/pkg/apis/deployment/v1alpha/deployment_spec.go index e2659188c..65cee34c5 100644 --- a/pkg/apis/deployment/v1alpha/deployment_spec.go +++ b/pkg/apis/deployment/v1alpha/deployment_spec.go @@ -26,7 +26,6 @@ import ( "reflect" "github.com/arangodb/kube-arangodb/pkg/util" - "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" "github.com/pkg/errors" "k8s.io/api/core/v1" ) @@ -72,7 +71,7 @@ type DeploymentSpec struct { Chaos ChaosSpec `json:"chaos"` - RootUserAccessSecretName *string `json:"rootUserAccessSecretName,omitempty"` + Bootstrap BootstrapSpec `json:"bootstrap",omitempty` } // Equal compares two DeploymentSpec @@ -133,10 +132,6 @@ func (s DeploymentSpec) IsSecure() bool { return s.TLS.IsSecure() } -func (s DeploymentSpec) GetRootUserAccessSecretName() string { - return util.StringOrDefault(s.RootUserAccessSecretName) -} - // GetServerGroupSpec returns the server group spec (from this // deployment spec) for the given group. func (s DeploymentSpec) GetServerGroupSpec(group ServerGroup) ServerGroupSpec { @@ -187,9 +182,7 @@ func (s *DeploymentSpec) SetDefaults(deploymentName string) { s.SyncMasters.SetDefaults(ServerGroupSyncMasters, s.Sync.IsEnabled(), s.GetMode()) s.SyncWorkers.SetDefaults(ServerGroupSyncWorkers, s.Sync.IsEnabled(), s.GetMode()) s.Chaos.SetDefaults() - if s.GetRootUserAccessSecretName() == "" { - s.RootUserAccessSecretName = util.NewString(deploymentName + "-root-access") - } + s.Bootstrap.SetDefaults(deploymentName) } // SetDefaultsFrom fills unspecified fields with a value from given source spec. @@ -215,9 +208,6 @@ func (s *DeploymentSpec) SetDefaultsFrom(source DeploymentSpec) { if s.DisableIPv6 == nil { s.DisableIPv6 = util.NewBoolOrNil(source.DisableIPv6) } - if s.RootUserAccessSecretName == nil { - s.RootUserAccessSecretName = util.NewStringOrNil(source.RootUserAccessSecretName) - } s.License.SetDefaultsFrom(source.License) s.ExternalAccess.SetDefaultsFrom(source.ExternalAccess) s.RocksDB.SetDefaultsFrom(source.RocksDB) @@ -231,6 +221,7 @@ func (s *DeploymentSpec) SetDefaultsFrom(source DeploymentSpec) { s.SyncMasters.SetDefaultsFrom(source.SyncMasters) s.SyncWorkers.SetDefaultsFrom(source.SyncWorkers) s.Chaos.SetDefaultsFrom(source.Chaos) + s.Bootstrap.SetDefaultsFrom(source.Bootstrap) } // Validate the specification. @@ -290,7 +281,7 @@ func (s *DeploymentSpec) Validate() error { if err := s.License.Validate(); err != nil { return maskAny(errors.Wrap(err, "spec.licenseKey")) } - if err := k8sutil.ValidateResourceName(s.GetRootUserAccessSecretName()); err != nil { + if err := s.Bootstrap.Validate(); err != nil { return maskAny(err) } return nil diff --git a/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go b/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go index eacadb77f..cd54340d3 100644 --- a/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go +++ b/pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go @@ -135,6 +135,29 @@ func (in *AuthenticationSpec) DeepCopy() *AuthenticationSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BootstrapSpec) DeepCopyInto(out *BootstrapSpec) { + *out = *in + if in.PasswordSecretNames != nil { + in, out := &in.PasswordSecretNames, &out.PasswordSecretNames + *out = make(PasswordSecretNameList, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapSpec. +func (in *BootstrapSpec) DeepCopy() *BootstrapSpec { + if in == nil { + return nil + } + out := new(BootstrapSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ChaosSpec) DeepCopyInto(out *ChaosSpec) { *out = *in @@ -257,11 +280,7 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) { in.SyncMasters.DeepCopyInto(&out.SyncMasters) in.SyncWorkers.DeepCopyInto(&out.SyncWorkers) in.Chaos.DeepCopyInto(&out.Chaos) - if in.RootUserAccessSecretName != nil { - in, out := &in.RootUserAccessSecretName, &out.RootUserAccessSecretName - *out = new(string) - **out = **in - } + in.Bootstrap.DeepCopyInto(&out.Bootstrap) return } @@ -551,6 +570,28 @@ func (in *MonitoringSpec) DeepCopy() *MonitoringSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in PasswordSecretNameList) DeepCopyInto(out *PasswordSecretNameList) { + { + in := &in + *out = make(PasswordSecretNameList, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PasswordSecretNameList. +func (in PasswordSecretNameList) DeepCopy() PasswordSecretNameList { + if in == nil { + return nil + } + out := new(PasswordSecretNameList) + in.DeepCopyInto(out) + return *out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in Plan) DeepCopyInto(out *Plan) { { diff --git a/pkg/deployment/bootstrap.go b/pkg/deployment/bootstrap.go index a83eeb979..88ae38afb 100644 --- a/pkg/deployment/bootstrap.go +++ b/pkg/deployment/bootstrap.go @@ -73,61 +73,76 @@ func (d *Deployment) EnsureBootstrap() error { } // ensureRootUserPassword ensures the root user secret and returns the password specified or generated -func (d *Deployment) ensureRootUserPassword() (string, error) { +func (d *Deployment) ensureUserPasswordSecret(secrets k8sutil.SecretInterface, username, secretName string) (string, error) { - spec := d.GetSpec() - secrets := d.GetKubeCli().CoreV1().Secrets(d.Namespace()) - if auth, err := secrets.Get(spec.GetRootUserAccessSecretName(), metav1.GetOptions{}); k8sutil.IsNotFound(err) { + if auth, err := secrets.Get(secretName, metav1.GetOptions{}); k8sutil.IsNotFound(err) { // Create new one tokenData := make([]byte, 32) rand.Read(tokenData) token := hex.EncodeToString(tokenData) owner := d.GetAPIObject().AsOwner() - if err := k8sutil.CreateBasicAuthSecret(secrets, spec.GetRootUserAccessSecretName(), rootUserName, token, &owner); err != nil { + if err := k8sutil.CreateBasicAuthSecret(secrets, secretName, username, token, &owner); err != nil { return "", err } return token, nil } else if err == nil { user, ok := auth.Data[constants.SecretUsername] - if ok && string(user) == rootUserName { + if ok && string(user) == username { pass, ok := auth.Data[constants.SecretPassword] if ok { return string(pass), nil } } - return "", fmt.Errorf("invalid secret format") + return "", fmt.Errorf("invalid secret format in secret %s", secretName) } else { return "", err } } -// runBootstrap is run for a deployment once -func (d *Deployment) runBootstrap() error { +func (d *Deployment) bootstrapUserPassword(client driver.Client, secrets k8sutil.SecretInterface, username, secretname string) error { - // execute the boostrap code - // make sure that the bootstrap code is idempotent - client, err := d.clientCache.GetDatabase(nil) + d.deps.Log.Debug().Msgf("Bootstrapping user %s, secret %s", username, secretname) + + password, err := d.ensureUserPasswordSecret(secrets, username, secretname) if err != nil { return maskAny(err) } - password, err := d.ensureRootUserPassword() - if err != nil { + // Obtain the user + user, err := client.User(nil, username) + if driver.IsNotFound(err) { + _, err := client.CreateUser(nil, username, &driver.UserOptions{Password: password}) return maskAny(err) + } else if err == nil { + return maskAny(user.Update(nil, driver.UserOptions{ + Password: password, + })) } + return err +} + +// runBootstrap is run for a deployment once +func (d *Deployment) runBootstrap() error { - // Obtain the root user - root, err := client.User(nil, rootUserName) + // execute the boostrap code + // make sure that the bootstrap code is idempotent + client, err := d.clientCache.GetDatabase(nil) if err != nil { return maskAny(err) } - if err = root.Update(nil, driver.UserOptions{ - Password: password, - }); err != nil { - return maskAny(err) + spec := d.GetSpec() + secrets := d.GetKubeCli().CoreV1().Secrets(d.Namespace()) + + for user, secret := range spec.Bootstrap.PasswordSecretNames { + if secret == api.PasswordSecretNameNone { + continue + } + if err := d.bootstrapUserPassword(client, secrets, user, secret); err != nil { + return maskAny(err) + } } return nil From 8a550379deb228e6cdc9d11fb789ca0d7a19e9f0 Mon Sep 17 00:00:00 2001 From: lamai93 Date: Tue, 15 Jan 2019 16:36:01 +0100 Subject: [PATCH 4/9] Style. --- pkg/deployment/bootstrap.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/deployment/bootstrap.go b/pkg/deployment/bootstrap.go index 88ae38afb..142e9fbab 100644 --- a/pkg/deployment/bootstrap.go +++ b/pkg/deployment/bootstrap.go @@ -101,6 +101,7 @@ func (d *Deployment) ensureUserPasswordSecret(secrets k8sutil.SecretInterface, u } } +// bootstrapUserPassword loads the password for the given user and updates the password stored in the database func (d *Deployment) bootstrapUserPassword(client driver.Client, secrets k8sutil.SecretInterface, username, secretname string) error { d.deps.Log.Debug().Msgf("Bootstrapping user %s, secret %s", username, secretname) @@ -111,16 +112,16 @@ func (d *Deployment) bootstrapUserPassword(client driver.Client, secrets k8sutil } // Obtain the user - user, err := client.User(nil, username) - if driver.IsNotFound(err) { + if user, err := client.User(nil, username); driver.IsNotFound(err) { _, err := client.CreateUser(nil, username, &driver.UserOptions{Password: password}) return maskAny(err) } else if err == nil { return maskAny(user.Update(nil, driver.UserOptions{ Password: password, })) + } else { + return err } - return err } // runBootstrap is run for a deployment once From d0704593f6c6b0e04eb96b30cdc2552214bff828 Mon Sep 17 00:00:00 2001 From: lamai93 Date: Wed, 16 Jan 2019 09:56:55 +0100 Subject: [PATCH 5/9] Style. --- pkg/apis/deployment/v1alpha/bootstrap.go | 56 ++++++++++++++++-------- pkg/deployment/bootstrap.go | 4 +- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/pkg/apis/deployment/v1alpha/bootstrap.go b/pkg/apis/deployment/v1alpha/bootstrap.go index 1d0057012..4a889d9dc 100644 --- a/pkg/apis/deployment/v1alpha/bootstrap.go +++ b/pkg/apis/deployment/v1alpha/bootstrap.go @@ -29,15 +29,18 @@ const ( UserNameRoot = "root" ) +// PasswordSecretName contains user password secret name +type PasswordSecretName string + const ( // PasswordSecretNameNone is magic value for no action - PasswordSecretNameNone = "None" + PasswordSecretNameNone PasswordSecretName = "None" // PasswordSecretNameAuto is magic value for autogenerate name - PasswordSecretNameAuto = "Auto" + PasswordSecretNameAuto PasswordSecretName = "Auto" ) // PasswordSecretNameList is a map from username to secretnames -type PasswordSecretNameList map[string]string +type PasswordSecretNameList map[string]PasswordSecretName // BootstrapSpec contains information for cluster bootstrapping type BootstrapSpec struct { @@ -45,30 +48,47 @@ type BootstrapSpec struct { PasswordSecretNames PasswordSecretNameList `json:"passwordSecretNames,omitempty"` } +// IsNone returns ture if p is empty or None +func (p PasswordSecretName) IsNone() bool { + return p == PasswordSecretNameNone || p == "" +} + +// IsAuto returns ture if p is Auto +func (p PasswordSecretName) IsAuto() bool { + return p == PasswordSecretNameAuto +} + +// Validate validates the password secret name +func (p PasswordSecretName) Validate() error { + if !p.IsNone() { + if err := k8sutil.ValidateResourceName(string(p)); err != nil { + return maskAny(err) + } + } + + return nil +} + // GetSecretName returns the secret name given by the specs. Or None if not set. -// Except for root user the default is Auto. -func (s PasswordSecretNameList) GetSecretName(user string) string { +func (s PasswordSecretNameList) GetSecretName(user string) PasswordSecretName { if s != nil { - if password, ok := s[user]; ok { - return password + if secretname, ok := s[user]; ok { + return secretname } } - return "" + return PasswordSecretNameNone } // getSecretNameForUserPassword returns the default secret name for the given user -func getSecretNameForUserPassword(deploymentname, username string) string { - return deploymentname + "-" + username + "-password" +func getSecretNameForUserPassword(deploymentname, username string) PasswordSecretName { + return PasswordSecretName(deploymentname + "-" + username + "-password") } // Validate the specification. func (b *BootstrapSpec) Validate() error { for _, secretname := range b.PasswordSecretNames { - if secretname == PasswordSecretNameNone { - continue - } - if err := k8sutil.ValidateResourceName(secretname); err != nil { - return maskAny(err) + if err := secretname.Validate(); err != nil { + return err } } @@ -78,13 +98,13 @@ func (b *BootstrapSpec) Validate() error { // SetDefaults fills in default values when a field is not specified. func (b *BootstrapSpec) SetDefaults(deploymentname string) { if b.PasswordSecretNames == nil { - b.PasswordSecretNames = map[string]string{ + b.PasswordSecretNames = PasswordSecretNameList{ UserNameRoot: getSecretNameForUserPassword(deploymentname, UserNameRoot), } } else { // Check if root is specified if secretname, ok := b.PasswordSecretNames[UserNameRoot]; ok { - if secretname == PasswordSecretNameAuto { + if secretname.IsAuto() { b.PasswordSecretNames[UserNameRoot] = getSecretNameForUserPassword(deploymentname, UserNameRoot) } } else { @@ -95,7 +115,7 @@ func (b *BootstrapSpec) SetDefaults(deploymentname string) { // Now fill in values for all users for user, secretname := range b.PasswordSecretNames { if user != UserNameRoot { - if secretname == PasswordSecretNameAuto { + if secretname.IsAuto() { b.PasswordSecretNames[user] = getSecretNameForUserPassword(deploymentname, user) } } diff --git a/pkg/deployment/bootstrap.go b/pkg/deployment/bootstrap.go index 142e9fbab..5e628c780 100644 --- a/pkg/deployment/bootstrap.go +++ b/pkg/deployment/bootstrap.go @@ -138,10 +138,10 @@ func (d *Deployment) runBootstrap() error { secrets := d.GetKubeCli().CoreV1().Secrets(d.Namespace()) for user, secret := range spec.Bootstrap.PasswordSecretNames { - if secret == api.PasswordSecretNameNone { + if secret.IsNone() { continue } - if err := d.bootstrapUserPassword(client, secrets, user, secret); err != nil { + if err := d.bootstrapUserPassword(client, secrets, user, string(secret)); err != nil { return maskAny(err) } } From 81b1375408c0d7fa7d19d7bd300db5ded226fd6c Mon Sep 17 00:00:00 2001 From: lamai93 Date: Wed, 16 Jan 2019 10:13:55 +0100 Subject: [PATCH 6/9] Empty string is auto. --- pkg/apis/deployment/v1alpha/bootstrap.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/apis/deployment/v1alpha/bootstrap.go b/pkg/apis/deployment/v1alpha/bootstrap.go index 4a889d9dc..64f7e46f7 100644 --- a/pkg/apis/deployment/v1alpha/bootstrap.go +++ b/pkg/apis/deployment/v1alpha/bootstrap.go @@ -50,12 +50,12 @@ type BootstrapSpec struct { // IsNone returns ture if p is empty or None func (p PasswordSecretName) IsNone() bool { - return p == PasswordSecretNameNone || p == "" + return p == PasswordSecretNameNone } // IsAuto returns ture if p is Auto func (p PasswordSecretName) IsAuto() bool { - return p == PasswordSecretNameAuto + return p == PasswordSecretNameAuto || p == "" } // Validate validates the password secret name From 4eeaeafb2231a4d12375616bfefb544a609b94d1 Mon Sep 17 00:00:00 2001 From: lamai93 Date: Wed, 16 Jan 2019 11:06:30 +0100 Subject: [PATCH 7/9] Added documentation. Simplified code. --- .../Kubernetes/DeploymentResource.md | 40 +++++++++++++ pkg/apis/deployment/v1alpha/bootstrap.go | 58 ++++++++++--------- 2 files changed, 70 insertions(+), 28 deletions(-) diff --git a/docs/Manual/Deployment/Kubernetes/DeploymentResource.md b/docs/Manual/Deployment/Kubernetes/DeploymentResource.md index 7d11b0304..86c6a2d75 100644 --- a/docs/Manual/Deployment/Kubernetes/DeploymentResource.md +++ b/docs/Manual/Deployment/Kubernetes/DeploymentResource.md @@ -342,6 +342,46 @@ This setting specifies the name of a kubernetes `Secret` that contains the license key token used for enterprise images. This value is not used for the community edition. +### `spec.bootstrap.passwordSecretNames.root: string` + +This setting specifies a secret name for the credentials of the root user. + +When a deployment is created the operator will setup the root user account +according to the credentials given by the secret. If the secret doesn't exist +the operator creates a secret with a random password. + +There are two magic values for the secret name: +- `None` specifies no action. This disables root password randomization. (Thus the root password is empty - not recommended) +- `Auto` specifies automatic name generation, which is `-root-password`. This is the default value. + + ### `spec..count: number` This setting specifies the number of servers to start for the given group. diff --git a/pkg/apis/deployment/v1alpha/bootstrap.go b/pkg/apis/deployment/v1alpha/bootstrap.go index 64f7e46f7..760d9a529 100644 --- a/pkg/apis/deployment/v1alpha/bootstrap.go +++ b/pkg/apis/deployment/v1alpha/bootstrap.go @@ -21,6 +21,8 @@ package v1alpha import ( + "fmt" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" ) @@ -48,23 +50,18 @@ type BootstrapSpec struct { PasswordSecretNames PasswordSecretNameList `json:"passwordSecretNames,omitempty"` } -// IsNone returns ture if p is empty or None +// IsNone returns true if p is None func (p PasswordSecretName) IsNone() bool { return p == PasswordSecretNameNone } -// IsAuto returns ture if p is Auto +// IsAuto returns true if p is Auto or p is empty func (p PasswordSecretName) IsAuto() bool { return p == PasswordSecretNameAuto || p == "" } // Validate validates the password secret name func (p PasswordSecretName) Validate() error { - if !p.IsNone() { - if err := k8sutil.ValidateResourceName(string(p)); err != nil { - return maskAny(err) - } - } return nil } @@ -86,7 +83,21 @@ func getSecretNameForUserPassword(deploymentname, username string) PasswordSecre // Validate the specification. func (b *BootstrapSpec) Validate() error { - for _, secretname := range b.PasswordSecretNames { + for username, secretname := range b.PasswordSecretNames { + // Remove this restriction as soon as we can bootstrap databases + if username != UserNameRoot { + return fmt.Errorf("only username `root` allowed in passwordSecretNames") + } + + if secretname.IsNone() { + if username != UserNameRoot { + return fmt.Errorf("magic value None not allowed for %s", username) + } + } else { + if err := k8sutil.ValidateResourceName(string(secretname)); err != nil { + return maskAny(err) + } + } if err := secretname.Validate(); err != nil { return err } @@ -98,27 +109,18 @@ func (b *BootstrapSpec) Validate() error { // SetDefaults fills in default values when a field is not specified. func (b *BootstrapSpec) SetDefaults(deploymentname string) { if b.PasswordSecretNames == nil { - b.PasswordSecretNames = PasswordSecretNameList{ - UserNameRoot: getSecretNameForUserPassword(deploymentname, UserNameRoot), - } - } else { - // Check if root is specified - if secretname, ok := b.PasswordSecretNames[UserNameRoot]; ok { - if secretname.IsAuto() { - b.PasswordSecretNames[UserNameRoot] = getSecretNameForUserPassword(deploymentname, UserNameRoot) - } - } else { - // implicit default - b.PasswordSecretNames[UserNameRoot] = getSecretNameForUserPassword(deploymentname, UserNameRoot) - } + b.PasswordSecretNames = make(map[string]PasswordSecretName) + } - // Now fill in values for all users - for user, secretname := range b.PasswordSecretNames { - if user != UserNameRoot { - if secretname.IsAuto() { - b.PasswordSecretNames[user] = getSecretNameForUserPassword(deploymentname, user) - } - } + // If root is not set init with Auto + if _, ok := b.PasswordSecretNames[UserNameRoot]; !ok { + b.PasswordSecretNames[UserNameRoot] = PasswordSecretNameAuto + } + + // Replace Auto with generated secret name + for user, secretname := range b.PasswordSecretNames { + if secretname.IsAuto() { + b.PasswordSecretNames[user] = getSecretNameForUserPassword(deploymentname, user) } } } From ea5e3ff52a0497cb69acf11ffbbbd45f78913e0f Mon Sep 17 00:00:00 2001 From: lamai93 Date: Tue, 19 Mar 2019 10:11:26 +0100 Subject: [PATCH 8/9] Added BootstrapSucceeded condition. --- .../Kubernetes/DeploymentResource.md | 28 --------------- pkg/apis/deployment/v1alpha/bootstrap.go | 11 +----- pkg/apis/deployment/v1alpha/conditions.go | 6 ++-- pkg/deployment/bootstrap.go | 12 ++++--- pkg/deployment/resources/pod_inspector.go | 2 +- pkg/util/k8sutil/names.go | 35 +++++++++++++++++++ 6 files changed, 48 insertions(+), 46 deletions(-) diff --git a/docs/Manual/Deployment/Kubernetes/DeploymentResource.md b/docs/Manual/Deployment/Kubernetes/DeploymentResource.md index 78ec0baae..cb61b0c48 100644 --- a/docs/Manual/Deployment/Kubernetes/DeploymentResource.md +++ b/docs/Manual/Deployment/Kubernetes/DeploymentResource.md @@ -354,34 +354,6 @@ There are two magic values for the secret name: - `None` specifies no action. This disables root password randomization. (Thus the root password is empty - not recommended) - `Auto` specifies automatic name generation, which is `-root-password`. This is the default value. - ### `spec..count: number` This setting specifies the number of servers to start for the given group. diff --git a/pkg/apis/deployment/v1alpha/bootstrap.go b/pkg/apis/deployment/v1alpha/bootstrap.go index 760d9a529..ab530a605 100644 --- a/pkg/apis/deployment/v1alpha/bootstrap.go +++ b/pkg/apis/deployment/v1alpha/bootstrap.go @@ -60,12 +60,6 @@ func (p PasswordSecretName) IsAuto() bool { return p == PasswordSecretNameAuto || p == "" } -// Validate validates the password secret name -func (p PasswordSecretName) Validate() error { - - return nil -} - // GetSecretName returns the secret name given by the specs. Or None if not set. func (s PasswordSecretNameList) GetSecretName(user string) PasswordSecretName { if s != nil { @@ -78,7 +72,7 @@ func (s PasswordSecretNameList) GetSecretName(user string) PasswordSecretName { // getSecretNameForUserPassword returns the default secret name for the given user func getSecretNameForUserPassword(deploymentname, username string) PasswordSecretName { - return PasswordSecretName(deploymentname + "-" + username + "-password") + return PasswordSecretName(k8sutil.FixupResourceName(deploymentname + "-" + username + "-password")) } // Validate the specification. @@ -98,9 +92,6 @@ func (b *BootstrapSpec) Validate() error { return maskAny(err) } } - if err := secretname.Validate(); err != nil { - return err - } } return nil diff --git a/pkg/apis/deployment/v1alpha/conditions.go b/pkg/apis/deployment/v1alpha/conditions.go index 97cac209b..855f73050 100644 --- a/pkg/apis/deployment/v1alpha/conditions.go +++ b/pkg/apis/deployment/v1alpha/conditions.go @@ -53,8 +53,10 @@ const ( ConditionTypeSecretsChanged ConditionType = "SecretsChanged" // ConditionTypeMemberOfCluster indicates that the member is a known member of the ArangoDB cluster. ConditionTypeMemberOfCluster ConditionType = "MemberOfCluster" - // ConditionTypeBoostrapCompleted indicates that the initial cluster boostrap has been completed. - ConditionTypeBoostrapCompleted ConditionType = "BootstrapCompleted" + // ConditionTypeBootstrapCompleted indicates that the initial cluster bootstrap has been completed. + ConditionTypeBootstrapCompleted ConditionType = "BootstrapCompleted" + // ConditionTypeBootstrapSucceded indicates that the initial cluster bootstrap completed successfully. + ConditionTypeBootstrapSucceded ConditionType = "BootstrapSucceded" // ConditionTypeTerminating indicates that the member is terminating but not yet terminated. ConditionTypeTerminating ConditionType = "Terminating" ) diff --git a/pkg/deployment/bootstrap.go b/pkg/deployment/bootstrap.go index 5e628c780..c54615603 100644 --- a/pkg/deployment/bootstrap.go +++ b/pkg/deployment/bootstrap.go @@ -46,20 +46,22 @@ func (d *Deployment) EnsureBootstrap() error { status, version := d.GetStatus() if status.Conditions.IsTrue(api.ConditionTypeReady) { - if _, hasBootstrap := status.Conditions.Get(api.ConditionTypeBoostrapCompleted); !hasBootstrap { + if _, hasBootstrap := status.Conditions.Get(api.ConditionTypeBootstrapCompleted); !hasBootstrap { return nil // The cluster was not initialised with ConditionTypeBoostrapCompleted == false } - if status.Conditions.IsTrue(api.ConditionTypeBoostrapCompleted) { + if status.Conditions.IsTrue(api.ConditionTypeBootstrapCompleted) { return nil // Nothing to do, already bootstrapped } d.deps.Log.Info().Msgf("Bootstrap deployment %s", d.Name()) err := d.runBootstrap() if err != nil { - status.Conditions.Update(api.ConditionTypeBoostrapCompleted, true, "Bootstrap failed", err.Error()) + status.Conditions.Update(api.ConditionTypeBootstrapCompleted, true, "Bootstrap failed", err.Error()) + status.Conditions.Update(api.ConditionTypeBootstrapSucceded, false, "Bootstrap failed", err.Error()) } else { - status.Conditions.Update(api.ConditionTypeBoostrapCompleted, true, "Bootstrap successful", "The bootstrap process has been completed") + status.Conditions.Update(api.ConditionTypeBootstrapCompleted, true, "Bootstrap successful", "The bootstrap process has been completed successfully") + status.Conditions.Update(api.ConditionTypeBootstrapSucceded, true, "Bootstrap successful", "The bootstrap process has been completed successfully") } if err = d.UpdateStatus(status, version); err != nil { @@ -127,7 +129,7 @@ func (d *Deployment) bootstrapUserPassword(client driver.Client, secrets k8sutil // runBootstrap is run for a deployment once func (d *Deployment) runBootstrap() error { - // execute the boostrap code + // execute the bootstrap code // make sure that the bootstrap code is idempotent client, err := d.clientCache.GetDatabase(nil) if err != nil { diff --git a/pkg/deployment/resources/pod_inspector.go b/pkg/deployment/resources/pod_inspector.go index 4ce21c0ac..28f1dbeaa 100644 --- a/pkg/deployment/resources/pod_inspector.go +++ b/pkg/deployment/resources/pod_inspector.go @@ -241,7 +241,7 @@ func (r *Resources) InspectPods(ctx context.Context) (util.Interval, error) { // Update overall conditions if _, hasReady := status.Conditions.Get(api.ConditionTypeReady); !hasReady { // Ready was never set, set BootstrapComplete to false - status.Conditions.Update(api.ConditionTypeBoostrapCompleted, false, "Bootstrap waiting", "Waiting for deployment") + status.Conditions.Update(api.ConditionTypeBootstrapCompleted, false, "Bootstrap waiting", "Waiting for deployment") } spec := r.context.GetSpec() allMembersReady := status.Members.AllMembersReady(spec.GetMode(), spec.Sync.IsEnabled()) diff --git a/pkg/util/k8sutil/names.go b/pkg/util/k8sutil/names.go index 4a242fedc..b026667b9 100644 --- a/pkg/util/k8sutil/names.go +++ b/pkg/util/k8sutil/names.go @@ -23,9 +23,11 @@ package k8sutil import ( + "crypto/sha1" "fmt" "regexp" "strings" + "unicode" ) var ( @@ -67,3 +69,36 @@ func stripArangodPrefix(id string) string { } return id } + +// FixupResourceName ensures that the given name +// complies with kubernetes name requirements. +// If the name is to long or contains invalid characters, +// if will be adjusted and a hash with be added. +func FixupResourceName(name string, maxLength ...int) string { + maxLen := 63 + if len(maxLength) > 0 { + maxLen = maxLength[0] + } + sb := strings.Builder{} + needHash := len(name) > maxLen + for _, ch := range name { + if unicode.IsDigit(ch) || unicode.IsLower(ch) || ch == '-' { + sb.WriteRune(ch) + } else if unicode.IsUpper(ch) { + sb.WriteRune(unicode.ToLower(ch)) + needHash = true + } else { + needHash = true + } + } + result := sb.String() + if needHash { + hash := sha1.Sum([]byte(name)) + h := fmt.Sprintf("-%0x", hash[:3]) + if len(result)+len(h) > maxLen { + result = result[:maxLen-(len(h))] + } + result = result + h + } + return result +} From e58fb328e89d75116f0227d7031eaf022c9c3578 Mon Sep 17 00:00:00 2001 From: lamai93 Date: Wed, 20 Mar 2019 15:46:06 +0100 Subject: [PATCH 9/9] Changed default value for root password initialization to None. --- docs/Manual/Deployment/Kubernetes/DeploymentResource.md | 4 ++-- pkg/apis/deployment/v1alpha/bootstrap.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/Manual/Deployment/Kubernetes/DeploymentResource.md b/docs/Manual/Deployment/Kubernetes/DeploymentResource.md index cb61b0c48..66ec4ab3f 100644 --- a/docs/Manual/Deployment/Kubernetes/DeploymentResource.md +++ b/docs/Manual/Deployment/Kubernetes/DeploymentResource.md @@ -351,8 +351,8 @@ according to the credentials given by the secret. If the secret doesn't exist the operator creates a secret with a random password. There are two magic values for the secret name: -- `None` specifies no action. This disables root password randomization. (Thus the root password is empty - not recommended) -- `Auto` specifies automatic name generation, which is `-root-password`. This is the default value. +- `None` specifies no action. This disables root password randomization. This is the default value. (Thus the root password is empty - not recommended) +- `Auto` specifies automatic name generation, which is `-root-password`. ### `spec..count: number` diff --git a/pkg/apis/deployment/v1alpha/bootstrap.go b/pkg/apis/deployment/v1alpha/bootstrap.go index ab530a605..e5e0406a1 100644 --- a/pkg/apis/deployment/v1alpha/bootstrap.go +++ b/pkg/apis/deployment/v1alpha/bootstrap.go @@ -50,14 +50,14 @@ type BootstrapSpec struct { PasswordSecretNames PasswordSecretNameList `json:"passwordSecretNames,omitempty"` } -// IsNone returns true if p is None +// IsNone returns true if p is None or p is empty func (p PasswordSecretName) IsNone() bool { - return p == PasswordSecretNameNone + return p == PasswordSecretNameNone || p == "" } -// IsAuto returns true if p is Auto or p is empty +// IsAuto returns true if p is Auto func (p PasswordSecretName) IsAuto() bool { - return p == PasswordSecretNameAuto || p == "" + return p == PasswordSecretNameAuto } // GetSecretName returns the secret name given by the specs. Or None if not set.