diff --git a/CHANGELOG.md b/CHANGELOG.md index d7dfeadb7..80ddc7d55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - (Bugfix) Fix Networking Client - (Feature) ConfigMap Inspector - (Feature) Envoy Image Layer +- (Feature) Add ArangoDeployment ServerGroupType ## [1.2.42](https://github.com/arangodb/kube-arangodb/tree/1.2.42) (2024-07-23) - (Maintenance) Go 1.22.4 & Kubernetes 1.29.6 libraries diff --git a/pkg/apis/deployment/v1/server_group.go b/pkg/apis/deployment/v1/server_group.go index 38df8a5f3..b504f1cd9 100644 --- a/pkg/apis/deployment/v1/server_group.go +++ b/pkg/apis/deployment/v1/server_group.go @@ -122,6 +122,20 @@ var ( } ) +// Type returns the Group Type +func (g ServerGroup) Type() ServerGroupType { + switch g { + case ServerGroupAgents, ServerGroupSingle, ServerGroupDBServers, ServerGroupCoordinators: + return ServerGroupTypeArangoD + case ServerGroupImageDiscovery: + return ServerGroupTypeID + case ServerGroupSyncMasters, ServerGroupSyncWorkers: + return ServerGroupTypeArangoSync + default: + return ServerGroupTypeUnknown + } +} + // AsRole returns the "role" value for the given group. func (g ServerGroup) AsRole() string { switch g { diff --git a/pkg/apis/deployment/v1/server_group_spec.go b/pkg/apis/deployment/v1/server_group_spec.go index 2436465af..3442bb731 100644 --- a/pkg/apis/deployment/v1/server_group_spec.go +++ b/pkg/apis/deployment/v1/server_group_spec.go @@ -514,11 +514,12 @@ func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentM for _, arg := range s.Args { parts := strings.Split(arg, "=") optionKey := strings.TrimSpace(parts[0]) - if group.IsArangod() { + switch group.Type() { + case ServerGroupTypeArangoD: if arangodOptions.IsCriticalOption(optionKey) { return errors.WithStack(errors.Wrapf(ValidationError, "Critical option '%s' cannot be overriden", optionKey)) } - } else if group.IsArangosync() { + case ServerGroupTypeArangoSync: if arangosyncOptions.IsCriticalOption(optionKey) { return errors.WithStack(errors.Wrapf(ValidationError, "Critical option '%s' cannot be overriden", optionKey)) } diff --git a/pkg/apis/deployment/v1/server_group_test.go b/pkg/apis/deployment/v1/server_group_test.go index 9a7743992..ef2ddc8d9 100644 --- a/pkg/apis/deployment/v1/server_group_test.go +++ b/pkg/apis/deployment/v1/server_group_test.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2024 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. @@ -61,3 +61,14 @@ func TestServerGroupIsArangosync(t *testing.T) { assert.True(t, ServerGroupSyncMasters.IsArangosync()) assert.True(t, ServerGroupSyncWorkers.IsArangosync()) } + +func TestServerGroupType(t *testing.T) { + assert.Equal(t, ServerGroupTypeUnknown, ServerGroupUnknown.Type()) + assert.Equal(t, ServerGroupTypeID, ServerGroupImageDiscovery.Type()) + assert.Equal(t, ServerGroupTypeArangoD, ServerGroupSingle.Type()) + assert.Equal(t, ServerGroupTypeArangoD, ServerGroupAgents.Type()) + assert.Equal(t, ServerGroupTypeArangoD, ServerGroupDBServers.Type()) + assert.Equal(t, ServerGroupTypeArangoD, ServerGroupCoordinators.Type()) + assert.Equal(t, ServerGroupTypeArangoSync, ServerGroupSyncMasters.Type()) + assert.Equal(t, ServerGroupTypeArangoSync, ServerGroupSyncWorkers.Type()) +} diff --git a/pkg/apis/deployment/v1/server_group_type.go b/pkg/apis/deployment/v1/server_group_type.go new file mode 100644 index 000000000..1c3c24f5d --- /dev/null +++ b/pkg/apis/deployment/v1/server_group_type.go @@ -0,0 +1,30 @@ +// +// DISCLAIMER +// +// Copyright 2024 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 v1 + +type ServerGroupType int + +const ( + ServerGroupTypeUnknown ServerGroupType = iota + ServerGroupTypeArangoD + ServerGroupTypeArangoSync + ServerGroupTypeID +) diff --git a/pkg/apis/deployment/v2alpha1/server_group.go b/pkg/apis/deployment/v2alpha1/server_group.go index be7f1aca6..bc3c2be6c 100644 --- a/pkg/apis/deployment/v2alpha1/server_group.go +++ b/pkg/apis/deployment/v2alpha1/server_group.go @@ -122,6 +122,20 @@ var ( } ) +// Type returns the Group Type +func (g ServerGroup) Type() ServerGroupType { + switch g { + case ServerGroupAgents, ServerGroupSingle, ServerGroupDBServers, ServerGroupCoordinators: + return ServerGroupTypeArangoD + case ServerGroupImageDiscovery: + return ServerGroupTypeID + case ServerGroupSyncMasters, ServerGroupSyncWorkers: + return ServerGroupTypeArangoSync + default: + return ServerGroupTypeUnknown + } +} + // AsRole returns the "role" value for the given group. func (g ServerGroup) AsRole() string { switch g { diff --git a/pkg/apis/deployment/v2alpha1/server_group_spec.go b/pkg/apis/deployment/v2alpha1/server_group_spec.go index 0f62fcfe5..8579e051c 100644 --- a/pkg/apis/deployment/v2alpha1/server_group_spec.go +++ b/pkg/apis/deployment/v2alpha1/server_group_spec.go @@ -514,11 +514,12 @@ func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentM for _, arg := range s.Args { parts := strings.Split(arg, "=") optionKey := strings.TrimSpace(parts[0]) - if group.IsArangod() { + switch group.Type() { + case ServerGroupTypeArangoD: if arangodOptions.IsCriticalOption(optionKey) { return errors.WithStack(errors.Wrapf(ValidationError, "Critical option '%s' cannot be overriden", optionKey)) } - } else if group.IsArangosync() { + case ServerGroupTypeArangoSync: if arangosyncOptions.IsCriticalOption(optionKey) { return errors.WithStack(errors.Wrapf(ValidationError, "Critical option '%s' cannot be overriden", optionKey)) } diff --git a/pkg/apis/deployment/v2alpha1/server_group_test.go b/pkg/apis/deployment/v2alpha1/server_group_test.go index b3e370b53..03eae5314 100644 --- a/pkg/apis/deployment/v2alpha1/server_group_test.go +++ b/pkg/apis/deployment/v2alpha1/server_group_test.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2024 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. @@ -61,3 +61,14 @@ func TestServerGroupIsArangosync(t *testing.T) { assert.True(t, ServerGroupSyncMasters.IsArangosync()) assert.True(t, ServerGroupSyncWorkers.IsArangosync()) } + +func TestServerGroupType(t *testing.T) { + assert.Equal(t, ServerGroupTypeUnknown, ServerGroupUnknown.Type()) + assert.Equal(t, ServerGroupTypeID, ServerGroupImageDiscovery.Type()) + assert.Equal(t, ServerGroupTypeArangoD, ServerGroupSingle.Type()) + assert.Equal(t, ServerGroupTypeArangoD, ServerGroupAgents.Type()) + assert.Equal(t, ServerGroupTypeArangoD, ServerGroupDBServers.Type()) + assert.Equal(t, ServerGroupTypeArangoD, ServerGroupCoordinators.Type()) + assert.Equal(t, ServerGroupTypeArangoSync, ServerGroupSyncMasters.Type()) + assert.Equal(t, ServerGroupTypeArangoSync, ServerGroupSyncWorkers.Type()) +} diff --git a/pkg/apis/deployment/v2alpha1/server_group_type.go b/pkg/apis/deployment/v2alpha1/server_group_type.go new file mode 100644 index 000000000..4b7c6c559 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/server_group_type.go @@ -0,0 +1,30 @@ +// +// DISCLAIMER +// +// Copyright 2024 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 v2alpha1 + +type ServerGroupType int + +const ( + ServerGroupTypeUnknown ServerGroupType = iota + ServerGroupTypeArangoD + ServerGroupTypeArangoSync + ServerGroupTypeID +) diff --git a/pkg/deployment/deployment_suite_test.go b/pkg/deployment/deployment_suite_test.go index 3c1ad9c1a..940280b27 100644 --- a/pkg/deployment/deployment_suite_test.go +++ b/pkg/deployment/deployment_suite_test.go @@ -107,12 +107,14 @@ func createTestTLSVolume(serverGroupString, ID string) core.Volume { } func createTestLifecycle(group api.ServerGroup) *core.Lifecycle { - if group.IsArangosync() { + switch group.Type() { + case api.ServerGroupTypeArangoSync: lifecycle, _ := k8sutil.NewLifecycleFinalizers() return lifecycle + default: + lifecycle, _ := k8sutil.NewLifecyclePort() + return lifecycle } - lifecycle, _ := k8sutil.NewLifecyclePort() - return lifecycle } func createTestToken(deployment *Deployment, testCase *testCaseStruct, paths []string) (string, error) { @@ -864,52 +866,51 @@ func podDataSort() func(t *testing.T, p *core.Pod) { func addLifecycle(name string, uuidRequired bool, license string, group api.ServerGroup) func(t *testing.T, p *core.Pod) { return func(t *testing.T, p *core.Pod) { - if group.IsArangosync() { - return - } + switch group.Type() { + case api.ServerGroupTypeArangoD: + if len(p.Spec.Containers) > 0 { + p.Spec.Containers[0].Env = append(k8sutil.GetLifecycleEnv(), p.Spec.Containers[0].Env...) + if license != "" { + p.Spec.Containers[0].Env = append([]core.EnvVar{ + k8sutil.CreateEnvSecretKeySelector(constants.EnvArangoLicenseKey, + license, constants.SecretKeyToken)}, p.Spec.Containers[0].Env...) + } + } - if len(p.Spec.Containers) > 0 { - p.Spec.Containers[0].Env = append(k8sutil.GetLifecycleEnv(), p.Spec.Containers[0].Env...) - if license != "" { - p.Spec.Containers[0].Env = append([]core.EnvVar{ - k8sutil.CreateEnvSecretKeySelector(constants.EnvArangoLicenseKey, - license, constants.SecretKeyToken)}, p.Spec.Containers[0].Env...) + if _, ok := k8sutil.GetAnyVolumeByName(p.Spec.Volumes, shared.LifecycleVolumeName); !ok { + p.Spec.Volumes = append([]core.Volume{k8sutil.LifecycleVolume()}, p.Spec.Volumes...) + } + if _, ok := k8sutil.GetAnyVolumeByName(p.Spec.Volumes, "arangod-data"); !ok { + p.Spec.Volumes = append([]core.Volume{k8sutil.LifecycleVolume()}, p.Spec.Volumes...) } - } - if _, ok := k8sutil.GetAnyVolumeByName(p.Spec.Volumes, shared.LifecycleVolumeName); !ok { - p.Spec.Volumes = append([]core.Volume{k8sutil.LifecycleVolume()}, p.Spec.Volumes...) - } - if _, ok := k8sutil.GetAnyVolumeByName(p.Spec.Volumes, "arangod-data"); !ok { - p.Spec.Volumes = append([]core.Volume{k8sutil.LifecycleVolume()}, p.Spec.Volumes...) - } + if len(p.Spec.Containers) > 0 { + p.Spec.Containers[0].Lifecycle = createTestLifecycle(api.ServerGroupAgents) + } - if len(p.Spec.Containers) > 0 { - p.Spec.Containers[0].Lifecycle = createTestLifecycle(api.ServerGroupAgents) - } + if len(p.Spec.Containers) > 0 { + if _, ok := k8sutil.GetAnyVolumeMountByName(p.Spec.Containers[0].VolumeMounts, "lifecycle"); !ok { + p.Spec.Containers[0].VolumeMounts = append(p.Spec.Containers[0].VolumeMounts, k8sutil.LifecycleVolumeMount()) + } - if len(p.Spec.Containers) > 0 { - if _, ok := k8sutil.GetAnyVolumeMountByName(p.Spec.Containers[0].VolumeMounts, "lifecycle"); !ok { - p.Spec.Containers[0].VolumeMounts = append(p.Spec.Containers[0].VolumeMounts, k8sutil.LifecycleVolumeMount()) + if _, ok := kresources.GetAnyContainerByName(p.Spec.InitContainers, "init-lifecycle"); !ok { + p.Spec.InitContainers = append( + []core.Container{createTestLifecycleContainer(emptyResources)}, + p.Spec.InitContainers..., + ) + } } - if _, ok := kresources.GetAnyContainerByName(p.Spec.InitContainers, "init-lifecycle"); !ok { + if _, ok := kresources.GetAnyContainerByName(p.Spec.InitContainers, "uuid"); !ok { + binaryPath, _ := os.Executable() p.Spec.InitContainers = append( - []core.Container{createTestLifecycleContainer(emptyResources)}, + []core.Container{ + k8sutil.ArangodInitContainer("uuid", name, "rocksdb", binaryPath, testImageOperator, uuidRequired, securityContext.NewSecurityContext()), + }, p.Spec.InitContainers..., ) } } - - if _, ok := kresources.GetAnyContainerByName(p.Spec.InitContainers, "uuid"); !ok { - binaryPath, _ := os.Executable() - p.Spec.InitContainers = append( - []core.Container{ - k8sutil.ArangodInitContainer("uuid", name, "rocksdb", binaryPath, testImageOperator, uuidRequired, securityContext.NewSecurityContext()), - }, - p.Spec.InitContainers..., - ) - } } } diff --git a/pkg/deployment/member/state.go b/pkg/deployment/member/state.go index 9bba04e97..0738a9441 100644 --- a/pkg/deployment/member/state.go +++ b/pkg/deployment/member/state.go @@ -34,6 +34,7 @@ import ( "github.com/arangodb/kube-arangodb/pkg/logging" "github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util/arangod" + "github.com/arangodb/kube-arangodb/pkg/util/assertion" "github.com/arangodb/kube-arangodb/pkg/util/errors" "github.com/arangodb/kube-arangodb/pkg/util/globals" ) @@ -123,13 +124,20 @@ func (s *stateInspector) RefreshState(ctx context.Context, members api.Deploymen ctxChild, cancel := globals.GetGlobalTimeouts().ArangoDCheck().WithTimeout(ctx) defer cancel() - if members[id].Group.IsArangosync() { + switch members[id].Group.Type() { + case api.ServerGroupTypeArangoSync: results[id] = s.fetchArangosyncMemberState(ctxChild, members[id]) - } else { + case api.ServerGroupTypeArangoD: results[id] = s.fetchServerMemberState(ctxChild, members[id], servingGroup) if results[id].IsServing() { client = results[id].client } + default: + assertion.InvalidGroupKey.Assert(true, "Unable to fetch Health for an unknown group: %s", members[id].Group.AsRole()) + results[id] = State{ + IsClusterHealthy: false, + serving: false, + } } }) @@ -155,22 +163,26 @@ func (s *stateInspector) RefreshState(ctx context.Context, members api.Deploymen continue } - if members[i].Group.IsArangosync() { + switch members[i].Group.Type() { + case api.ServerGroupTypeArangoD: + if v, ok := h.Members[driver.ServerID(m.Member.ID)]; ok { + results[i].IsClusterHealthy = v.Status == driver.ServerStatusGood + if results[i].IsServing() && v.SyncStatus == driver.ServerSyncStatusServing { + if cs.client == nil || util.Rand().Intn(100) > 50 { + // Set client from nil or take next client with 50% probability. + cs.client = results[i].client + cs.Version = results[i].Version + } + } + } + case api.ServerGroupTypeArangoSync: // ArangoSync is considered as healthy when it is possible to fetch version. results[i].IsClusterHealthy = true - continue + default: + assertion.InvalidGroupKey.Assert(true, "Unable to fetch Health for an unknown group: %s", members[i].Group.AsRole()) + results[i].IsClusterHealthy = false } - if v, ok := h.Members[driver.ServerID(m.Member.ID)]; ok { - results[i].IsClusterHealthy = v.Status == driver.ServerStatusGood - if results[i].IsServing() && v.SyncStatus == driver.ServerSyncStatusServing { - if cs.client == nil || util.Rand().Intn(100) > 50 { - // Set client from nil or take next client with 50% probability. - cs.client = results[i].client - cs.Version = results[i].Version - } - } - } } return nil diff --git a/pkg/deployment/pod/utils.go b/pkg/deployment/pod/utils.go index da5b7fd7d..3ffa5ffa4 100644 --- a/pkg/deployment/pod/utils.go +++ b/pkg/deployment/pod/utils.go @@ -25,6 +25,7 @@ import ( meta "k8s.io/apimachinery/pkg/apis/meta/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" + "github.com/arangodb/kube-arangodb/pkg/util/assertion" "github.com/arangodb/kube-arangodb/pkg/util/errors" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/service" @@ -41,7 +42,8 @@ func GenerateMemberEndpoint(services service.Inspector, apiObject meta.Object, s } func GenerateMemberEndpointFromService(svc *core.Service, apiObject meta.Object, spec api.DeploymentSpec, group api.ServerGroup, member api.MemberStatus) (string, error) { - if group.IsArangod() { + switch group.Type() { + case api.ServerGroupTypeArangoD: switch method := spec.CommunicationMethod.Get(); method { case api.DeploymentCommunicationMethodDNS, api.DeploymentCommunicationMethodHeadlessDNS: return k8sutil.CreateServiceDNSNameWithDomain(svc, spec.ClusterDomain), nil @@ -60,7 +62,9 @@ func GenerateMemberEndpointFromService(svc *core.Service, apiObject meta.Object, default: return k8sutil.CreatePodDNSNameWithDomain(apiObject, spec.ClusterDomain, group.AsRole(), member.ID), nil } - } else { + case api.ServerGroupTypeArangoSync: return k8sutil.CreateSyncMasterClientServiceDNSNameWithDomain(apiObject, spec.ClusterDomain), nil + default: + return "", assertion.InvalidGroupKey.Assert(true, "Unable to create Endpoint for an unknown group: %s", group.AsRole()) } } diff --git a/pkg/deployment/reconcile/action_wait_for_member_up.go b/pkg/deployment/reconcile/action_wait_for_member_up.go index e38d6fe1c..9b4b525c7 100644 --- a/pkg/deployment/reconcile/action_wait_for_member_up.go +++ b/pkg/deployment/reconcile/action_wait_for_member_up.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2024 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. @@ -29,6 +29,7 @@ import ( api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" "github.com/arangodb/kube-arangodb/pkg/deployment/client" "github.com/arangodb/kube-arangodb/pkg/deployment/resources" + "github.com/arangodb/kube-arangodb/pkg/util/assertion" "github.com/arangodb/kube-arangodb/pkg/util/errors" "github.com/arangodb/kube-arangodb/pkg/util/globals" ) @@ -72,22 +73,27 @@ func (a *actionWaitForMemberUp) CheckProgress(ctx context.Context) (bool, bool, ctxChild, cancel := globals.GetGlobalTimeouts().ArangoD().WithTimeout(ctx) defer cancel() - if a.action.Group.IsArangosync() { - return a.checkProgressArangoSync(ctxChild) - } - switch a.actionCtx.GetMode() { - case api.DeploymentModeSingle: - return a.checkProgressSingle(ctxChild) - case api.DeploymentModeActiveFailover: - if a.action.Group == api.ServerGroupAgents { - return a.checkProgressAgent() + switch a.action.Group.Type() { + case api.ServerGroupTypeArangoD: + switch a.actionCtx.GetMode() { + case api.DeploymentModeSingle: + return a.checkProgressSingle(ctxChild) + case api.DeploymentModeActiveFailover: + if a.action.Group == api.ServerGroupAgents { + return a.checkProgressAgent() + } + return a.checkProgressSingleInActiveFailover(ctxChild) + default: + if a.action.Group == api.ServerGroupAgents { + return a.checkProgressAgent() + } + return a.checkProgressCluster(ctx) } - return a.checkProgressSingleInActiveFailover(ctxChild) + case api.ServerGroupTypeArangoSync: + return a.checkProgressArangoSync(ctxChild) default: - if a.action.Group == api.ServerGroupAgents { - return a.checkProgressAgent() - } - return a.checkProgressCluster(ctx) + assertion.InvalidGroupKey.Assert(true, "Unable to execute action WaitForMemberUp for an unknown group: %s", a.action.Group.AsRole()) + return true, false, nil } } diff --git a/pkg/deployment/reconcile/helper_shutdown.go b/pkg/deployment/reconcile/helper_shutdown.go index fa2c9d550..d90b818d7 100644 --- a/pkg/deployment/reconcile/helper_shutdown.go +++ b/pkg/deployment/reconcile/helper_shutdown.go @@ -31,6 +31,7 @@ import ( "github.com/arangodb/kube-arangodb/pkg/deployment/client" "github.com/arangodb/kube-arangodb/pkg/deployment/features" "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/assertion" "github.com/arangodb/kube-arangodb/pkg/util/errors" "github.com/arangodb/kube-arangodb/pkg/util/globals" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/kerrors" @@ -157,7 +158,8 @@ func (s shutdownHelperAPI) Start(ctx context.Context) (bool, error) { } } - if group.IsArangod() { + switch group.Type() { + case api.ServerGroupTypeArangoD: // Invoke shutdown endpoint c, err := s.actionCtx.GetMembersState().GetMemberClient(s.action.MemberID) if err != nil { @@ -177,14 +179,17 @@ func (s shutdownHelperAPI) Start(ctx context.Context) (bool, error) { s.log.Err(err).Debug("Failed to shutdown member") return false, errors.WithStack(err) } - } else if group.IsArangosync() { + return false, nil + case api.ServerGroupTypeArangoSync: // Terminate pod if err := cache.Client().Kubernetes().CoreV1().Pods(cache.Namespace()).Delete(ctx, podName, meta.DeleteOptions{}); err != nil { return false, errors.WithStack(err) } + return false, nil + default: + assertion.InvalidGroupKey.Assert(true, "Unable to execute ShutdownAction for an unknown group: %s", group.AsRole()) + return false, nil } - - return false, nil } // CheckProgress returns true when pod is terminated. diff --git a/pkg/deployment/reconcile/plan_builder_tls.go b/pkg/deployment/reconcile/plan_builder_tls.go index 17566f284..766716b53 100644 --- a/pkg/deployment/reconcile/plan_builder_tls.go +++ b/pkg/deployment/reconcile/plan_builder_tls.go @@ -41,6 +41,7 @@ import ( "github.com/arangodb/kube-arangodb/pkg/deployment/features" "github.com/arangodb/kube-arangodb/pkg/deployment/resources" "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/assertion" "github.com/arangodb/kube-arangodb/pkg/util/constants" "github.com/arangodb/kube-arangodb/pkg/util/crypto" operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http" @@ -542,10 +543,15 @@ func (r *Reconciler) keyfileRenewalRequired(ctx context.Context, apiObject k8sut // Verify AltNames var altNames memberTls.KeyfileInput - if group.IsArangosync() { - altNames, err = memberTls.GetSyncAltNames(apiObject, spec, tlsSpec, group, member) - } else { + + switch group.Type() { + case api.ServerGroupTypeArangoD: altNames, err = memberTls.GetServerAltNames(apiObject, spec, tlsSpec, service, group, member) + case api.ServerGroupTypeArangoSync: + altNames, err = memberTls.GetSyncAltNames(apiObject, spec, tlsSpec, group, member) + default: + assertion.InvalidGroupKey.Assert(true, "Unable to check TLS Key Renewal for an unknown group: %s", group.AsRole()) + return false, false } if err != nil { @@ -568,37 +574,44 @@ func (r *Reconciler) keyfileRenewalRequired(ctx context.Context, apiObject k8sut } // Ensure secret is propagated only on 3.7.0+ enterprise and inplace mode - if mode == api.TLSRotateModeInPlace && group.IsArangod() { - conn, err := context.GetMembersState().GetMemberClient(member.ID) - if err != nil { - r.planLogger.Err(err).Warn("Unable to get client") - return false, false - } + if mode == api.TLSRotateModeInPlace { + switch group.Type() { + case api.ServerGroupTypeArangoD: + conn, err := context.GetMembersState().GetMemberClient(member.ID) + if err != nil { + r.planLogger.Err(err).Warn("Unable to get client") + return false, false + } - s, exists := cachedStatus.Secret().V1().GetSimple(k8sutil.CreateTLSKeyfileSecretName(apiObject.GetName(), group.AsRole(), member.ID)) - if !exists { - r.planLogger.Warn("Keyfile secret is missing") - return false, false - } + s, exists := cachedStatus.Secret().V1().GetSimple(k8sutil.CreateTLSKeyfileSecretName(apiObject.GetName(), group.AsRole(), member.ID)) + if !exists { + r.planLogger.Warn("Keyfile secret is missing") + return false, false + } - c := client.NewClient(conn.Connection(), r.log) - tls, err := c.GetTLS(ctx) - if err != nil { - r.planLogger.Err(err).Warn("Unable to get tls details") - return false, false - } + c := client.NewClient(conn.Connection(), r.log) + tls, err := c.GetTLS(ctx) + if err != nil { + r.planLogger.Err(err).Warn("Unable to get tls details") + return false, false + } - keyfile, ok := s.Data[constants.SecretTLSKeyfile] - if !ok { - r.planLogger.Warn("Keyfile secret is invalid") - return false, false - } + keyfile, ok := s.Data[constants.SecretTLSKeyfile] + if !ok { + r.planLogger.Warn("Keyfile secret is invalid") + return false, false + } - keyfileSha := util.SHA256(keyfile) + keyfileSha := util.SHA256(keyfile) - if tls.Result.KeyFile.GetSHA().Checksum() != keyfileSha { - r.planLogger.Str("current", tls.Result.KeyFile.GetSHA().Checksum()).Str("desired", keyfileSha).Debug("Unable to get tls details") - return true, false + if tls.Result.KeyFile.GetSHA().Checksum() != keyfileSha { + r.planLogger.Str("current", tls.Result.KeyFile.GetSHA().Checksum()).Str("desired", keyfileSha).Debug("Unable to get tls details") + return true, false + } + case api.ServerGroupTypeArangoSync: + break + default: + assertion.InvalidGroupKey.Assert(true, "Unable to check TLS Key Renewal for an unknown group: %s", group.AsRole()) } } diff --git a/pkg/deployment/resources/pod_creator.go b/pkg/deployment/resources/pod_creator.go index 4656a364d..5befe8a2c 100644 --- a/pkg/deployment/resources/pod_creator.go +++ b/pkg/deployment/resources/pod_creator.go @@ -42,6 +42,7 @@ import ( "github.com/arangodb/kube-arangodb/pkg/deployment/pod" "github.com/arangodb/kube-arangodb/pkg/deployment/reconciler" "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/assertion" "github.com/arangodb/kube-arangodb/pkg/util/constants" "github.com/arangodb/kube-arangodb/pkg/util/errors" "github.com/arangodb/kube-arangodb/pkg/util/globals" @@ -335,7 +336,8 @@ func (r *Resources) RenderPodForMember(ctx context.Context, acs sutil.ACS, spec podName := k8sutil.CreatePodName(apiObject.GetName(), roleAbbr, m.ID, CreatePodSuffix(spec)) var podCreator interfaces.PodCreator - if group.IsArangod() { + switch group.Type() { + case api.ServerGroupTypeArangoD: // Prepare arguments autoUpgrade := m.Conditions.IsTrue(api.ConditionTypeAutoUpgrade) || spec.Upgrade.Get().AutoUpgrade @@ -353,7 +355,7 @@ func (r *Resources) RenderPodForMember(ctx context.Context, acs sutil.ACS, spec arangoMember: *member, cachedStatus: cache, } - } else if group.IsArangosync() { + case api.ServerGroupTypeArangoSync: // Check image if !imageInfo.Enterprise { log.Str("image", spec.GetImage()).Debug("Image is not an enterprise image") @@ -377,8 +379,8 @@ func (r *Resources) RenderPodForMember(ctx context.Context, acs sutil.ACS, spec memberStatus: m, cachedStatus: cache, } - } else { - return nil, errors.Errorf("unable to render Pod") + default: + return nil, assertion.InvalidGroupKey.Assert(true, "Unable to render pod for an unknown group: %s", group.AsRole()) } pod, err := RenderArangoPod(ctx, cache, apiObject, role, m.ID, podName, podCreator) @@ -468,7 +470,8 @@ func (r *Resources) createPodForMember(ctx context.Context, cachedStatus inspect newPhase := api.MemberPhaseCreated // Create pod - if group.IsArangod() { + switch group.Type() { + case api.ServerGroupTypeArangoD: // Prepare arguments autoUpgrade := m.Conditions.IsTrue(api.ConditionTypeAutoUpgrade) if autoUpgrade { @@ -507,7 +510,7 @@ func (r *Resources) createPodForMember(ctx context.Context, cachedStatus inspect } else { log.Str("pod-name", pod.Name).Debug("Created pod with predefined image") } - } else if group.IsArangosync() { + case api.ServerGroupTypeArangoSync: // Check monitoring token secret if group == api.ServerGroupSyncMasters { // Create TLS secret @@ -545,6 +548,8 @@ func (r *Resources) createPodForMember(ctx context.Context, cachedStatus inspect m.Pod.Propagate(&m) log.Str("pod-name", pod.Name).Debug("Created pod") + default: + return assertion.InvalidGroupKey.Assert(true, "Unable to create pod for an unknown group: %s", group.AsRole()) } member.GetPhaseExecutor().Execute(r.context.GetAPIObject(), spec, group, &m, api.Action{}, newPhase) diff --git a/pkg/deployment/resources/pod_creator_arangod.go b/pkg/deployment/resources/pod_creator_arangod.go index 01667d39e..092a53d14 100644 --- a/pkg/deployment/resources/pod_creator_arangod.go +++ b/pkg/deployment/resources/pod_creator_arangod.go @@ -598,7 +598,7 @@ func (m *MemberArangoDPod) Annotations() map[string]string { func (m *MemberArangoDPod) Labels() map[string]string { l := collection.ReservedLabels().Filter(collection.MergeAnnotations(m.spec.Labels, m.groupSpec.Labels)) - if m.group.IsArangod() && m.status.Topology != nil && m.deploymentStatus.Topology.Enabled() && m.deploymentStatus.Topology.ID == m.status.Topology.ID { + if m.status.Topology != nil && m.deploymentStatus.Topology.Enabled() && m.deploymentStatus.Topology.ID == m.status.Topology.ID { if l == nil { l = map[string]string{} } diff --git a/pkg/deployment/resources/secrets.go b/pkg/deployment/resources/secrets.go index 63983c46e..f79a7f136 100644 --- a/pkg/deployment/resources/secrets.go +++ b/pkg/deployment/resources/secrets.go @@ -41,6 +41,7 @@ import ( "github.com/arangodb/kube-arangodb/pkg/deployment/pod" "github.com/arangodb/kube-arangodb/pkg/metrics" "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/assertion" "github.com/arangodb/kube-arangodb/pkg/util/constants" "github.com/arangodb/kube-arangodb/pkg/util/errors" "github.com/arangodb/kube-arangodb/pkg/util/globals" @@ -129,36 +130,41 @@ func (r *Resources) EnsureSecrets(ctx context.Context, cachedStatus inspectorInt } if err := reconcileRequired.ParallelAll(len(members), func(id int) error { - if !members[id].Group.IsArangod() { - return nil - } + switch members[id].Group.Type() { + case api.ServerGroupTypeArangoD: + memberName := members[id].Member.ArangoMemberName(r.context.GetAPIObject().GetName(), members[id].Group) - memberName := members[id].Member.ArangoMemberName(r.context.GetAPIObject().GetName(), members[id].Group) - - member, ok := cachedStatus.ArangoMember().V1().GetSimple(memberName) - if !ok { - return errors.Errorf("Member %s not found", memberName) - } - - service, ok := cachedStatus.Service().V1().GetSimple(memberName) - if !ok { - return errors.Errorf("Service of member %s not found", memberName) - } + member, ok := cachedStatus.ArangoMember().V1().GetSimple(memberName) + if !ok { + return errors.Errorf("Member %s not found", memberName) + } - tlsKeyfileSecretName := k8sutil.AppendTLSKeyfileSecretPostfix(member.GetName()) - if _, exists := cachedStatus.Secret().V1().GetSimple(tlsKeyfileSecretName); !exists { - serverNames, err := tls.GetServerAltNames(apiObject, spec, spec.TLS, service, members[id].Group, members[id].Member) - if err != nil { - return errors.WithStack(errors.Wrapf(err, "Failed to render alt names")) + service, ok := cachedStatus.Service().V1().GetSimple(memberName) + if !ok { + return errors.Errorf("Service of member %s not found", memberName) } - owner := member.AsOwner() - if created, err := createTLSServerCertificate(ctx, log, cachedStatus, secrets, serverNames, spec.TLS, tlsKeyfileSecretName, &owner); err != nil && !kerrors.IsAlreadyExists(err) { - return errors.WithStack(errors.Wrapf(err, "Failed to create TLS keyfile secret")) - } else if created { - reconcileRequired.Required() + + tlsKeyfileSecretName := k8sutil.AppendTLSKeyfileSecretPostfix(member.GetName()) + if _, exists := cachedStatus.Secret().V1().GetSimple(tlsKeyfileSecretName); !exists { + serverNames, err := tls.GetServerAltNames(apiObject, spec, spec.TLS, service, members[id].Group, members[id].Member) + if err != nil { + return errors.WithStack(errors.Wrapf(err, "Failed to render alt names")) + } + owner := member.AsOwner() + if created, err := createTLSServerCertificate(ctx, log, cachedStatus, secrets, serverNames, spec.TLS, tlsKeyfileSecretName, &owner); err != nil && !kerrors.IsAlreadyExists(err) { + return errors.WithStack(errors.Wrapf(err, "Failed to create TLS keyfile secret")) + } else if created { + reconcileRequired.Required() + } } + return nil + case api.ServerGroupTypeArangoSync: + // Nothing to do + return nil + default: + assertion.InvalidGroupKey.Assert(true, "Unable to create TLS Secret an unknown group: %s", members[id].Group.AsRole()) + return nil } - return nil }); err != nil { return errors.Section(err, "TLS TrustStore") } diff --git a/pkg/util/assertion/assert.go b/pkg/util/assertion/assert.go index aa79e4a76..05acb6237 100644 --- a/pkg/util/assertion/assert.go +++ b/pkg/util/assertion/assert.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2023-2024 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. @@ -22,6 +22,8 @@ package assertion import ( "fmt" + + "github.com/arangodb/kube-arangodb/pkg/util/errors" ) type Key string @@ -30,19 +32,20 @@ const ( KeyUnknown Key = "" DeprecatedActionKey Key = "DeprecatedAction" CommunityLicenseCheckKey Key = "CommunityLicenseCheck" + InvalidGroupKey Key = "InvalidGroup" ) -func (k Key) Assert(condition bool, msg string, args ...interface{}) { - assert(2, condition, k, msg, args...) +func (k Key) Assert(condition bool, msg string, args ...interface{}) error { + return assert(2, condition, k, msg, args...) } -func Assert(condition bool, key Key, msg string, args ...interface{}) { - assert(2, condition, key, msg, args...) +func Assert(condition bool, key Key, msg string, args ...interface{}) error { + return assert(2, condition, key, msg, args...) } -func assert(skip int, condition bool, key Key, msg string, args ...interface{}) { +func assert(skip int, condition bool, key Key, msg string, args ...interface{}) error { if !condition { - return + return nil } metricsObject.incKeyMetric(key) @@ -50,4 +53,6 @@ func assert(skip int, condition bool, key Key, msg string, args ...interface{}) frames := frames(skip) _assert(frames, fmt.Sprintf(msg, args...)) + + return errors.WithStack(errors.Errorf("Error Assertion `%s`: %s", key, fmt.Sprintf(msg, args...))) }