Skip to content

Commit

Permalink
[Feature] Topology Discovery (#835)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajanikow authored Nov 15, 2021
1 parent 1b66d2e commit 281f28f
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions pkg/deployment/reconcile/plan_builder_high.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/deployment/reconcile/plan_builder_topology.community.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
48 changes: 41 additions & 7 deletions pkg/deployment/rotation/arangod_containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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
}
149 changes: 149 additions & 0 deletions pkg/deployment/rotation/arangod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ package rotation
import (
"testing"

"github.com/arangodb/kube-arangodb/pkg/deployment/topology"

core "k8s.io/api/core/v1"
)

Expand Down Expand Up @@ -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...)
}
9 changes: 7 additions & 2 deletions pkg/deployment/rotation/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 281f28f

Please sign in to comment.