Skip to content

Commit

Permalink
[Feature] Add ArangoDeployment ServerGroupType (#1699)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajanikow authored Aug 22, 2024
1 parent e84f860 commit 5772914
Show file tree
Hide file tree
Showing 19 changed files with 316 additions and 146 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions pkg/apis/deployment/v1/server_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 3 additions & 2 deletions pkg/apis/deployment/v1/server_group_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand Down
13 changes: 12 additions & 1 deletion pkg/apis/deployment/v1/server_group_test.go
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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())
}
30 changes: 30 additions & 0 deletions pkg/apis/deployment/v1/server_group_type.go
Original file line number Diff line number Diff line change
@@ -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
)
14 changes: 14 additions & 0 deletions pkg/apis/deployment/v2alpha1/server_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 3 additions & 2 deletions pkg/apis/deployment/v2alpha1/server_group_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand Down
13 changes: 12 additions & 1 deletion pkg/apis/deployment/v2alpha1/server_group_test.go
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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())
}
30 changes: 30 additions & 0 deletions pkg/apis/deployment/v2alpha1/server_group_type.go
Original file line number Diff line number Diff line change
@@ -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
)
75 changes: 38 additions & 37 deletions pkg/deployment/deployment_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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...,
)
}
}
}

Expand Down
40 changes: 26 additions & 14 deletions pkg/deployment/member/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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,
}
}
})

Expand All @@ -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
Expand Down
8 changes: 6 additions & 2 deletions pkg/deployment/pod/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -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())
}
}
Loading

0 comments on commit 5772914

Please sign in to comment.