diff --git a/CHANGELOG.md b/CHANGELOG.md index a59465af4..f97ac4a63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Add new admin commands to fetch agency dump and agency state - Add Graceful shutdown as finalizer (supports kubectl delete) - Add Watch to Lifecycle command +- Add Topology Discovery ## [1.2.4](https://github.com/arangodb/kube-arangodb/tree/1.2.4) (2021-10-22) - Replace `beta.kubernetes.io/arch` Pod label with `kubernetes.io/arch` using Silent Rotation diff --git a/pkg/deployment/reconcile/plan_builder_high.go b/pkg/deployment/reconcile/plan_builder_high.go index ed4e32e58..757df74ac 100644 --- a/pkg/deployment/reconcile/plan_builder_high.go +++ b/pkg/deployment/reconcile/plan_builder_high.go @@ -89,6 +89,7 @@ func createHighPlan(ctx context.Context, log zerolog.Logger, apiObject k8sutil.A ApplyIfEmpty(createCleanOutPlan). ApplyIfEmpty(updateMemberUpdateConditionsPlan). ApplyIfEmpty(updateMemberRotationConditionsPlan). + ApplyIfEmpty(createTopologyMemberUpdatePlan). ApplyIfEmpty(createTopologyMemberConditionPlan). Plan(), true } diff --git a/pkg/deployment/reconcile/plan_builder_topology.community.go b/pkg/deployment/reconcile/plan_builder_topology.community.go index 76193e02f..9d8a68b96 100644 --- a/pkg/deployment/reconcile/plan_builder_topology.community.go +++ b/pkg/deployment/reconcile/plan_builder_topology.community.go @@ -37,6 +37,12 @@ func createTopologyEnablementPlan(ctx context.Context, cachedStatus inspectorInterface.Inspector, context PlanBuilderContext) api.Plan { return nil } +func createTopologyMemberUpdatePlan(ctx context.Context, + log zerolog.Logger, apiObject k8sutil.APIObject, + spec api.DeploymentSpec, status api.DeploymentStatus, + cachedStatus inspectorInterface.Inspector, context PlanBuilderContext) api.Plan { + return nil +} func createTopologyMemberConditionPlan(ctx context.Context, log zerolog.Logger, apiObject k8sutil.APIObject, diff --git a/pkg/deployment/rotation/arangod_containers.go b/pkg/deployment/rotation/arangod_containers.go index ade046864..396735b91 100644 --- a/pkg/deployment/rotation/arangod_containers.go +++ b/pkg/deployment/rotation/arangod_containers.go @@ -26,6 +26,8 @@ package rotation import ( "strings" + "github.com/arangodb/kube-arangodb/pkg/deployment/topology" + "k8s.io/apimachinery/pkg/api/equality" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" @@ -50,15 +52,26 @@ func containersCompare(_ api.DeploymentSpec, _ api.ServerGroup, spec, status *co for id := range a { if ac, bc := &a[id], &b[id]; ac.Name == bc.Name { if ac.Name == api.ServerGroupReservedContainerNameServer { - if !isOnlyLogLevelChanged(ac.Command, bc.Command) { - continue - } + if isOnlyLogLevelChanged(ac.Command, bc.Command) { + plan = append(plan, builder.NewAction(api.ActionTypeRuntimeContainerArgsLogLevelUpdate). + AddParam(ContainerName, ac.Name)) - plan = append(plan, builder.NewAction(api.ActionTypeRuntimeContainerArgsLogLevelUpdate). - AddParam(ContainerName, ac.Name)) + bc.Command = ac.Command + mode = mode.And(InPlaceRotation) + } - bc.Command = ac.Command - mode = mode.And(InPlaceRotation) + if !equality.Semantic.DeepEqual(ac.Env, bc.Env) { + if areEnvsEqual(ac.Env, bc.Env, func(a, b map[string]core.EnvVar) (map[string]core.EnvVar, map[string]core.EnvVar) { + if _, ok := a[topology.ArangoDBZone]; !ok { + delete(b, topology.ArangoDBZone) + } + + return a, b + }) { + bc.Env = ac.Env + mode = mode.And(SilentRotation) + } + } } else { if ac.Image != bc.Image { // Image changed @@ -162,3 +175,24 @@ func internalContainerLifecycleCompare(spec, status *core.Container) Mode { return SkippedRotation } + +func areEnvsEqual(a, b []core.EnvVar, rules ...func(a, b map[string]core.EnvVar) (map[string]core.EnvVar, map[string]core.EnvVar)) bool { + am := getEnvs(a) + bm := getEnvs(b) + + for _, r := range rules { + am, bm = r(am, bm) + } + + return equality.Semantic.DeepEqual(am, bm) +} + +func getEnvs(e []core.EnvVar) map[string]core.EnvVar { + m := map[string]core.EnvVar{} + + for _, q := range e { + m[q.Name] = q + } + + return m +} diff --git a/pkg/deployment/rotation/arangod_test.go b/pkg/deployment/rotation/arangod_test.go index 9eea9fc81..e63ba69a8 100644 --- a/pkg/deployment/rotation/arangod_test.go +++ b/pkg/deployment/rotation/arangod_test.go @@ -25,6 +25,8 @@ package rotation import ( "testing" + "github.com/arangodb/kube-arangodb/pkg/deployment/topology" + core "k8s.io/api/core/v1" ) @@ -177,3 +179,150 @@ func Test_ArangoD_Affinity(t *testing.T) { runTestCases(t)(testCases...) } + +func Test_ArangoD_Labels(t *testing.T) { + testCases := []TestCase{ + { + name: "Add label", + + spec: buildPodSpec(func(pod *core.PodTemplateSpec) { + pod.Labels = map[string]string{} + }), + + status: buildPodSpec(func(pod *core.PodTemplateSpec) { + pod.Labels = map[string]string{ + "A": "B", + } + }), + + expectedMode: SkippedRotation, + }, + { + name: "Remove label", + + spec: buildPodSpec(func(pod *core.PodTemplateSpec) { + pod.Labels = map[string]string{ + "A": "B", + } + }), + + status: buildPodSpec(func(pod *core.PodTemplateSpec) { + pod.Labels = map[string]string{} + }), + + expectedMode: SkippedRotation, + }, + { + name: "Change label", + + spec: buildPodSpec(func(pod *core.PodTemplateSpec) { + pod.Labels = map[string]string{ + "A": "A", + } + }), + + status: buildPodSpec(func(pod *core.PodTemplateSpec) { + pod.Labels = map[string]string{ + "A": "B", + } + }), + + expectedMode: SkippedRotation, + }, + } + + runTestCases(t)(testCases...) +} + +func Test_ArangoD_Envs_Zone(t *testing.T) { + testCases := []TestCase{ + { + name: "Add Zone env", + + spec: buildPodSpec(addContainer("server", func(c *core.Container) { + c.Env = []core.EnvVar{} + })), + + status: buildPodSpec(addContainer("server", func(c *core.Container) { + c.Env = []core.EnvVar{ + { + Name: topology.ArangoDBZone, + Value: "A", + }, + } + })), + + expectedMode: SilentRotation, + }, + { + name: "Remove Zone env", + + spec: buildPodSpec(addContainer("server", func(c *core.Container) { + c.Env = []core.EnvVar{ + { + Name: topology.ArangoDBZone, + Value: "A", + }, + } + })), + + status: buildPodSpec(addContainer("server", func(c *core.Container) { + c.Env = []core.EnvVar{} + })), + + expectedMode: GracefulRotation, + }, + { + name: "Update Zone env", + + spec: buildPodSpec(addContainer("server", func(c *core.Container) { + c.Env = []core.EnvVar{ + { + Name: topology.ArangoDBZone, + Value: "A", + }, + } + })), + + status: buildPodSpec(addContainer("server", func(c *core.Container) { + c.Env = []core.EnvVar{ + { + Name: topology.ArangoDBZone, + Value: "B", + }, + } + })), + + expectedMode: GracefulRotation, + }, + { + name: "Update other env", + + spec: buildPodSpec(addContainer("server", func(c *core.Container) { + c.Env = []core.EnvVar{ + { + Name: "Q", + Value: "A", + }, + { + Name: topology.ArangoDBZone, + Value: "A", + }, + } + })), + + status: buildPodSpec(addContainer("server", func(c *core.Container) { + c.Env = []core.EnvVar{ + { + Name: topology.ArangoDBZone, + Value: "A", + }, + } + })), + + expectedMode: GracefulRotation, + }, + } + + runTestCases(t)(testCases...) +} diff --git a/pkg/deployment/rotation/compare.go b/pkg/deployment/rotation/compare.go index 5821e09e6..957b1fee3 100644 --- a/pkg/deployment/rotation/compare.go +++ b/pkg/deployment/rotation/compare.go @@ -21,6 +21,8 @@ package rotation import ( + "encoding/json" + api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" "github.com/arangodb/kube-arangodb/pkg/deployment/resources" "github.com/rs/zerolog" @@ -88,10 +90,13 @@ func compare(log zerolog.Logger, deploymentSpec api.DeploymentSpec, member api.M } if spec.RotationNeeded(newStatus) { + specData, _ := json.Marshal(spec) + statusData, _ := json.Marshal(newStatus) + log.Info().Str("before", spec.PodSpecChecksum). Str("id", member.ID). - Interface("spec", spec). - Interface("status", newStatus). + Str("spec", string(specData)). + Str("status", string(statusData)). Msg("Pod needs rotation - templates does not match") return GracefulRotation, nil, nil