diff --git a/CHANGELOG.md b/CHANGELOG.md index 5080f0145..59a7c55e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Change Log ## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A) +- Add v2alpha1 API for ArangoDeployment and ArangoDeploymentReplication ## [1.1.2](https://github.com/arangodb/kube-arangodb/tree/1.1.2) (2020-11-11) - Fix Bootstrap phase and move it under Plan diff --git a/Makefile b/Makefile index fb1637f6b..b8601f946 100644 --- a/Makefile +++ b/Makefile @@ -239,7 +239,7 @@ update-generated: "all" \ "github.com/arangodb/kube-arangodb/pkg/generated" \ "github.com/arangodb/kube-arangodb/pkg/apis" \ - "deployment:v1 replication:v1 storage:v1alpha backup:v1" \ + "deployment:v1 replication:v1 storage:v1alpha backup:v1 deployment:v2alpha1 replication:v2alpha1" \ --go-header-file "./tools/codegen/boilerplate.go.txt" \ $(VERIFYARGS) GOPATH=$(GOBUILDDIR) $(VENDORDIR)/k8s.io/code-generator/generate-groups.sh \ diff --git a/pkg/apis/deployment/v2alpha1/authentication_spec.go b/pkg/apis/deployment/v2alpha1/authentication_spec.go new file mode 100644 index 000000000..97d5ca6f7 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/authentication_spec.go @@ -0,0 +1,93 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/pkg/errors" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" +) + +// AuthenticationSpec holds authentication specific configuration settings +type AuthenticationSpec struct { + JWTSecretName *string `json:"jwtSecretName,omitempty"` +} + +const ( + // JWTSecretNameDisabled is the value of JWTSecretName to use for disabling authentication. + JWTSecretNameDisabled = "None" +) + +// GetJWTSecretName returns the value of jwtSecretName. +func (s AuthenticationSpec) GetJWTSecretName() string { + return util.StringOrDefault(s.JWTSecretName) +} + +// IsAuthenticated returns true if authentication is enabled. +// Returns false other (when JWTSecretName == "None"). +func (s AuthenticationSpec) IsAuthenticated() bool { + return s.GetJWTSecretName() != JWTSecretNameDisabled +} + +// Validate the given spec +func (s AuthenticationSpec) Validate(required bool) error { + if required && !s.IsAuthenticated() { + return maskAny(errors.Wrap(ValidationError, "JWT secret is required")) + } + if s.IsAuthenticated() { + if err := k8sutil.ValidateResourceName(s.GetJWTSecretName()); err != nil { + return maskAny(err) + } + } + return nil +} + +// SetDefaults fills in missing defaults +func (s *AuthenticationSpec) SetDefaults(defaultJWTSecretName string) { + if s.GetJWTSecretName() == "" { + // Note that we don't check for nil here, since even a specified, but empty + // string should result in the default value. + s.JWTSecretName = util.NewString(defaultJWTSecretName) + } +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *AuthenticationSpec) SetDefaultsFrom(source AuthenticationSpec) { + if s.JWTSecretName == nil { + s.JWTSecretName = util.NewStringOrNil(source.JWTSecretName) + } +} + +// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. +// It returns a list of fields that have been reset. +// Field names are relative to given field prefix. +func (s AuthenticationSpec) ResetImmutableFields(fieldPrefix string, target *AuthenticationSpec) []string { + var resetFields []string + if s.IsAuthenticated() != target.IsAuthenticated() { + // Note: You can change the name, but not from empty to non-empty (or reverse). + target.JWTSecretName = util.NewStringOrNil(s.JWTSecretName) + resetFields = append(resetFields, fieldPrefix+".jwtSecretName") + } + return resetFields +} diff --git a/pkg/apis/deployment/v2alpha1/authentication_spec_test.go b/pkg/apis/deployment/v2alpha1/authentication_spec_test.go new file mode 100644 index 000000000..40b175d94 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/authentication_spec_test.go @@ -0,0 +1,105 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/stretchr/testify/assert" +) + +func TestAuthenticationSpecValidate(t *testing.T) { + // Valid + assert.Nil(t, AuthenticationSpec{JWTSecretName: util.NewString("None")}.Validate(false)) + assert.Nil(t, AuthenticationSpec{JWTSecretName: util.NewString("foo")}.Validate(false)) + assert.Nil(t, AuthenticationSpec{JWTSecretName: util.NewString("foo")}.Validate(true)) + + // Not valid + assert.Error(t, AuthenticationSpec{JWTSecretName: util.NewString("Foo")}.Validate(false)) +} + +func TestAuthenticationSpecIsAuthenticated(t *testing.T) { + assert.False(t, AuthenticationSpec{JWTSecretName: util.NewString("None")}.IsAuthenticated()) + assert.True(t, AuthenticationSpec{JWTSecretName: util.NewString("foo")}.IsAuthenticated()) + assert.True(t, AuthenticationSpec{JWTSecretName: util.NewString("")}.IsAuthenticated()) +} + +func TestAuthenticationSpecSetDefaults(t *testing.T) { + def := func(spec AuthenticationSpec) AuthenticationSpec { + spec.SetDefaults("test-jwt") + return spec + } + + assert.Equal(t, "test-jwt", def(AuthenticationSpec{}).GetJWTSecretName()) + assert.Equal(t, "foo", def(AuthenticationSpec{JWTSecretName: util.NewString("foo")}).GetJWTSecretName()) +} + +func TestAuthenticationSpecResetImmutableFields(t *testing.T) { + tests := []struct { + Original AuthenticationSpec + Target AuthenticationSpec + Expected AuthenticationSpec + Result []string + }{ + // Valid "changes" + { + AuthenticationSpec{JWTSecretName: util.NewString("None")}, + AuthenticationSpec{JWTSecretName: util.NewString("None")}, + AuthenticationSpec{JWTSecretName: util.NewString("None")}, + nil, + }, + { + AuthenticationSpec{JWTSecretName: util.NewString("foo")}, + AuthenticationSpec{JWTSecretName: util.NewString("foo")}, + AuthenticationSpec{JWTSecretName: util.NewString("foo")}, + nil, + }, + { + AuthenticationSpec{JWTSecretName: util.NewString("foo")}, + AuthenticationSpec{JWTSecretName: util.NewString("foo2")}, + AuthenticationSpec{JWTSecretName: util.NewString("foo2")}, + nil, + }, + + // Invalid changes + { + AuthenticationSpec{JWTSecretName: util.NewString("foo")}, + AuthenticationSpec{JWTSecretName: util.NewString("None")}, + AuthenticationSpec{JWTSecretName: util.NewString("foo")}, + []string{"test.jwtSecretName"}, + }, + { + AuthenticationSpec{JWTSecretName: util.NewString("None")}, + AuthenticationSpec{JWTSecretName: util.NewString("foo")}, + AuthenticationSpec{JWTSecretName: util.NewString("None")}, + []string{"test.jwtSecretName"}, + }, + } + + for _, test := range tests { + result := test.Original.ResetImmutableFields("test", &test.Target) + assert.Equal(t, test.Result, result) + assert.Equal(t, test.Expected, test.Target) + } +} diff --git a/pkg/apis/deployment/v2alpha1/bootstrap.go b/pkg/apis/deployment/v2alpha1/bootstrap.go new file mode 100644 index 000000000..879956cac --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/bootstrap.go @@ -0,0 +1,140 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 + +import ( + "fmt" + + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" +) + +const ( + // UserNameRoot root user name + UserNameRoot = "root" +) + +// PasswordSecretName contains user password secret name +type PasswordSecretName string + +func (p PasswordSecretName) Get() string { + return string(p) +} + +const ( + // PasswordSecretNameNone is magic value for no action + PasswordSecretNameNone PasswordSecretName = "None" + // PasswordSecretNameAuto is magic value for autogenerate name + PasswordSecretNameAuto PasswordSecretName = "Auto" +) + +// PasswordSecretNameList is a map from username to secretnames +type PasswordSecretNameList map[string]PasswordSecretName + +// BootstrapSpec contains information for cluster bootstrapping +type BootstrapSpec struct { + // PasswordSecretNames contains a map of username to password-secret-name + PasswordSecretNames PasswordSecretNameList `json:"passwordSecretNames,omitempty"` +} + +// IsNone returns true if p is None or p is empty +func (p PasswordSecretName) IsNone() bool { + return p == PasswordSecretNameNone || p == "" +} + +// IsAuto returns true if p is Auto +func (p PasswordSecretName) IsAuto() bool { + return p == PasswordSecretNameAuto +} + +// GetSecretName returns the secret name given by the specs. Or None if not set. +func (s PasswordSecretNameList) GetSecretName(user string) PasswordSecretName { + if s != nil { + if secretname, ok := s[user]; ok { + return secretname + } + } + return PasswordSecretNameNone +} + +// getSecretNameForUserPassword returns the default secret name for the given user +func getSecretNameForUserPassword(deploymentname, username string) PasswordSecretName { + return PasswordSecretName(k8sutil.FixupResourceName(deploymentname + "-" + username + "-password")) +} + +// Validate the specification. +func (b *BootstrapSpec) Validate() error { + 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) + } + } + } + + 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 = make(map[string]PasswordSecretName) + } + + // If root is not set init with Auto + if _, ok := b.PasswordSecretNames[UserNameRoot]; !ok { + b.PasswordSecretNames[UserNameRoot] = PasswordSecretNameNone + } + + // Replace Auto with generated secret name + for user, secretname := range b.PasswordSecretNames { + if secretname.IsAuto() { + 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/v2alpha1/chaos_spec.go b/pkg/apis/deployment/v2alpha1/chaos_spec.go new file mode 100644 index 000000000..65064813d --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/chaos_spec.go @@ -0,0 +1,91 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + time "time" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/pkg/errors" +) + +// ChaosSpec holds configuration for the deployment chaos monkey. +type ChaosSpec struct { + // Enabled switches the chaos monkey for a deployment on or off. + Enabled *bool `json:"enabled,omitempty"` + // Interval is the time between events + Interval *time.Duration `json:"interval,omitempty"` + // KillPodProbability is the chance of a pod being killed during an event + KillPodProbability *Percent `json:"kill-pod-probability,omitempty"` +} + +// IsEnabled returns the value of enabled. +func (s ChaosSpec) IsEnabled() bool { + return util.BoolOrDefault(s.Enabled) +} + +// GetInterval returns the value of interval. +func (s ChaosSpec) GetInterval() time.Duration { + return util.DurationOrDefault(s.Interval) +} + +// GetKillPodProbability returns the value of kill-pod-probability. +func (s ChaosSpec) GetKillPodProbability() Percent { + return PercentOrDefault(s.KillPodProbability) +} + +// Validate the given spec +func (s ChaosSpec) Validate() error { + if s.IsEnabled() { + if s.GetInterval() <= 0 { + return maskAny(errors.Wrapf(ValidationError, "Interval must be > 0")) + } + if err := s.GetKillPodProbability().Validate(); err != nil { + return maskAny(err) + } + } + return nil +} + +// SetDefaults fills in missing defaults +func (s *ChaosSpec) SetDefaults() { + if s.GetInterval() == 0 { + s.Interval = util.NewDuration(time.Minute) + } + if s.GetKillPodProbability() == 0 { + s.KillPodProbability = NewPercent(50) + } +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *ChaosSpec) SetDefaultsFrom(source ChaosSpec) { + if s.Enabled == nil { + s.Enabled = util.NewBoolOrNil(source.Enabled) + } + if s.Interval == nil { + s.Interval = util.NewDurationOrNil(source.Interval) + } + if s.KillPodProbability == nil { + s.KillPodProbability = NewPercentOrNil(source.KillPodProbability) + } +} diff --git a/pkg/apis/deployment/v2alpha1/conditions.go b/pkg/apis/deployment/v2alpha1/conditions.go new file mode 100644 index 000000000..da18827bd --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/conditions.go @@ -0,0 +1,200 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/arangodb/kube-arangodb/pkg/util" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ConditionType is a strongly typed condition name +type ConditionType string + +const ( + // ConditionTypeReady indicates that the member or entire deployment is ready and running normally. + ConditionTypeReady ConditionType = "Ready" + // ConditionTypeTerminated indicates that the member has terminated and will not restart. + ConditionTypeTerminated ConditionType = "Terminated" + // ConditionTypeAutoUpgrade indicates that the member has to be started with `--database.auto-upgrade` once. + ConditionTypeAutoUpgrade ConditionType = "AutoUpgrade" + // ConditionTypeCleanedOut indicates that the member (dbserver) has been cleaned out. + // Always check in combination with ConditionTypeTerminated. + ConditionTypeCleanedOut ConditionType = "CleanedOut" + // ConditionTypeAgentRecoveryNeeded indicates that the member (agent) will no + // longer recover from its current volume and there has to be rebuild + // using the recovery procedure. + ConditionTypeAgentRecoveryNeeded ConditionType = "AgentRecoveryNeeded" + // ConditionTypePodSchedulingFailure indicates that one or more pods belonging to the deployment cannot be schedule. + ConditionTypePodSchedulingFailure ConditionType = "PodSchedulingFailure" + // ConditionTypeSecretsChanged indicates that the value of one of more secrets used by + // the deployment have changed. Once that is the case, the operator will no longer + // touch the deployment, until the original secrets have been restored. + ConditionTypeSecretsChanged ConditionType = "SecretsChanged" + // ConditionTypeMemberOfCluster indicates that the member is a known member of the ArangoDB cluster. + ConditionTypeMemberOfCluster ConditionType = "MemberOfCluster" + // 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" + // ConditionTypeTerminating indicates that the deployment is up to date. + ConditionTypeUpToDate ConditionType = "UpToDate" + // ConditionTypeMarkedToRemove indicates that the member is marked to be removed. + ConditionTypeMarkedToRemove ConditionType = "MarkedToRemove" +) + +// Condition represents one current condition of a deployment or deployment member. +// A condition might not show up if it is not happening. +// For example, if a cluster is not upgrading, the Upgrading condition would not show up. +type Condition struct { + // Type of condition. + Type ConditionType `json:"type"` + // Status of the condition, one of True, False, Unknown. + Status v1.ConditionStatus `json:"status"` + // The last time this condition was updated. + LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"` + // Last time the condition transitioned from one status to another. + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + // The reason for the condition's last transition. + Reason string `json:"reason,omitempty"` + // A human readable message indicating details about the transition. + Message string `json:"message,omitempty"` +} + +func (c Condition) IsTrue() bool { + return c.Status == v1.ConditionTrue +} + +// ConditionList is a list of conditions. +// Each type is allowed only once. +type ConditionList []Condition + +// Equal checks for equality +func (list ConditionList) Equal(other ConditionList) bool { + if len(list) != len(other) { + return false + } + + for i := 0; i < len(list); i++ { + c, found := other.Get(list[i].Type) + if !found { + return false + } + + if !list[i].Equal(c) { + return false + } + } + + return true +} + +// Equal checks for equality +func (c Condition) Equal(other Condition) bool { + return c.Type == other.Type && + c.Status == other.Status && + util.TimeCompareEqual(c.LastUpdateTime, other.LastUpdateTime) && + util.TimeCompareEqual(c.LastTransitionTime, other.LastTransitionTime) && + c.Reason == other.Reason && + c.Message == other.Message +} + +// IsTrue return true when a condition with given type exists and its status is `True`. +func (list ConditionList) IsTrue(conditionType ConditionType) bool { + c, found := list.Get(conditionType) + return found && c.IsTrue() +} + +// Get a condition by type. +// Returns true if found, false if not found. +func (list ConditionList) Get(conditionType ConditionType) (Condition, bool) { + // Covers nil and empty lists + if len(list) == 0 { + return Condition{}, false + } + + for _, x := range list { + if x.Type == conditionType { + return x, true + } + } + // Not found + return Condition{}, false +} + +// Update the condition, replacing an old condition with same type (if any) +// Returns true when changes were made, false otherwise. +func (list *ConditionList) Update(conditionType ConditionType, status bool, reason, message string) bool { + src := *list + statusX := v1.ConditionFalse + if status { + statusX = v1.ConditionTrue + } + for i, x := range src { + if x.Type == conditionType { + if x.Status != statusX { + // Transition to another status + src[i].Status = statusX + now := metav1.Now() + src[i].LastTransitionTime = now + src[i].LastUpdateTime = now + src[i].Reason = reason + src[i].Message = message + } else if x.Reason != reason || x.Message != message { + src[i].LastUpdateTime = metav1.Now() + src[i].Reason = reason + src[i].Message = message + } else { + return false + } + return true + } + } + // Not found + now := metav1.Now() + *list = append(src, Condition{ + Type: conditionType, + LastUpdateTime: now, + LastTransitionTime: now, + Status: statusX, + Reason: reason, + Message: message, + }) + return true +} + +// Remove the condition with given type. +// Returns true if removed, or false if not found. +func (list *ConditionList) Remove(conditionType ConditionType) bool { + src := *list + for i, x := range src { + if x.Type == conditionType { + *list = append(src[:i], src[i+1:]...) + return true + } + } + // Not found + return false +} diff --git a/pkg/apis/deployment/v2alpha1/conditions_test.go b/pkg/apis/deployment/v2alpha1/conditions_test.go new file mode 100644 index 000000000..b5d667a19 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/conditions_test.go @@ -0,0 +1,99 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConditionListIsTrue(t *testing.T) { + assert.False(t, ConditionList{}.IsTrue(ConditionTypeReady)) + + cl := ConditionList{} + cl.Update(ConditionTypeReady, true, "test", "msg") + assert.True(t, cl.IsTrue(ConditionTypeReady)) + assert.False(t, cl.IsTrue(ConditionTypeTerminated)) + + cl.Update(ConditionTypeReady, false, "test", "msg") + assert.False(t, cl.IsTrue(ConditionTypeReady)) + + cl.Remove(ConditionTypeReady) + assert.False(t, cl.IsTrue(ConditionTypeReady)) + assert.Equal(t, 0, len(cl)) +} + +func TestConditionListGet(t *testing.T) { + conv := func(c Condition, b bool) []interface{} { + return []interface{}{c, b} + } + + cl := ConditionList{} + assert.EqualValues(t, conv(Condition{}, false), conv(cl.Get(ConditionTypeReady))) + cl.Update(ConditionTypeReady, false, "test", "msg") + assert.EqualValues(t, conv(cl[0], true), conv(cl.Get(ConditionTypeReady))) +} + +func TestConditionListUpdate(t *testing.T) { + cl := ConditionList{} + assert.Equal(t, 0, len(cl)) + + assert.True(t, cl.Update(ConditionTypeReady, true, "test", "msg")) + assert.True(t, cl.IsTrue(ConditionTypeReady)) + assert.Equal(t, 1, len(cl)) + + assert.False(t, cl.Update(ConditionTypeReady, true, "test", "msg")) + assert.True(t, cl.IsTrue(ConditionTypeReady)) + assert.Equal(t, 1, len(cl)) + + assert.True(t, cl.Update(ConditionTypeReady, false, "test", "msg")) + assert.False(t, cl.IsTrue(ConditionTypeReady)) + assert.Equal(t, 1, len(cl)) + + assert.True(t, cl.Update(ConditionTypeReady, false, "test2", "msg")) + assert.False(t, cl.IsTrue(ConditionTypeReady)) + assert.Equal(t, 1, len(cl)) + + assert.True(t, cl.Update(ConditionTypeReady, false, "test2", "msg2")) + assert.False(t, cl.IsTrue(ConditionTypeReady)) + assert.Equal(t, 1, len(cl)) +} + +func TestConditionListRemove(t *testing.T) { + cl := ConditionList{} + assert.Equal(t, 0, len(cl)) + + cl.Update(ConditionTypeReady, true, "test", "msg") + cl.Update(ConditionTypeTerminated, false, "test", "msg") + assert.Equal(t, 2, len(cl)) + + assert.True(t, cl.Remove(ConditionTypeReady)) + assert.Equal(t, 1, len(cl)) + + assert.False(t, cl.Remove(ConditionTypeReady)) + assert.Equal(t, 1, len(cl)) + + assert.True(t, cl.Remove(ConditionTypeTerminated)) + assert.Equal(t, 0, len(cl)) +} diff --git a/pkg/apis/deployment/v2alpha1/const.go b/pkg/apis/deployment/v2alpha1/const.go new file mode 100644 index 000000000..8e48e3f2a --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/const.go @@ -0,0 +1,44 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 LabelsMode string + +const ( + // LabelsDisabledMode disable annotations/labels override. Default if there is no annotations/labels set in ArangoDeployment + LabelsDisabledMode LabelsMode = "disabled" + // LabelsAppendMode add new annotations/labels without affecting old ones + LabelsAppendMode LabelsMode = "append" + // LabelsReplaceMode replace existing annotations/labels + LabelsReplaceMode LabelsMode = "replace" +) + +func (a LabelsMode) New() *LabelsMode { + return &a +} + +func (a *LabelsMode) Get(def LabelsMode) LabelsMode { + if a == nil { + return def + } + + return *a +} diff --git a/pkg/apis/deployment/v2alpha1/database_spec.go b/pkg/apis/deployment/v2alpha1/database_spec.go new file mode 100644 index 000000000..8c8d95abd --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/database_spec.go @@ -0,0 +1,36 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Adam Janikowski +// + +package v2alpha1 + +type DatabaseSpec struct { + // Maintenance manage maintenance mode on Cluster side. Requires maintenance feature to be enabled + Maintenance *bool `json:"maintenance,omitempty"` +} + +func (m *DatabaseSpec) GetMaintenance() bool { + if m == nil || m.Maintenance == nil { + return false + } + + return *m.Maintenance +} diff --git a/pkg/apis/deployment/v2alpha1/deployment.go b/pkg/apis/deployment/v2alpha1/deployment.go new file mode 100644 index 000000000..b7b329610 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/deployment.go @@ -0,0 +1,105 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/arangodb/kube-arangodb/pkg/apis/deployment" + meta "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ArangoDeploymentList is a list of ArangoDB clusters. +type ArangoDeploymentList struct { + meta.TypeMeta `json:",inline"` + // Standard list metadata + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + meta.ListMeta `json:"metadata,omitempty"` + Items []ArangoDeployment `json:"items"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ArangoDeployment contains the entire Kubernetes info for an ArangoDB database deployment. +type ArangoDeployment struct { + meta.TypeMeta `json:",inline"` + meta.ObjectMeta `json:"metadata,omitempty"` + Spec DeploymentSpec `json:"spec,omitempty"` + Status DeploymentStatus `json:"status,omitempty"` +} + +type ServerGroupFunc func(ServerGroup, ServerGroupSpec, *MemberStatusList) error + +// AsOwner creates an OwnerReference for the given deployment +func (d *ArangoDeployment) AsOwner() meta.OwnerReference { + trueVar := true + return meta.OwnerReference{ + APIVersion: SchemeGroupVersion.String(), + Kind: deployment.ArangoDeploymentResourceKind, + Name: d.Name, + UID: d.UID, + Controller: &trueVar, + // For now BlockOwnerDeletion does not work on OpenShift, so we leave it out. + //BlockOwnerDeletion: &trueVar, + } +} + +// ForeachServerGroup calls the given callback for all server groups. +// If the callback returns an error, this error is returned and no other server +// groups are processed. +// Groups are processed in this order: agents, single, dbservers, coordinators, syncmasters, syncworkers +func (d *ArangoDeployment) ForeachServerGroup(cb ServerGroupFunc, status *DeploymentStatus) error { + if status == nil { + status = &d.Status + } + if err := cb(ServerGroupAgents, d.Spec.Agents, &status.Members.Agents); err != nil { + return maskAny(err) + } + if err := cb(ServerGroupSingle, d.Spec.Single, &status.Members.Single); err != nil { + return maskAny(err) + } + if err := cb(ServerGroupDBServers, d.Spec.DBServers, &status.Members.DBServers); err != nil { + return maskAny(err) + } + if err := cb(ServerGroupCoordinators, d.Spec.Coordinators, &status.Members.Coordinators); err != nil { + return maskAny(err) + } + if err := cb(ServerGroupSyncMasters, d.Spec.SyncMasters, &status.Members.SyncMasters); err != nil { + return maskAny(err) + } + if err := cb(ServerGroupSyncWorkers, d.Spec.SyncWorkers, &status.Members.SyncWorkers); err != nil { + return maskAny(err) + } + return nil +} + +// IsUpToDate checks if applied version match current version in spec +func (d ArangoDeployment) IsUpToDate() (bool, error) { + sha, err := d.Spec.Checksum() + if err != nil { + return false, err + } + + return sha == d.Status.AppliedVersion && d.Status.Conditions.IsTrue(ConditionTypeUpToDate), nil +} diff --git a/pkg/apis/deployment/v2alpha1/deployment_backup_status.go b/pkg/apis/deployment/v2alpha1/deployment_backup_status.go new file mode 100644 index 000000000..38ace018f --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/deployment_backup_status.go @@ -0,0 +1,50 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Lars Maier +// +package v2alpha1 + +type DeploymentRestoreState string + +const ( + DeploymentRestoreStateRestoring DeploymentRestoreState = "Restoring" + DeploymentRestoreStateRestored DeploymentRestoreState = "Restored" + DeploymentRestoreStateRestoreFailed DeploymentRestoreState = "RestoreFailed" +) + +type DeploymentRestoreResult struct { + RequestedFrom string `json:"requestedFrom"` + State DeploymentRestoreState `json:"state"` + Message string `json:"message,omitempty"` +} + +func (dr *DeploymentRestoreResult) Equal(other *DeploymentRestoreResult) bool { + if dr == nil { + return other == nil + } + + if other == nil { + return false + } + + return dr.RequestedFrom == other.RequestedFrom && + dr.Message == other.Message && + dr.State == other.State +} diff --git a/pkg/apis/deployment/v2alpha1/deployment_features.go b/pkg/apis/deployment/v2alpha1/deployment_features.go new file mode 100644 index 000000000..4d8b0cc2f --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/deployment_features.go @@ -0,0 +1,36 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +type DeploymentFeatures struct { + FoxxQueues *bool `json:"foxx.queues,omitempty"` +} + +// GetFoxxQueues return if foxx queues are enabled. Defaults to true. +func (d *DeploymentFeatures) GetFoxxQueues() bool { + if d == nil || d.FoxxQueues == nil { + return true + } + + return *d.FoxxQueues +} diff --git a/pkg/apis/deployment/v2alpha1/deployment_mode.go b/pkg/apis/deployment/v2alpha1/deployment_mode.go new file mode 100644 index 000000000..99ba7b33f --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/deployment_mode.go @@ -0,0 +1,123 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/pkg/errors" +) + +// DeploymentMode specifies the type of ArangoDB deployment to create. +type DeploymentMode string + +const ( + // DeploymentModeSingle yields a single server + DeploymentModeSingle DeploymentMode = "Single" + // DeploymentModeActiveFailover yields an agency and a active-failover server pair + DeploymentModeActiveFailover DeploymentMode = "ActiveFailover" + // DeploymentModeCluster yields an full cluster (agency, dbservers & coordinators) + DeploymentModeCluster DeploymentMode = "Cluster" +) + +// Validate the mode. +// Return errors when validation fails, nil on success. +func (m DeploymentMode) Validate() error { + switch m { + case DeploymentModeSingle, DeploymentModeActiveFailover, DeploymentModeCluster: + return nil + default: + return maskAny(errors.Wrapf(ValidationError, "Unknown deployment mode: '%s'", string(m))) + } +} + +// Get mode or default value +func (m *DeploymentMode) Get() DeploymentMode { + if m == nil { + return DeploymentModeCluster + } + + return *m +} + +// String return string from mode +func (m *DeploymentMode) String() string { + return string(m.Get()) +} + +// Nww return pointer to mode +func (m DeploymentMode) New() *DeploymentMode { + return &m +} + +// HasSingleServers returns true when the given mode is "Single" or "ActiveFailover". +func (m DeploymentMode) HasSingleServers() bool { + return m == DeploymentModeSingle || m == DeploymentModeActiveFailover +} + +// HasAgents returns true when the given mode is "ActiveFailover" or "Cluster". +func (m DeploymentMode) HasAgents() bool { + return m == DeploymentModeActiveFailover || m == DeploymentModeCluster +} + +// HasDBServers returns true when the given mode is "Cluster". +func (m DeploymentMode) HasDBServers() bool { + return m == DeploymentModeCluster +} + +// HasCoordinators returns true when the given mode is "Cluster". +func (m DeploymentMode) HasCoordinators() bool { + return m == DeploymentModeCluster +} + +// SupportsSync returns true when the given mode supports dc2dc replication. +func (m DeploymentMode) SupportsSync() bool { + return m == DeploymentModeCluster +} + +// IsCluster returns true if the deployment mode is cluster +func (m DeploymentMode) IsCluster() bool { + return m == DeploymentModeCluster +} + +// NewMode returns a reference to a string with given value. +func NewMode(input DeploymentMode) *DeploymentMode { + return &input +} + +// NewModeOrNil returns nil if input is nil, otherwise returns a clone of the given value. +func NewModeOrNil(input *DeploymentMode) *DeploymentMode { + if input == nil { + return nil + } + return NewMode(*input) +} + +// ModeOrDefault returns the default value (or empty string) if input is nil, otherwise returns the referenced value. +func ModeOrDefault(input *DeploymentMode, defaultValue ...DeploymentMode) DeploymentMode { + if input == nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return "" + } + return *input +} diff --git a/pkg/apis/deployment/v2alpha1/deployment_mode_test.go b/pkg/apis/deployment/v2alpha1/deployment_mode_test.go new file mode 100644 index 000000000..c3211056e --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/deployment_mode_test.go @@ -0,0 +1,68 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDeploymentModeValidate(t *testing.T) { + // Valid + assert.Nil(t, DeploymentMode("Single").Validate()) + assert.Nil(t, DeploymentMode("ActiveFailover").Validate()) + assert.Nil(t, DeploymentMode("Cluster").Validate()) + + // Not valid + assert.Error(t, DeploymentMode("").Validate()) + assert.Error(t, DeploymentMode(" cluster").Validate()) + assert.Error(t, DeploymentMode("singles").Validate()) + assert.Error(t, DeploymentMode("single").Validate()) + assert.Error(t, DeploymentMode("activefailover").Validate()) + assert.Error(t, DeploymentMode("cluster").Validate()) +} + +func TestDeploymentModeHasX(t *testing.T) { + assert.True(t, DeploymentModeSingle.HasSingleServers()) + assert.True(t, DeploymentModeActiveFailover.HasSingleServers()) + assert.False(t, DeploymentModeCluster.HasSingleServers()) + + assert.False(t, DeploymentModeSingle.HasAgents()) + assert.True(t, DeploymentModeActiveFailover.HasAgents()) + assert.True(t, DeploymentModeCluster.HasAgents()) + + assert.False(t, DeploymentModeSingle.HasDBServers()) + assert.False(t, DeploymentModeActiveFailover.HasDBServers()) + assert.True(t, DeploymentModeCluster.HasDBServers()) + + assert.False(t, DeploymentModeSingle.HasCoordinators()) + assert.False(t, DeploymentModeActiveFailover.HasCoordinators()) + assert.True(t, DeploymentModeCluster.HasCoordinators()) +} + +func TestDeploymentModeSupportsSync(t *testing.T) { + assert.False(t, DeploymentModeSingle.SupportsSync()) + assert.False(t, DeploymentModeActiveFailover.SupportsSync()) + assert.True(t, DeploymentModeCluster.SupportsSync()) +} diff --git a/pkg/apis/deployment/v2alpha1/deployment_phase.go b/pkg/apis/deployment/v2alpha1/deployment_phase.go new file mode 100644 index 000000000..ce40d5ec6 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/deployment_phase.go @@ -0,0 +1,42 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +// DeploymentPhase is a strongly typed lifetime phase of a deployment +type DeploymentPhase string + +const ( + // DeploymentPhaseNone indicates that the phase is not set yet + DeploymentPhaseNone DeploymentPhase = "" + // DeploymentPhaseRunning indicates that the deployment is under control of the + // ArangoDeployment operator. + DeploymentPhaseRunning DeploymentPhase = "Running" + // DeploymentPhaseFailed indicates that a deployment is in a failed state + // from which automatic recovery is impossible. Inspect `Reason` for more info. + DeploymentPhaseFailed DeploymentPhase = "Failed" +) + +// IsFailed returns true if given state is DeploymentStateFailed +func (cs DeploymentPhase) IsFailed() bool { + return cs == DeploymentPhaseFailed +} diff --git a/pkg/apis/deployment/v2alpha1/deployment_phase_test.go b/pkg/apis/deployment/v2alpha1/deployment_phase_test.go new file mode 100644 index 000000000..2269a235b --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/deployment_phase_test.go @@ -0,0 +1,35 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDeploymentPhaseIsFailed(t *testing.T) { + assert.False(t, DeploymentPhaseNone.IsFailed()) + assert.True(t, DeploymentPhaseFailed.IsFailed()) + assert.False(t, DeploymentPhaseRunning.IsFailed()) +} diff --git a/pkg/apis/deployment/v2alpha1/deployment_spec.go b/pkg/apis/deployment/v2alpha1/deployment_spec.go new file mode 100644 index 000000000..12950dc24 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/deployment_spec.go @@ -0,0 +1,462 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "crypto/sha256" + "encoding/json" + "fmt" + "reflect" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/pkg/errors" + core "k8s.io/api/core/v1" +) + +var ( + DefaultImage = "arangodb/arangodb:latest" +) + +// validatePullPolicy the image pull policy. +// Return errors when validation fails, nil on success. +func validatePullPolicy(v core.PullPolicy) error { + switch v { + case "", core.PullAlways, core.PullNever, core.PullIfNotPresent: + return nil + default: + return maskAny(errors.Wrapf(ValidationError, "Unknown pull policy: '%s'", string(v))) + } +} + +// DeploymentSpec contains the spec part of a ArangoDeployment resource. +type DeploymentSpec struct { + Mode *DeploymentMode `json:"mode,omitempty"` + Environment *Environment `json:"environment,omitempty"` + StorageEngine *StorageEngine `json:"storageEngine,omitempty"` + Image *string `json:"image,omitempty"` + ImagePullPolicy *core.PullPolicy `json:"imagePullPolicy,omitempty"` + ImagePullSecrets []string `json:"imagePullSecrets,omitempty"` + ImageDiscoveryMode *DeploymentImageDiscoveryModeSpec `json:"imageDiscoveryMode,omitempty"` + DowntimeAllowed *bool `json:"downtimeAllowed,omitempty"` + DisableIPv6 *bool `json:"disableIPv6,omitempty"` + + Features *DeploymentFeatures `json:"features,omitempty"` + + NetworkAttachedVolumes *bool `json:"networkAttachedVolumes,omitempty"` + + // Annotations specified the annotations added to Pods in this group. + Annotations map[string]string `json:"annotations,omitempty"` + // AnnotationsIgnoreList list regexp or plain definitions which annotations should be ignored + AnnotationsIgnoreList []string `json:"annotationsIgnoreList,omitempty"` + // AnnotationsMode Define annotations mode which should be use while overriding annotations + AnnotationsMode *LabelsMode `json:"annotationsMode,omitempty"` + // Labels specified the labels added to Pods in this group. + Labels map[string]string `json:"labels,omitempty"` + // LabelsIgnoreList list regexp or plain definitions which labels should be ignored + LabelsIgnoreList []string `json:"labelsIgnoreList,omitempty"` + // LabelsMode Define labels mode which should be use while overriding labels + LabelsMode *LabelsMode `json:"labelsMode,omitempty"` + + RestoreFrom *string `json:"restoreFrom,omitempty"` + + RestoreEncryptionSecret *string `json:"restoreEncryptionSecret,omitempty"` + + // AllowUnsafeUpgrade determines if upgrade on missing member or with not in sync shards is allowed + AllowUnsafeUpgrade *bool `json:"allowUnsafeUpgrade,omitempty"` + + ExternalAccess ExternalAccessSpec `json:"externalAccess"` + RocksDB RocksDBSpec `json:"rocksdb"` + Authentication AuthenticationSpec `json:"auth"` + TLS TLSSpec `json:"tls"` + Sync SyncSpec `json:"sync"` + License LicenseSpec `json:"license"` + Metrics MetricsSpec `json:"metrics"` + Lifecycle LifecycleSpec `json:"lifecycle,omitempty"` + + ID *ServerIDGroupSpec `json:"id,omitempty"` + + // Database holds information about database state, like maintenance mode + Database *DatabaseSpec `json:"database,omitempty"` + + Single ServerGroupSpec `json:"single"` + Agents ServerGroupSpec `json:"agents"` + DBServers ServerGroupSpec `json:"dbservers"` + Coordinators ServerGroupSpec `json:"coordinators"` + SyncMasters ServerGroupSpec `json:"syncmasters"` + SyncWorkers ServerGroupSpec `json:"syncworkers"` + + Chaos ChaosSpec `json:"chaos"` + + Recovery *ArangoDeploymentRecoverySpec `json:"recovery,omitempty"` + + Bootstrap BootstrapSpec `json:"bootstrap,omitempty"` + + Timeouts *Timeouts `json:"timeouts,omitempty"` +} + +// GetRestoreFrom returns the restore from string or empty string if not set +func (s *DeploymentSpec) GetRestoreFrom() string { + return util.StringOrDefault(s.RestoreFrom) +} + +// HasRestoreFrom returns true if RestoreFrom is set +func (s *DeploymentSpec) HasRestoreFrom() bool { + return s.RestoreFrom != nil +} + +// Equal compares two DeploymentSpec +func (s *DeploymentSpec) Equal(other *DeploymentSpec) bool { + return reflect.DeepEqual(s, other) +} + +// GetMode returns the value of mode. +func (s DeploymentSpec) GetMode() DeploymentMode { + return ModeOrDefault(s.Mode) +} + +// GetEnvironment returns the value of environment. +func (s DeploymentSpec) GetEnvironment() Environment { + return EnvironmentOrDefault(s.Environment) +} + +// GetAnnotations returns the annotations of this group +func (s DeploymentSpec) GetAnnotations() map[string]string { + return s.Annotations +} + +// GetStorageEngine returns the value of storageEngine. +func (s DeploymentSpec) GetStorageEngine() StorageEngine { + return StorageEngineOrDefault(s.StorageEngine) +} + +// GetImage returns the value of image. +func (s DeploymentSpec) GetImage() string { + return util.StringOrDefault(s.Image) +} + +// GetSyncImage returns, if set, Sync.Image or the default image. +func (s DeploymentSpec) GetSyncImage() string { + if s.Sync.HasSyncImage() { + return s.Sync.GetSyncImage() + } + return s.GetImage() +} + +// GetImagePullPolicy returns the value of imagePullPolicy. +func (s DeploymentSpec) GetImagePullPolicy() core.PullPolicy { + return util.PullPolicyOrDefault(s.ImagePullPolicy) +} + +// IsDowntimeAllowed returns the value of downtimeAllowed. +func (s DeploymentSpec) IsDowntimeAllowed() bool { + return util.BoolOrDefault(s.DowntimeAllowed) +} + +// IsDisableIPv6 returns the value of disableIPv6. +func (s DeploymentSpec) IsDisableIPv6() bool { + return util.BoolOrDefault(s.DisableIPv6) +} + +// IsNetworkAttachedVolumes returns the value of networkAttachedVolumes, default false +func (s DeploymentSpec) IsNetworkAttachedVolumes() bool { + return util.BoolOrDefault(s.NetworkAttachedVolumes, false) +} + +// GetListenAddr returns "[::]" or "0.0.0.0" depending on IsDisableIPv6 +func (s DeploymentSpec) GetListenAddr() string { + if s.IsDisableIPv6() { + return "0.0.0.0" + } + return "[::]" +} + +// IsAuthenticated returns true when authentication is enabled +func (s DeploymentSpec) IsAuthenticated() bool { + return s.Authentication.IsAuthenticated() +} + +// IsSecure returns true when SSL is enabled +func (s DeploymentSpec) IsSecure() bool { + return s.TLS.IsSecure() +} + +// GetServerGroupSpec returns the server group spec (from this +// deployment spec) for the given group. +func (s DeploymentSpec) GetServerGroupSpec(group ServerGroup) ServerGroupSpec { + switch group { + case ServerGroupSingle: + return s.Single + case ServerGroupAgents: + return s.Agents + case ServerGroupDBServers: + return s.DBServers + case ServerGroupCoordinators: + return s.Coordinators + case ServerGroupSyncMasters: + return s.SyncMasters + case ServerGroupSyncWorkers: + return s.SyncWorkers + default: + return ServerGroupSpec{} + } +} + +// UpdateServerGroupSpec returns the server group spec (from this +// deployment spec) for the given group. +func (s *DeploymentSpec) UpdateServerGroupSpec(group ServerGroup, gspec ServerGroupSpec) { + switch group { + case ServerGroupSingle: + s.Single = gspec + case ServerGroupAgents: + s.Agents = gspec + case ServerGroupDBServers: + s.DBServers = gspec + case ServerGroupCoordinators: + s.Coordinators = gspec + case ServerGroupSyncMasters: + s.SyncMasters = gspec + case ServerGroupSyncWorkers: + s.SyncWorkers = gspec + } +} + +// SetDefaults fills in default values when a field is not specified. +func (s *DeploymentSpec) SetDefaults(deploymentName string) { + if s.GetMode() == "" { + s.Mode = NewMode(DeploymentModeCluster) + } + if s.GetEnvironment() == "" { + s.Environment = NewEnvironment(EnvironmentDevelopment) + } + if s.GetStorageEngine() == "" { + s.StorageEngine = NewStorageEngine(StorageEngineRocksDB) + } + if s.GetImage() == "" && s.IsDevelopment() { + s.Image = util.NewString(DefaultImage) + } + if s.GetImagePullPolicy() == "" { + s.ImagePullPolicy = util.NewPullPolicy(core.PullIfNotPresent) + } + s.ExternalAccess.SetDefaults() + s.RocksDB.SetDefaults() + s.Authentication.SetDefaults(deploymentName + "-jwt") + s.TLS.SetDefaults(deploymentName + "-ca") + s.Sync.SetDefaults(deploymentName+"-sync-jwt", deploymentName+"-sync-client-auth-ca", deploymentName+"-sync-ca", deploymentName+"-sync-mt") + s.Single.SetDefaults(ServerGroupSingle, s.GetMode().HasSingleServers(), s.GetMode()) + s.Agents.SetDefaults(ServerGroupAgents, s.GetMode().HasAgents(), s.GetMode()) + s.DBServers.SetDefaults(ServerGroupDBServers, s.GetMode().HasDBServers(), s.GetMode()) + s.Coordinators.SetDefaults(ServerGroupCoordinators, s.GetMode().HasCoordinators(), s.GetMode()) + s.SyncMasters.SetDefaults(ServerGroupSyncMasters, s.Sync.IsEnabled(), s.GetMode()) + s.SyncWorkers.SetDefaults(ServerGroupSyncWorkers, s.Sync.IsEnabled(), s.GetMode()) + s.Metrics.SetDefaults(deploymentName+"-exporter-jwt-token", s.Authentication.IsAuthenticated()) + s.Chaos.SetDefaults() + s.Bootstrap.SetDefaults(deploymentName) +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *DeploymentSpec) SetDefaultsFrom(source DeploymentSpec) { + if s.Mode == nil { + s.Mode = NewModeOrNil(source.Mode) + } + if s.Environment == nil { + s.Environment = NewEnvironmentOrNil(source.Environment) + } + if s.StorageEngine == nil { + s.StorageEngine = NewStorageEngineOrNil(source.StorageEngine) + } + if s.Image == nil { + s.Image = util.NewStringOrNil(source.Image) + } + if s.ImagePullPolicy == nil { + s.ImagePullPolicy = util.NewPullPolicyOrNil(source.ImagePullPolicy) + } + if s.DowntimeAllowed == nil { + s.DowntimeAllowed = util.NewBoolOrNil(source.DowntimeAllowed) + } + if s.DisableIPv6 == nil { + s.DisableIPv6 = util.NewBoolOrNil(source.DisableIPv6) + } + + if s.AllowUnsafeUpgrade == nil { + s.AllowUnsafeUpgrade = util.NewBoolOrNil(source.AllowUnsafeUpgrade) + } + if s.Database == nil { + s.Database = source.Database.DeepCopy() + } + + s.License.SetDefaultsFrom(source.License) + s.ExternalAccess.SetDefaultsFrom(source.ExternalAccess) + s.RocksDB.SetDefaultsFrom(source.RocksDB) + s.Authentication.SetDefaultsFrom(source.Authentication) + s.TLS.SetDefaultsFrom(source.TLS) + s.Sync.SetDefaultsFrom(source.Sync) + s.Single.SetDefaultsFrom(source.Single) + s.Agents.SetDefaultsFrom(source.Agents) + s.DBServers.SetDefaultsFrom(source.DBServers) + s.Coordinators.SetDefaultsFrom(source.Coordinators) + s.SyncMasters.SetDefaultsFrom(source.SyncMasters) + s.SyncWorkers.SetDefaultsFrom(source.SyncWorkers) + s.Metrics.SetDefaultsFrom(source.Metrics) + s.Lifecycle.SetDefaultsFrom(source.Lifecycle) + s.Chaos.SetDefaultsFrom(source.Chaos) + s.Bootstrap.SetDefaultsFrom(source.Bootstrap) +} + +// Validate the specification. +// Return errors when validation fails, nil on success. +func (s *DeploymentSpec) Validate() error { + if err := s.GetMode().Validate(); err != nil { + return maskAny(errors.Wrap(err, "spec.mode")) + } + if err := s.GetEnvironment().Validate(); err != nil { + return maskAny(errors.Wrap(err, "spec.environment")) + } + if err := s.GetStorageEngine().Validate(); err != nil { + return maskAny(errors.Wrap(err, "spec.storageEngine")) + } + if err := validatePullPolicy(s.GetImagePullPolicy()); err != nil { + return maskAny(errors.Wrap(err, "spec.imagePullPolicy")) + } + if s.GetImage() == "" { + return maskAny(errors.Wrapf(ValidationError, "spec.image must be set")) + } + if err := s.ExternalAccess.Validate(); err != nil { + return maskAny(errors.Wrap(err, "spec.externalAccess")) + } + if err := s.RocksDB.Validate(); err != nil { + return maskAny(errors.Wrap(err, "spec.rocksdb")) + } + if err := s.Authentication.Validate(false); err != nil { + return maskAny(errors.Wrap(err, "spec.auth")) + } + if err := s.TLS.Validate(); err != nil { + return maskAny(errors.Wrap(err, "spec.tls")) + } + if err := s.Sync.Validate(s.GetMode()); err != nil { + return maskAny(errors.Wrap(err, "spec.sync")) + } + if err := s.Single.Validate(ServerGroupSingle, s.GetMode().HasSingleServers(), s.GetMode(), s.GetEnvironment()); err != nil { + return maskAny(err) + } + if err := s.Agents.Validate(ServerGroupAgents, s.GetMode().HasAgents(), s.GetMode(), s.GetEnvironment()); err != nil { + return maskAny(err) + } + if err := s.DBServers.Validate(ServerGroupDBServers, s.GetMode().HasDBServers(), s.GetMode(), s.GetEnvironment()); err != nil { + return maskAny(err) + } + if err := s.Coordinators.Validate(ServerGroupCoordinators, s.GetMode().HasCoordinators(), s.GetMode(), s.GetEnvironment()); err != nil { + return maskAny(err) + } + if err := s.SyncMasters.Validate(ServerGroupSyncMasters, s.Sync.IsEnabled(), s.GetMode(), s.GetEnvironment()); err != nil { + return maskAny(err) + } + if err := s.SyncWorkers.Validate(ServerGroupSyncWorkers, s.Sync.IsEnabled(), s.GetMode(), s.GetEnvironment()); err != nil { + return maskAny(err) + } + if err := s.Metrics.Validate(); err != nil { + return maskAny(errors.Wrap(err, "spec.metrics")) + } + if err := s.Chaos.Validate(); err != nil { + return maskAny(errors.Wrap(err, "spec.chaos")) + } + if err := s.License.Validate(); err != nil { + return maskAny(errors.Wrap(err, "spec.licenseKey")) + } + if err := s.Bootstrap.Validate(); err != nil { + return maskAny(err) + } + return nil +} + +// IsDevelopment returns true when the spec contains a Development environment. +func (s DeploymentSpec) IsDevelopment() bool { + return s.GetEnvironment() == EnvironmentDevelopment +} + +// IsProduction returns true when the spec contains a Production environment. +func (s DeploymentSpec) IsProduction() bool { + return s.GetEnvironment() == EnvironmentProduction +} + +// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. +// It returns a list of fields that have been reset. +// Field names are relative to `spec.`. +func (s DeploymentSpec) ResetImmutableFields(target *DeploymentSpec) []string { + var resetFields []string + if s.GetMode() != target.GetMode() { + target.Mode = NewModeOrNil(s.Mode) + resetFields = append(resetFields, "mode") + } + if s.GetStorageEngine() != target.GetStorageEngine() { + target.StorageEngine = NewStorageEngineOrNil(s.StorageEngine) + resetFields = append(resetFields, "storageEngine") + } + if s.IsDisableIPv6() != target.IsDisableIPv6() { + target.DisableIPv6 = util.NewBoolOrNil(s.DisableIPv6) + resetFields = append(resetFields, "disableIPv6") + } + if l := s.ExternalAccess.ResetImmutableFields("externalAccess", &target.ExternalAccess); l != nil { + resetFields = append(resetFields, l...) + } + if l := s.RocksDB.ResetImmutableFields("rocksdb", &target.RocksDB); l != nil { + resetFields = append(resetFields, l...) + } + if l := s.Authentication.ResetImmutableFields("auth", &target.Authentication); l != nil { + resetFields = append(resetFields, l...) + } + if l := s.Sync.ResetImmutableFields("sync", &target.Sync); l != nil { + resetFields = append(resetFields, l...) + } + if l := s.Single.ResetImmutableFields(ServerGroupSingle, "single", &target.Single); l != nil { + resetFields = append(resetFields, l...) + } + if l := s.Agents.ResetImmutableFields(ServerGroupAgents, "agents", &target.Agents); l != nil { + resetFields = append(resetFields, l...) + } + if l := s.DBServers.ResetImmutableFields(ServerGroupDBServers, "dbservers", &target.DBServers); l != nil { + resetFields = append(resetFields, l...) + } + if l := s.Coordinators.ResetImmutableFields(ServerGroupCoordinators, "coordinators", &target.Coordinators); l != nil { + resetFields = append(resetFields, l...) + } + if l := s.SyncMasters.ResetImmutableFields(ServerGroupSyncMasters, "syncmasters", &target.SyncMasters); l != nil { + resetFields = append(resetFields, l...) + } + if l := s.SyncWorkers.ResetImmutableFields(ServerGroupSyncWorkers, "syncworkers", &target.SyncWorkers); l != nil { + resetFields = append(resetFields, l...) + } + if l := s.Metrics.ResetImmutableFields("metrics", &target.Metrics); l != nil { + resetFields = append(resetFields, l...) + } + return resetFields +} + +// Checksum return checksum of current ArangoDeployment Spec section +func (s DeploymentSpec) Checksum() (string, error) { + data, err := json.Marshal(s) + if err != nil { + return "", err + } + + return fmt.Sprintf("%0x", sha256.Sum256(data)), nil +} diff --git a/pkg/apis/deployment/v2alpha1/deployment_spec_image.go b/pkg/apis/deployment/v2alpha1/deployment_spec_image.go new file mode 100644 index 000000000..ce9b868ac --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/deployment_spec_image.go @@ -0,0 +1,55 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Adam Janikowski +// + +package v2alpha1 + +import "fmt" + +type DeploymentImageDiscoveryModeSpec string + +func NewDeploymentImageDiscoveryModeSpec(mode DeploymentImageDiscoveryModeSpec) *DeploymentImageDiscoveryModeSpec { + return &mode +} + +const ( + DeploymentImageDiscoveryDirectMode = "direct" + DeploymentImageDiscoveryKubeletMode = "kubelet" +) + +func (d DeploymentImageDiscoveryModeSpec) Validate() error { + switch d { + case DeploymentImageDiscoveryKubeletMode: + return nil + case DeploymentImageDiscoveryDirectMode: + return nil + default: + return fmt.Errorf("mode %s is not supported", d) + } +} + +func (d *DeploymentImageDiscoveryModeSpec) Get() DeploymentImageDiscoveryModeSpec { + if d == nil { + return DeploymentImageDiscoveryKubeletMode + } + + return *d +} diff --git a/pkg/apis/deployment/v2alpha1/deployment_spec_test.go b/pkg/apis/deployment/v2alpha1/deployment_spec_test.go new file mode 100644 index 000000000..b1a1dc4bf --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/deployment_spec_test.go @@ -0,0 +1,124 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" +) + +func TestDeploymentSpecValidate(t *testing.T) { + // TODO +} + +func TestDeploymentSpecSetDefaults(t *testing.T) { + def := func(spec DeploymentSpec) DeploymentSpec { + spec.SetDefaults("test") + return spec + } + + assert.Equal(t, "arangodb/arangodb:latest", def(DeploymentSpec{}).GetImage()) +} + +func TestDeploymentSpecResetImmutableFields(t *testing.T) { + tests := []struct { + Original DeploymentSpec + Target DeploymentSpec + Expected DeploymentSpec + ApplyDefaults bool + Result []string + }{ + // Valid "changes" + { + DeploymentSpec{Image: util.NewString("foo")}, + DeploymentSpec{Image: util.NewString("foo2")}, + DeploymentSpec{Image: util.NewString("foo2")}, + false, + nil, + }, + { + DeploymentSpec{Image: util.NewString("foo")}, + DeploymentSpec{Image: util.NewString("foo2")}, + DeploymentSpec{Image: util.NewString("foo2")}, + true, + nil, + }, + { + DeploymentSpec{ImagePullPolicy: util.NewPullPolicy(v1.PullAlways)}, + DeploymentSpec{ImagePullPolicy: util.NewPullPolicy(v1.PullNever)}, + DeploymentSpec{ImagePullPolicy: util.NewPullPolicy(v1.PullNever)}, + false, + nil, + }, + { + DeploymentSpec{ImagePullPolicy: util.NewPullPolicy(v1.PullAlways)}, + DeploymentSpec{ImagePullPolicy: util.NewPullPolicy(v1.PullNever)}, + DeploymentSpec{ImagePullPolicy: util.NewPullPolicy(v1.PullNever)}, + true, + nil, + }, + + // Invalid changes + { + DeploymentSpec{Mode: NewMode(DeploymentModeSingle)}, + DeploymentSpec{Mode: NewMode(DeploymentModeCluster)}, + DeploymentSpec{Mode: NewMode(DeploymentModeSingle)}, + false, + []string{"mode"}, + }, + { + DeploymentSpec{Mode: NewMode(DeploymentModeSingle)}, + DeploymentSpec{Mode: NewMode(DeploymentModeCluster)}, + DeploymentSpec{Mode: NewMode(DeploymentModeSingle)}, + true, + []string{"mode", "agents.count"}, + }, + { + DeploymentSpec{DisableIPv6: util.NewBool(false)}, + DeploymentSpec{DisableIPv6: util.NewBool(true)}, + DeploymentSpec{DisableIPv6: util.NewBool(false)}, + false, + []string{"disableIPv6"}, + }, + } + + for _, test := range tests { + if test.ApplyDefaults { + test.Original.SetDefaults("foo") + test.Expected.SetDefaults("foo") + test.Target.SetDefaultsFrom(test.Original) + test.Target.SetDefaults("foo") + } + result := test.Original.ResetImmutableFields(&test.Target) + if test.ApplyDefaults { + if len(result) > 0 { + test.Target.SetDefaults("foo") + } + } + assert.Equal(t, test.Result, result) + assert.Equal(t, test.Expected, test.Target) + } +} diff --git a/pkg/apis/deployment/v2alpha1/deployment_status.go b/pkg/apis/deployment/v2alpha1/deployment_status.go new file mode 100644 index 000000000..2c006ab47 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/deployment_status.go @@ -0,0 +1,101 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/arangodb/kube-arangodb/pkg/util" +) + +// DeploymentStatus contains the status part of a Cluster resource. +type DeploymentStatus struct { + // Phase holds the current lifetime phase of the deployment + Phase DeploymentPhase `json:"phase,omitempty"` + // Reason contains a human readable reason for reaching the current state (can be empty) + Reason string `json:"reason,omitempty"` // Reason for current state + + // AppliedVersion defines checksum of applied spec + AppliedVersion string `json:"appliedVersion"` + + // ServiceName holds the name of the Service a client can use (inside the k8s cluster) + // to access ArangoDB. + ServiceName string `json:"serviceName,omitempty"` + // SyncServiceName holds the name of the Service a client can use (inside the k8s cluster) + // to access syncmasters (only set when dc2dc synchronization is enabled). + SyncServiceName string `json:"syncServiceName,omitempty"` + + ExporterServiceName string `json:"exporterServiceName,omitempty"` + + ExporterServiceMonitorName string `json:"exporterServiceMonitorName,omitempty"` + + Restore *DeploymentRestoreResult `json:"restore,omitempty"` + + // Images holds a list of ArangoDB images with their ID and ArangoDB version. + Images ImageInfoList `json:"arangodb-images,omitempty"` + // Image that is currently being used when new pods are created + CurrentImage *ImageInfo `json:"current-image,omitempty"` + + // Members holds the status for all members in all server groups + Members DeploymentStatusMembers `json:"members"` + + // Conditions specific to the entire deployment + Conditions ConditionList `json:"conditions,omitempty"` + + // Plan to update this deployment + Plan Plan `json:"plan,omitempty"` + + // AcceptedSpec contains the last specification that was accepted by the operator. + AcceptedSpec *DeploymentSpec `json:"accepted-spec,omitempty"` + + // SecretHashes keeps a sha256 hash of secret values, so we can + // detect changes in secret values. + SecretHashes *SecretHashes `json:"secret-hashes,omitempty"` + + // Hashes keep status of hashes in deployment + Hashes DeploymentStatusHashes `json:"hashes,omitempty"` + + // ForceStatusReload if set to true forces a reload of the status from the custom resource. + ForceStatusReload *bool `json:"force-status-reload,omitempty"` +} + +// Equal checks for equality +func (ds *DeploymentStatus) Equal(other DeploymentStatus) bool { + return ds.Phase == other.Phase && + ds.Reason == other.Reason && + ds.ServiceName == other.ServiceName && + ds.SyncServiceName == other.SyncServiceName && + ds.ExporterServiceName == other.ExporterServiceName && + ds.ExporterServiceMonitorName == other.ExporterServiceMonitorName && + ds.Images.Equal(other.Images) && + ds.Restore.Equal(other.Restore) && + ds.CurrentImage.Equal(other.CurrentImage) && + ds.Members.Equal(other.Members) && + ds.Conditions.Equal(other.Conditions) && + ds.Plan.Equal(other.Plan) && + ds.AcceptedSpec.Equal(other.AcceptedSpec) && + ds.SecretHashes.Equal(other.SecretHashes) +} + +// IsForceReload returns true if ForceStatusReload is set to true +func (ds *DeploymentStatus) IsForceReload() bool { + return util.BoolOrDefault(ds.ForceStatusReload, false) +} diff --git a/pkg/apis/deployment/v2alpha1/deployment_status_members.go b/pkg/apis/deployment/v2alpha1/deployment_status_members.go new file mode 100644 index 000000000..e4d629eaa --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/deployment_status_members.go @@ -0,0 +1,287 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/pkg/errors" +) + +// DeploymentStatusMembers holds the member status of all server groups +type DeploymentStatusMembers struct { + Single MemberStatusList `json:"single,omitempty"` + Agents MemberStatusList `json:"agents,omitempty"` + DBServers MemberStatusList `json:"dbservers,omitempty"` + Coordinators MemberStatusList `json:"coordinators,omitempty"` + SyncMasters MemberStatusList `json:"syncmasters,omitempty"` + SyncWorkers MemberStatusList `json:"syncworkers,omitempty"` +} + +// Equal checks for equality +func (ds DeploymentStatusMembers) Equal(other DeploymentStatusMembers) bool { + return ds.Single.Equal(other.Single) && + ds.Agents.Equal(other.Agents) && + ds.DBServers.Equal(other.DBServers) && + ds.Coordinators.Equal(other.Coordinators) && + ds.SyncMasters.Equal(other.SyncMasters) && + ds.SyncWorkers.Equal(other.SyncWorkers) +} + +// ContainsID returns true if the given set of members contains a member with given ID. +func (ds DeploymentStatusMembers) ContainsID(id string) bool { + return ds.Single.ContainsID(id) || + ds.Agents.ContainsID(id) || + ds.DBServers.ContainsID(id) || + ds.Coordinators.ContainsID(id) || + ds.SyncMasters.ContainsID(id) || + ds.SyncWorkers.ContainsID(id) +} + +// ElementByID returns the element in the given list that has the given ID and true. +// If no such element exists, false is returned. +func (ds DeploymentStatusMembers) ElementByID(id string) (MemberStatus, ServerGroup, bool) { + if result, found := ds.Single.ElementByID(id); found { + return result, ServerGroupSingle, true + } + if result, found := ds.Agents.ElementByID(id); found { + return result, ServerGroupAgents, true + } + if result, found := ds.DBServers.ElementByID(id); found { + return result, ServerGroupDBServers, true + } + if result, found := ds.Coordinators.ElementByID(id); found { + return result, ServerGroupCoordinators, true + } + if result, found := ds.SyncMasters.ElementByID(id); found { + return result, ServerGroupSyncMasters, true + } + if result, found := ds.SyncWorkers.ElementByID(id); found { + return result, ServerGroupSyncWorkers, true + } + return MemberStatus{}, 0, false +} + +// ForeachServerGroup calls the given callback for all server groups. +// If the callback returns an error, this error is returned and the callback is +// not called for the remaining groups. +func (ds DeploymentStatusMembers) ForeachServerGroup(cb func(group ServerGroup, list MemberStatusList) error) error { + return ds.ForeachServerInGroups(cb, AllServerGroups...) +} + +func (ds DeploymentStatusMembers) ForeachServerInGroups(cb func(group ServerGroup, list MemberStatusList) error, groups ...ServerGroup) error { + for _, group := range groups { + if err := ds.ForServerGroup(cb, group); err != nil { + return err + } + } + + return nil +} + +func (ds DeploymentStatusMembers) ForServerGroup(cb func(group ServerGroup, list MemberStatusList) error, group ServerGroup) error { + switch group { + case ServerGroupSingle: + if err := cb(ServerGroupSingle, ds.Single); err != nil { + return maskAny(err) + } + case ServerGroupAgents: + if err := cb(ServerGroupAgents, ds.Agents); err != nil { + return maskAny(err) + } + case ServerGroupDBServers: + if err := cb(ServerGroupDBServers, ds.DBServers); err != nil { + return maskAny(err) + } + case ServerGroupCoordinators: + if err := cb(ServerGroupCoordinators, ds.Coordinators); err != nil { + return maskAny(err) + } + case ServerGroupSyncMasters: + if err := cb(ServerGroupSyncMasters, ds.SyncMasters); err != nil { + return maskAny(err) + } + case ServerGroupSyncWorkers: + if err := cb(ServerGroupSyncWorkers, ds.SyncWorkers); err != nil { + return maskAny(err) + } + } + return nil +} + +// MemberStatusByPodName returns a reference to the element in the given set of lists that has the given pod name. +// If no such element exists, nil is returned. +func (ds DeploymentStatusMembers) MemberStatusByPodName(podName string) (MemberStatus, ServerGroup, bool) { + if result, found := ds.Single.ElementByPodName(podName); found { + return result, ServerGroupSingle, true + } + if result, found := ds.Agents.ElementByPodName(podName); found { + return result, ServerGroupAgents, true + } + if result, found := ds.DBServers.ElementByPodName(podName); found { + return result, ServerGroupDBServers, true + } + if result, found := ds.Coordinators.ElementByPodName(podName); found { + return result, ServerGroupCoordinators, true + } + if result, found := ds.SyncMasters.ElementByPodName(podName); found { + return result, ServerGroupSyncMasters, true + } + if result, found := ds.SyncWorkers.ElementByPodName(podName); found { + return result, ServerGroupSyncWorkers, true + } + return MemberStatus{}, 0, false +} + +// MemberStatusByPVCName returns a reference to the element in the given set of lists that has the given PVC name. +// If no such element exists, nil is returned. +func (ds DeploymentStatusMembers) MemberStatusByPVCName(pvcName string) (MemberStatus, ServerGroup, bool) { + if result, found := ds.Single.ElementByPVCName(pvcName); found { + return result, ServerGroupSingle, true + } + if result, found := ds.Agents.ElementByPVCName(pvcName); found { + return result, ServerGroupAgents, true + } + if result, found := ds.DBServers.ElementByPVCName(pvcName); found { + return result, ServerGroupDBServers, true + } + // Note: Other server groups do not have PVC's so we can skip them. + return MemberStatus{}, 0, false +} + +// Add adds the given status in the given group. +func (ds *DeploymentStatusMembers) Add(status MemberStatus, group ServerGroup) error { + var err error + switch group { + case ServerGroupSingle: + err = ds.Single.add(status) + case ServerGroupAgents: + err = ds.Agents.add(status) + case ServerGroupDBServers: + err = ds.DBServers.add(status) + case ServerGroupCoordinators: + err = ds.Coordinators.add(status) + case ServerGroupSyncMasters: + err = ds.SyncMasters.add(status) + case ServerGroupSyncWorkers: + err = ds.SyncWorkers.add(status) + default: + return maskAny(errors.Wrapf(NotFoundError, "ServerGroup %d is not known", group)) + } + if err != nil { + return maskAny(err) + } + return nil +} + +// Update updates the given status in the given group. +func (ds *DeploymentStatusMembers) Update(status MemberStatus, group ServerGroup) error { + var err error + switch group { + case ServerGroupSingle: + err = ds.Single.update(status) + case ServerGroupAgents: + err = ds.Agents.update(status) + case ServerGroupDBServers: + err = ds.DBServers.update(status) + case ServerGroupCoordinators: + err = ds.Coordinators.update(status) + case ServerGroupSyncMasters: + err = ds.SyncMasters.update(status) + case ServerGroupSyncWorkers: + err = ds.SyncWorkers.update(status) + default: + return maskAny(errors.Wrapf(NotFoundError, "ServerGroup %d is not known", group)) + } + if err != nil { + return maskAny(err) + } + return nil +} + +// RemoveByID a member with given ID from the given group. +// Returns a NotFoundError if the ID of the given member or group cannot be found. +func (ds *DeploymentStatusMembers) RemoveByID(id string, group ServerGroup) error { + var err error + switch group { + case ServerGroupSingle: + err = ds.Single.removeByID(id) + case ServerGroupAgents: + err = ds.Agents.removeByID(id) + case ServerGroupDBServers: + err = ds.DBServers.removeByID(id) + case ServerGroupCoordinators: + err = ds.Coordinators.removeByID(id) + case ServerGroupSyncMasters: + err = ds.SyncMasters.removeByID(id) + case ServerGroupSyncWorkers: + err = ds.SyncWorkers.removeByID(id) + default: + return maskAny(errors.Wrapf(NotFoundError, "ServerGroup %d is not known", group)) + } + if err != nil { + return maskAny(err) + } + return nil +} + +// AllMembersReady returns true when all members, that must be ready for the given mode, are in the Ready state. +func (ds DeploymentStatusMembers) AllMembersReady(mode DeploymentMode, syncEnabled bool) bool { + syncReady := func() bool { + if syncEnabled { + return ds.SyncMasters.AllMembersReady() && ds.SyncWorkers.AllMembersReady() + } + return true + } + switch mode { + case DeploymentModeSingle: + return ds.Single.MembersReady() > 0 + case DeploymentModeActiveFailover: + return ds.Agents.AllMembersReady() && ds.Single.MembersReady() > 0 + case DeploymentModeCluster: + return ds.Agents.AllMembersReady() && + ds.DBServers.AllMembersReady() && + ds.Coordinators.AllMembersReady() && + syncReady() + default: + return false + } +} + +// MembersOfGroup returns the member list of the given group +func (ds DeploymentStatusMembers) MembersOfGroup(group ServerGroup) MemberStatusList { + switch group { + case ServerGroupSingle: + return ds.Single + case ServerGroupAgents: + return ds.Agents + case ServerGroupDBServers: + return ds.DBServers + case ServerGroupCoordinators: + return ds.Coordinators + case ServerGroupSyncMasters: + return ds.SyncMasters + case ServerGroupSyncWorkers: + return ds.SyncWorkers + default: + return MemberStatusList{} + } +} diff --git a/pkg/apis/deployment/v2alpha1/deployment_status_members_test.go b/pkg/apis/deployment/v2alpha1/deployment_status_members_test.go new file mode 100644 index 000000000..26f62df59 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/deployment_status_members_test.go @@ -0,0 +1,86 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Adam Janikowski +// + +package v2alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func newMemberList() DeploymentStatusMembers { + return DeploymentStatusMembers{ + Single: MemberStatusList{{ID: ServerGroupSingle.AsRole()}}, + Agents: MemberStatusList{{ID: ServerGroupAgents.AsRole()}}, + DBServers: MemberStatusList{{ID: ServerGroupDBServers.AsRole()}}, + Coordinators: MemberStatusList{{ID: ServerGroupCoordinators.AsRole()}}, + SyncMasters: MemberStatusList{{ID: ServerGroupSyncMasters.AsRole()}}, + SyncWorkers: MemberStatusList{{ID: ServerGroupSyncWorkers.AsRole()}}, + } +} + +func Test_StatusMemberList_EnsureDefaultExecutionOrder(t *testing.T) { + statusMembers := newMemberList() + + order := AllServerGroups + + orderIndex := 0 + + statusMembers.ForeachServerGroup(func(group ServerGroup, list MemberStatusList) error { + require.True(t, orderIndex < len(order)) + + require.Equal(t, order[orderIndex], group) + + require.Len(t, list, 1) + + require.Equal(t, order[orderIndex].AsRole(), list[0].ID) + + orderIndex += 1 + + return nil + }) +} + +func Test_StatusMemberList_CustomExecutionOrder(t *testing.T) { + statusMembers := newMemberList() + + order := []ServerGroup{ + ServerGroupDBServers, + } + + orderIndex := 0 + + statusMembers.ForeachServerInGroups(func(group ServerGroup, list MemberStatusList) error { + require.True(t, orderIndex < len(order)) + + require.Equal(t, order[orderIndex], group) + + require.Len(t, list, 1) + + require.Equal(t, order[orderIndex].AsRole(), list[0].ID) + + orderIndex += 1 + + return nil + }, order...) +} diff --git a/pkg/apis/deployment/v2alpha1/doc.go b/pkg/apis/deployment/v2alpha1/doc.go new file mode 100644 index 000000000..02b467726 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/doc.go @@ -0,0 +1,25 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// +k8s:deepcopy-gen=package +// +groupName=database.arangodb.com +package v2alpha1 diff --git a/pkg/apis/deployment/v2alpha1/duration.go b/pkg/apis/deployment/v2alpha1/duration.go new file mode 100644 index 000000000..f0d478424 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/duration.go @@ -0,0 +1,81 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "time" + + "github.com/pkg/errors" +) + +// Duration is a period of time, specified in go time.Duration format. +// This is intended to allow human friendly TTL's to be specified. +type Duration string + +// Validate the duration. +// Return errors when validation fails, nil on success. +func (d Duration) Validate() error { + if d != "" { + if _, err := time.ParseDuration(string(d)); err != nil { + return maskAny(errors.Wrapf(ValidationError, "Invalid duration: '%s': %s", string(d), err.Error())) + } + } + return nil +} + +// AsDuration parses the duration to a time.Duration value. +// In case of a parse error, 0 is returned. +func (d Duration) AsDuration() time.Duration { + if d == "" { + return 0 + } + result, err := time.ParseDuration(string(d)) + if err != nil { + return 0 + } + return result +} + +// NewDuration returns a reference to a Duration with given value. +func NewDuration(input Duration) *Duration { + return &input +} + +// NewDurationOrNil returns nil if input is nil, otherwise returns a clone of the given value. +func NewDurationOrNil(input *Duration) *Duration { + if input == nil { + return nil + } + return NewDuration(*input) +} + +// DurationOrDefault returns the default value (or empty string) if input is nil, otherwise returns the referenced value. +func DurationOrDefault(input *Duration, defaultValue ...Duration) Duration { + if input == nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return "" + } + return *input +} diff --git a/pkg/apis/deployment/v2alpha1/environment.go b/pkg/apis/deployment/v2alpha1/environment.go new file mode 100644 index 000000000..7b567343e --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/environment.go @@ -0,0 +1,77 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/pkg/errors" +) + +// Environment in which to run the cluster +type Environment string + +const ( + // EnvironmentDevelopment yields a cluster optimized for development + EnvironmentDevelopment Environment = "Development" + // EnvironmentProduction yields a cluster optimized for production + EnvironmentProduction Environment = "Production" +) + +// Validate the environment. +// Return errors when validation fails, nil on success. +func (e Environment) Validate() error { + switch e { + case EnvironmentDevelopment, EnvironmentProduction: + return nil + default: + return maskAny(errors.Wrapf(ValidationError, "Unknown environment: '%s'", string(e))) + } +} + +// IsProduction returns true when the given environment is a production environment. +func (e Environment) IsProduction() bool { + return e == EnvironmentProduction +} + +// NewEnvironment returns a reference to a string with given value. +func NewEnvironment(input Environment) *Environment { + return &input +} + +// NewEnvironmentOrNil returns nil if input is nil, otherwise returns a clone of the given value. +func NewEnvironmentOrNil(input *Environment) *Environment { + if input == nil { + return nil + } + return NewEnvironment(*input) +} + +// EnvironmentOrDefault returns the default value (or empty string) if input is nil, otherwise returns the referenced value. +func EnvironmentOrDefault(input *Environment, defaultValue ...Environment) Environment { + if input == nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return "" + } + return *input +} diff --git a/pkg/apis/deployment/v2alpha1/environment_test.go b/pkg/apis/deployment/v2alpha1/environment_test.go new file mode 100644 index 000000000..89dc95473 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/environment_test.go @@ -0,0 +1,41 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEnvironmentValidate(t *testing.T) { + // Valid + assert.Nil(t, Environment("Development").Validate()) + assert.Nil(t, Environment("Production").Validate()) + + // Not valid + assert.Error(t, Environment("").Validate()) + assert.Error(t, Environment(" development").Validate()) + assert.Error(t, Environment("development").Validate()) + assert.Error(t, Environment("production").Validate()) +} diff --git a/pkg/apis/deployment/v2alpha1/errors.go b/pkg/apis/deployment/v2alpha1/errors.go new file mode 100644 index 000000000..891bbbc0c --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/errors.go @@ -0,0 +1,53 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import "github.com/pkg/errors" + +var ( + // ValidationError indicates a validation failure + ValidationError = errors.New("validation failed") + + // AlreadyExistsError indicates an object that already exists + AlreadyExistsError = errors.New("already exists") + + // NotFoundError indicates an object that cannot be found + NotFoundError = errors.New("not found") + + maskAny = errors.WithStack +) + +// IsValidation return true when the given error is or is caused by a ValidationError. +func IsValidation(err error) bool { + return errors.Cause(err) == ValidationError +} + +// IsAlreadyExists return true when the given error is or is caused by a AlreadyExistsError. +func IsAlreadyExists(err error) bool { + return errors.Cause(err) == AlreadyExistsError +} + +// IsNotFound return true when the given error is or is caused by a NotFoundError. +func IsNotFound(err error) bool { + return errors.Cause(err) == NotFoundError +} diff --git a/pkg/apis/deployment/v2alpha1/external_access_spec.go b/pkg/apis/deployment/v2alpha1/external_access_spec.go new file mode 100644 index 000000000..e8f6eb671 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/external_access_spec.go @@ -0,0 +1,122 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "fmt" + "net" + "net/url" + + "github.com/arangodb/kube-arangodb/pkg/util" +) + +// ExternalAccessSpec holds configuration for the external access provided for the deployment. +type ExternalAccessSpec struct { + // Type of external access + Type *ExternalAccessType `json:"type,omitempty"` + // Optional port used in case of Auto or NodePort type. + NodePort *int `json:"nodePort,omitempty"` + // Optional IP used to configure a load-balancer on, in case of Auto or LoadBalancer type. + LoadBalancerIP *string `json:"loadBalancerIP,omitempty"` + // If specified and supported by the platform, this will restrict traffic through the cloud-provider + // load-balancer will be restricted to the specified client IPs. This field will be ignored if the + // cloud-provider does not support the feature. + // More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/ + LoadBalancerSourceRanges []string `json:"loadBalancerSourceRanges,omitempty"` + // Advertised Endpoint is passed to the coordinators/single servers for advertising a specific endpoint + AdvertisedEndpoint *string `json:"advertisedEndpoint,omitempty"` +} + +// GetType returns the value of type. +func (s ExternalAccessSpec) GetType() ExternalAccessType { + return ExternalAccessTypeOrDefault(s.Type, ExternalAccessTypeAuto) +} + +// GetNodePort returns the value of nodePort. +func (s ExternalAccessSpec) GetNodePort() int { + return util.IntOrDefault(s.NodePort) +} + +// GetLoadBalancerIP returns the value of loadBalancerIP. +func (s ExternalAccessSpec) GetLoadBalancerIP() string { + return util.StringOrDefault(s.LoadBalancerIP) +} + +// GetAdvertisedEndpoint returns the advertised endpoint or empty string if none was specified +func (s ExternalAccessSpec) GetAdvertisedEndpoint() string { + return util.StringOrDefault(s.AdvertisedEndpoint) +} + +// HasAdvertisedEndpoint return whether an advertised endpoint was specified or not +func (s ExternalAccessSpec) HasAdvertisedEndpoint() bool { + return s.AdvertisedEndpoint != nil +} + +// Validate the given spec +func (s ExternalAccessSpec) Validate() error { + if err := s.GetType().Validate(); err != nil { + return maskAny(err) + } + if s.AdvertisedEndpoint != nil { + ep := s.GetAdvertisedEndpoint() + if _, err := url.Parse(ep); err != nil { + return maskAny(fmt.Errorf("Failed to parse advertised endpoint '%s': %s", ep, err)) + } + } + for _, x := range s.LoadBalancerSourceRanges { + if _, _, err := net.ParseCIDR(x); err != nil { + return maskAny(fmt.Errorf("Failed to parse loadbalancer source range '%s': %s", x, err)) + } + } + return nil +} + +// SetDefaults fills in missing defaults +func (s *ExternalAccessSpec) SetDefaults() { +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *ExternalAccessSpec) SetDefaultsFrom(source ExternalAccessSpec) { + if s.Type == nil { + s.Type = NewExternalAccessTypeOrNil(source.Type) + } + if s.NodePort == nil { + s.NodePort = util.NewIntOrNil(source.NodePort) + } + if s.LoadBalancerIP == nil { + s.LoadBalancerIP = util.NewStringOrNil(source.LoadBalancerIP) + } + if s.LoadBalancerSourceRanges == nil && len(source.LoadBalancerSourceRanges) > 0 { + s.LoadBalancerSourceRanges = append([]string{}, source.LoadBalancerSourceRanges...) + } + if s.AdvertisedEndpoint == nil { + s.AdvertisedEndpoint = source.AdvertisedEndpoint + } +} + +// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. +// It returns a list of fields that have been reset. +// Field names are relative to given field prefix. +func (s ExternalAccessSpec) ResetImmutableFields(fieldPrefix string, target *ExternalAccessSpec) []string { + return nil +} diff --git a/pkg/apis/deployment/v2alpha1/external_access_type.go b/pkg/apis/deployment/v2alpha1/external_access_type.go new file mode 100644 index 000000000..ac397f053 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/external_access_type.go @@ -0,0 +1,95 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/pkg/errors" + v1 "k8s.io/api/core/v1" +) + +// ExternalAccessType specifies the type of external access provides for the deployment +type ExternalAccessType string + +const ( + // ExternalAccessTypeNone yields a cluster with no external access + ExternalAccessTypeNone ExternalAccessType = "None" + // ExternalAccessTypeAuto yields a cluster with an automatic selection for external access + ExternalAccessTypeAuto ExternalAccessType = "Auto" + // ExternalAccessTypeLoadBalancer yields a cluster with a service of type `LoadBalancer` to provide external access + ExternalAccessTypeLoadBalancer ExternalAccessType = "LoadBalancer" + // ExternalAccessTypeNodePort yields a cluster with a service of type `NodePort` to provide external access + ExternalAccessTypeNodePort ExternalAccessType = "NodePort" +) + +func (t ExternalAccessType) IsNone() bool { return t == ExternalAccessTypeNone } +func (t ExternalAccessType) IsAuto() bool { return t == ExternalAccessTypeAuto } +func (t ExternalAccessType) IsLoadBalancer() bool { return t == ExternalAccessTypeLoadBalancer } +func (t ExternalAccessType) IsNodePort() bool { return t == ExternalAccessTypeNodePort } + +// AsServiceType returns the k8s ServiceType for this ExternalAccessType. +// If type is "Auto", ServiceTypeLoadBalancer is returned. +func (t ExternalAccessType) AsServiceType() v1.ServiceType { + switch t { + case ExternalAccessTypeLoadBalancer, ExternalAccessTypeAuto: + return v1.ServiceTypeLoadBalancer + case ExternalAccessTypeNodePort: + return v1.ServiceTypeNodePort + default: + return "" + } +} + +// Validate the type. +// Return errors when validation fails, nil on success. +func (t ExternalAccessType) Validate() error { + switch t { + case ExternalAccessTypeNone, ExternalAccessTypeAuto, ExternalAccessTypeLoadBalancer, ExternalAccessTypeNodePort: + return nil + default: + return maskAny(errors.Wrapf(ValidationError, "Unknown external access type: '%s'", string(t))) + } +} + +// NewExternalAccessType returns a reference to a string with given value. +func NewExternalAccessType(input ExternalAccessType) *ExternalAccessType { + return &input +} + +// NewExternalAccessTypeOrNil returns nil if input is nil, otherwise returns a clone of the given value. +func NewExternalAccessTypeOrNil(input *ExternalAccessType) *ExternalAccessType { + if input == nil { + return nil + } + return NewExternalAccessType(*input) +} + +// ExternalAccessTypeOrDefault returns the default value (or empty string) if input is nil, otherwise returns the referenced value. +func ExternalAccessTypeOrDefault(input *ExternalAccessType, defaultValue ...ExternalAccessType) ExternalAccessType { + if input == nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return "" + } + return *input +} diff --git a/pkg/apis/deployment/v2alpha1/hashes.go b/pkg/apis/deployment/v2alpha1/hashes.go new file mode 100644 index 000000000..727c03fd5 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/hashes.go @@ -0,0 +1,51 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Adam Janikowski +// + +package v2alpha1 + +import shared "github.com/arangodb/kube-arangodb/pkg/apis/shared/v1" + +type DeploymentStatusHashes struct { + Encryption DeploymentStatusHashesEncryption `json:"rocksDBEncryption,omitempty"` + TLS DeploymentStatusHashesTLS `json:"tls,omitempty"` + JWT DeploymentStatusHashesJWT `json:"jwt,omitempty"` +} + +type DeploymentStatusHashesEncryption struct { + Keys shared.HashList `json:"keys,omitempty"` + + Propagated bool `json:"propagated,omitempty"` +} + +type DeploymentStatusHashesTLS struct { + CA *string `json:"ca,omitempty"` + Truststore shared.HashList `json:"truststore,omitempty"` + + Propagated bool `json:"propagated,omitempty"` +} + +type DeploymentStatusHashesJWT struct { + Active string `json:"active,omitempty"` + Passive shared.HashList `json:"passive,omitempty"` + + Propagated bool `json:"propagated,omitempty"` +} diff --git a/pkg/apis/deployment/v2alpha1/image_info.go b/pkg/apis/deployment/v2alpha1/image_info.go new file mode 100644 index 000000000..036b81a22 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/image_info.go @@ -0,0 +1,110 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import driver "github.com/arangodb/go-driver" + +// ImageInfo contains an ID of an image and the ArangoDB version inside the image. +type ImageInfo struct { + Image string `json:"image"` // Human provided name of the image + ImageID string `json:"image-id,omitempty"` // Unique ID (with SHA256) of the image + ArangoDBVersion driver.Version `json:"arangodb-version,omitempty"` // ArangoDB version within the image + Enterprise bool `json:"enterprise,omitempty"` // If set, this is an enterprise image +} + +// ImageInfoList is a list of image infos +type ImageInfoList []ImageInfo + +// GetByImage returns the info in the given list for the image with given name. +// If not found, false is returned. +func (l ImageInfoList) GetByImage(image string) (ImageInfo, bool) { + for _, x := range l { + if x.Image == image { + return x, true + } + } + return ImageInfo{}, false +} + +// GetByImageID returns the info in the given list for the image with given id. +// If not found, false is returned. +func (l ImageInfoList) GetByImageID(imageID string) (ImageInfo, bool) { + for _, x := range l { + if x.ImageID == imageID { + return x, true + } + } + return ImageInfo{}, false +} + +// AddOrUpdate adds the given info to the given list, if its image does not exist +// in the list. If the image does exist in the list, its entry is replaced by the given info. +// If not found, false is returned. +func (l *ImageInfoList) AddOrUpdate(info ImageInfo) { + // Look for existing entry + for i, x := range *l { + if x.Image == info.Image { + (*l)[i] = info + return + } + } + // No existing entry found, add it + *l = append(*l, info) +} + +// Equal compares to ImageInfo +func (i *ImageInfo) Equal(other *ImageInfo) bool { + if i == nil && other == nil { + return true + } else if i == nil || other == nil { + return false + } else if i == other { + return true + } + + return i.ArangoDBVersion == other.ArangoDBVersion && + i.Enterprise == other.Enterprise && + i.Image == other.Image && + i.ImageID == other.ImageID +} + +// Equal compares to ImageInfoList +func (l ImageInfoList) Equal(other ImageInfoList) bool { + if len(l) != len(other) { + return false + } + + for i := 0; i < len(l); i++ { + ii, found := l.GetByImageID(l[i].ImageID) + + if !found { + return false + } + + if !l[i].Equal(&ii) { + return false + } + } + + return true +} diff --git a/pkg/apis/deployment/v2alpha1/image_info_test.go b/pkg/apis/deployment/v2alpha1/image_info_test.go new file mode 100644 index 000000000..ac588fdfc --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/image_info_test.go @@ -0,0 +1,50 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestImageInfoList(t *testing.T) { + var list ImageInfoList + + _, found := list.GetByImage("notfound") + assert.False(t, found) + _, found = list.GetByImageID("id-notfound") + assert.False(t, found) + + list.AddOrUpdate(ImageInfo{ + Image: "foo", + ImageID: "foo-ID", + ArangoDBVersion: "1.3.4", + }) + assert.Len(t, list, 1) + + _, found = list.GetByImage("foo") + assert.True(t, found) + _, found = list.GetByImageID("foo-ID") + assert.True(t, found) +} diff --git a/pkg/apis/deployment/v2alpha1/immutable_checksum_test.go b/pkg/apis/deployment/v2alpha1/immutable_checksum_test.go new file mode 100644 index 000000000..860ea4854 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/immutable_checksum_test.go @@ -0,0 +1,65 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Adam Janikowski +// + +package v2alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +type checksumCompareCases []checksumCompareCase + +type checksumCompareCase struct { + name string + + spec DeploymentSpec + checksum string +} + +func runChecksumCompareCases(t *testing.T, cases checksumCompareCases) { + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + runChecksumCompareCase(t, c) + }) + } +} + +func runChecksumCompareCase(t *testing.T, c checksumCompareCase) { + s, err := c.spec.Checksum() + require.NoError(t, err) + + require.Equalf(t, c.checksum, s, "Checksum od ArangoDeployment mismatch") +} + +func TestImmutableSpec(t *testing.T) { + cases := checksumCompareCases{ + { + name: "Default case - from 1.0.3", + spec: DeploymentSpec{}, + checksum: "a164088b280d72c177c2eafdab7a346fb296264b70c06329b776c506925bb54e", + }, + } + + runChecksumCompareCases(t, cases) +} diff --git a/pkg/apis/deployment/v2alpha1/license_spec.go b/pkg/apis/deployment/v2alpha1/license_spec.go new file mode 100644 index 000000000..e445ac2ca --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/license_spec.go @@ -0,0 +1,59 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 + +import ( + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" +) + +// LicenseSpec holds the license related information +type LicenseSpec struct { + SecretName *string `json:"secretName,omitempty"` +} + +// HasSecretName returns true if a license key secret name was set +func (s LicenseSpec) HasSecretName() bool { + return s.SecretName != nil +} + +// GetSecretName returns the license key if set. Empty string otherwise. +func (s LicenseSpec) GetSecretName() string { + return util.StringOrDefault(s.SecretName) +} + +// Validate validates the LicenseSpec +func (s LicenseSpec) Validate() error { + if s.HasSecretName() { + if err := k8sutil.ValidateResourceName(s.GetSecretName()); err != nil { + return err + } + } + + return nil +} + +// SetDefaultsFrom fills all values not set in s with values from other +func (s LicenseSpec) SetDefaultsFrom(other LicenseSpec) { + if !s.HasSecretName() { + s.SecretName = util.NewStringOrNil(other.SecretName) + } +} diff --git a/pkg/apis/deployment/v2alpha1/license_spec_test.go b/pkg/apis/deployment/v2alpha1/license_spec_test.go new file mode 100644 index 000000000..e2a6ddfb0 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/license_spec_test.go @@ -0,0 +1,35 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 + +import ( + "testing" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/stretchr/testify/assert" +) + +func TestLicenseSpecValidation(t *testing.T) { + assert.Nil(t, LicenseSpec{SecretName: nil}.Validate()) + assert.Nil(t, LicenseSpec{SecretName: util.NewString("some-name")}.Validate()) + + assert.Error(t, LicenseSpec{SecretName: util.NewString("@@")}.Validate()) +} diff --git a/pkg/apis/deployment/v2alpha1/lifecycle_spec.go b/pkg/apis/deployment/v2alpha1/lifecycle_spec.go new file mode 100644 index 000000000..5ac9ab3fe --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/lifecycle_spec.go @@ -0,0 +1,37 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Tomasz Mielech +// + +package v2alpha1 + +import ( + v1 "k8s.io/api/core/v1" +) + +type LifecycleSpec struct { + Resources v1.ResourceRequirements `json:"resources,omitempty"` +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *LifecycleSpec) SetDefaultsFrom(source LifecycleSpec) { + setDefaultsFromResourceList(&s.Resources.Limits, source.Resources.Limits) + setDefaultsFromResourceList(&s.Resources.Requests, source.Resources.Requests) +} diff --git a/pkg/apis/deployment/v2alpha1/member_phase.go b/pkg/apis/deployment/v2alpha1/member_phase.go new file mode 100644 index 000000000..aea7717bd --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/member_phase.go @@ -0,0 +1,57 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +// MemberPhase is a strongly typed lifetime phase of a deployment member +type MemberPhase string + +const ( + // MemberPhaseNone indicates that the state is not set yet + MemberPhaseNone MemberPhase = "" + // MemberPhaseCreated indicates that all resources needed for the member have been created + MemberPhaseCreated MemberPhase = "Created" + // MemberPhaseFailed indicates that the member is gone beyond hope of recovery. It must be replaced with a new member. + MemberPhaseFailed MemberPhase = "Failed" + // MemberPhaseCleanOut indicates that a dbserver is in the process of being cleaned out + MemberPhaseCleanOut MemberPhase = "CleanOut" + // MemberPhaseDrain indicates that a dbserver is in the process of being cleaned out as result of draining a node + MemberPhaseDrain MemberPhase = "Drain" + // MemberPhaseResign indicates that a dbserver is in the process of resigning for a shutdown + MemberPhaseResign MemberPhase = "Resign" + // MemberPhaseShuttingDown indicates that a member is shutting down + MemberPhaseShuttingDown MemberPhase = "ShuttingDown" + // MemberPhaseRotating indicates that a member is being rotated + MemberPhaseRotating MemberPhase = "Rotating" + // MemberPhaseUpgrading indicates that a member is in the process of upgrading its database data format + MemberPhaseUpgrading MemberPhase = "Upgrading" +) + +// IsFailed returns true when given phase == "Failed" +func (p MemberPhase) IsFailed() bool { + return p == MemberPhaseFailed +} + +// IsCreatedOrDrain returns true when given phase is MemberPhaseCreated or MemberPhaseDrain +func (p MemberPhase) IsCreatedOrDrain() bool { + return p == MemberPhaseCreated || p == MemberPhaseDrain +} diff --git a/pkg/apis/deployment/v2alpha1/member_status.go b/pkg/apis/deployment/v2alpha1/member_status.go new file mode 100644 index 000000000..679f64f95 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/member_status.go @@ -0,0 +1,140 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "reflect" + "time" + + "k8s.io/apimachinery/pkg/types" + + driver "github.com/arangodb/go-driver" + "github.com/arangodb/kube-arangodb/pkg/util" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// MemberStatus holds the current status of a single member (server) +type MemberStatus struct { + // ID holds the unique ID of the member. + // This id is also used within the ArangoDB cluster to identify this server. + ID string `json:"id"` + // Phase holds the current lifetime phase of this member + Phase MemberPhase `json:"phase"` + // CreatedAt holds the creation timestamp of this member. + CreatedAt metav1.Time `json:"created-at"` + // PersistentVolumeClaimName holds the name of the persistent volume claim used for this member (if any). + PersistentVolumeClaimName string `json:"persistentVolumeClaimName,omitempty"` + // PodName holds the name of the Pod that currently runs this member + PodName string `json:"podName,omitempty"` + // PodUID holds the UID of the Pod that currently runs this member + PodUID types.UID `json:"podUID,omitempty"` + // PodSpecVersion holds the checksum of Pod spec that currently runs this member. Used to rotate pods + PodSpecVersion string `json:"podSpecVersion,omitempty"` + // Conditions specific to this member + Conditions ConditionList `json:"conditions,omitempty"` + // RecentTerminatons holds the times when this member was recently terminated. + // First entry is the oldest. (do not add omitempty, since we want to be able to switch from a list to an empty list) + RecentTerminations []metav1.Time `json:"recent-terminations"` + // IsInitialized is set after the very first time a pod was created for this member. + // After that, DBServers must have a UUID field or fail. + IsInitialized bool `json:"initialized"` + // CleanoutJobID holds the ID of the agency job for cleaning out this server + CleanoutJobID string `json:"cleanout-job-id,omitempty"` + // SideCarSpecs contains list of specifications specified for side cars + SideCarSpecs map[string]v1.Container `json:"sidecars-specs,omitempty"` + // ArangoVersion holds the ArangoDB version in member + ArangoVersion driver.Version `json:"arango-version,omitempty"` + // ImageId holds the members ArangoDB image ID + ImageID string `json:"image-id,omitempty"` + // Image holds image details + Image *ImageInfo `json:"image,omitempty"` +} + +// Equal checks for equality +func (s MemberStatus) Equal(other MemberStatus) bool { + return s.ID == other.ID && + s.Phase == other.Phase && + util.TimeCompareEqual(s.CreatedAt, other.CreatedAt) && + s.PersistentVolumeClaimName == other.PersistentVolumeClaimName && + s.PodName == other.PodName && + s.Conditions.Equal(other.Conditions) && + s.IsInitialized == other.IsInitialized && + s.CleanoutJobID == other.CleanoutJobID && + reflect.DeepEqual(s.SideCarSpecs, other.SideCarSpecs) && + s.ArangoVersion == other.ArangoVersion && + s.ImageID == other.ImageID && + s.Image.Equal(other.Image) +} + +// Age returns the duration since the creation timestamp of this member. +func (s MemberStatus) Age() time.Duration { + return time.Since(s.CreatedAt.Time) +} + +// RemoveTerminationsBefore removes all recent terminations before the given timestamp. +// It returns the number of terminations that have been removed. +func (s *MemberStatus) RemoveTerminationsBefore(timestamp time.Time) int { + removed := 0 + for { + if len(s.RecentTerminations) == 0 { + // Nothing left + return removed + } + if s.RecentTerminations[0].Time.Before(timestamp) { + // Let's remove it + s.RecentTerminations = s.RecentTerminations[1:] + removed++ + } else { + // First (oldest) is not before given timestamp, we're done + return removed + } + } +} + +// RecentTerminationsSince returns the number of terminations since the given timestamp. +func (s MemberStatus) RecentTerminationsSince(timestamp time.Time) int { + count := 0 + for idx := len(s.RecentTerminations) - 1; idx >= 0; idx-- { + if s.RecentTerminations[idx].Time.Before(timestamp) { + // This termination is before the timestamp, so we're done + return count + } + count++ + } + return count +} + +// IsNotReadySince returns true when the given member has not been ready since the given timestamp. +// That means it: +// - A) Was created before timestamp and never reached a ready state or +// - B) The Ready condition is set to false, and last transision is before timestamp +func (s MemberStatus) IsNotReadySince(timestamp time.Time) bool { + cond, found := s.Conditions.Get(ConditionTypeReady) + if found { + // B + return cond.Status != v1.ConditionTrue && cond.LastTransitionTime.Time.Before(timestamp) + } + // A + return s.CreatedAt.Time.Before(timestamp) +} diff --git a/pkg/apis/deployment/v2alpha1/member_status_list.go b/pkg/apis/deployment/v2alpha1/member_status_list.go new file mode 100644 index 000000000..0900affd5 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/member_status_list.go @@ -0,0 +1,212 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "math/rand" + "sort" + "time" + + "github.com/pkg/errors" + v1 "k8s.io/api/core/v1" +) + +// MemberStatusList is a list of MemberStatus entries +type MemberStatusList []MemberStatus + +// Equal checks for equality +func (l MemberStatusList) Equal(other MemberStatusList) bool { + if len(l) != len(other) { + return false + } + + for i := 0; i < len(l); i++ { + o, found := other.ElementByID(l[i].ID) + if !found { + return false + } + + if !l[i].Equal(o) { + return false + } + } + + return true +} + +// ContainsID returns true if the given list contains a member with given ID. +func (l MemberStatusList) ContainsID(id string) bool { + for _, x := range l { + if x.ID == id { + return true + } + } + return false +} + +// ElementByID returns the element in the given list that has the given ID and true. +// If no such element exists, false is returned. +func (l MemberStatusList) ElementByID(id string) (MemberStatus, bool) { + for i, x := range l { + if x.ID == id { + return l[i], true + } + } + return MemberStatus{}, false +} + +// ElementByPodName returns the element in the given list that has the given pod name and true. +// If no such element exists, an empty element and false is returned. +func (l MemberStatusList) ElementByPodName(podName string) (MemberStatus, bool) { + for i, x := range l { + if x.PodName == podName { + return l[i], true + } + } + return MemberStatus{}, false +} + +// ElementByPVCName returns the element in the given list that has the given PVC name and true. +// If no such element exists, an empty element and false is returned. +func (l MemberStatusList) ElementByPVCName(pvcName string) (MemberStatus, bool) { + for i, x := range l { + if x.PersistentVolumeClaimName == pvcName { + return l[i], true + } + } + return MemberStatus{}, false +} + +// Add a member to the list. +// Returns an AlreadyExistsError if the ID of the given member already exists. +func (l *MemberStatusList) add(m MemberStatus) error { + src := *l + for _, x := range src { + if x.ID == m.ID { + return maskAny(errors.Wrapf(AlreadyExistsError, "Member '%s' already exists", m.ID)) + } + } + newList := append(src, m) + sort.Slice(newList, func(i, j int) bool { return newList[i].ID < newList[j].ID }) + *l = newList + return nil +} + +// Update a member in the list. +// Returns a NotFoundError if the ID of the given member cannot be found. +func (l MemberStatusList) update(m MemberStatus) error { + for i, x := range l { + if x.ID == m.ID { + l[i] = m + return nil + } + } + return maskAny(errors.Wrapf(NotFoundError, "Member '%s' is not a member", m.ID)) +} + +// RemoveByID a member with given ID from the list. +// Returns a NotFoundError if the ID of the given member cannot be found. +func (l *MemberStatusList) removeByID(id string) error { + src := *l + for i, x := range src { + if x.ID == id { + *l = append(src[:i], src[i+1:]...) + return nil + } + } + return maskAny(errors.Wrapf(NotFoundError, "Member '%s' is not a member", id)) +} + +// SelectMemberToRemove selects a member from the given list that should +// be removed in a scale down action. +// Returns an error if the list is empty. +func (l MemberStatusList) SelectMemberToRemove() (MemberStatus, error) { + if len(l) > 0 { + // Try to find member with phase to be removed + for _, m := range l { + if m.Conditions.IsTrue(ConditionTypeMarkedToRemove) { + return m, nil + } + } + // Try to find a not ready member + for _, m := range l { + if m.Phase == MemberPhaseNone { + return m, nil + } + } + for _, m := range l { + if !m.Conditions.IsTrue(ConditionTypeReady) { + return m, nil + } + } + // Pick a random member that is in created state + perm := rand.Perm(len(l)) + for _, idx := range perm { + m := l[idx] + if m.Phase == MemberPhaseCreated { + return m, nil + } + } + } + return MemberStatus{}, maskAny(errors.Wrap(NotFoundError, "No member available for removal")) +} + +// MembersReady returns the number of members that are in the Ready state. +func (l MemberStatusList) MembersReady() int { + readyCount := 0 + for _, x := range l { + if x.Conditions.IsTrue(ConditionTypeReady) { + readyCount++ + } + } + return readyCount +} + +// AllMembersReady returns the true if all members are in the Ready state. +func (l MemberStatusList) AllMembersReady() bool { + return len(l) == l.MembersReady() +} + +// AllConditionTrueSince returns true if all members satisfy the condition since the given period +func (l MemberStatusList) AllConditionTrueSince(cond ConditionType, status v1.ConditionStatus, period time.Duration) bool { + for _, x := range l { + if c, ok := x.Conditions.Get(cond); ok { + if c.Status == status && c.LastTransitionTime.Time.Add(period).Before(time.Now()) { + continue + } + } + return false + } + + return true +} + +// AllFailed returns true if all members are failed +func (l MemberStatusList) AllFailed() bool { + for _, x := range l { + if !x.Phase.IsFailed() { + return false + } + } + return true +} diff --git a/pkg/apis/deployment/v2alpha1/member_status_list_test.go b/pkg/apis/deployment/v2alpha1/member_status_list_test.go new file mode 100644 index 000000000..c8649dbf4 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/member_status_list_test.go @@ -0,0 +1,91 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestMemberStatusList tests modifying a MemberStatusList. +func TestMemberStatusList(t *testing.T) { + list := &MemberStatusList{} + m1 := MemberStatus{ID: "m1"} + m2 := MemberStatus{ID: "m2"} + m3 := MemberStatus{ID: "m3"} + assert.Equal(t, 0, len(*list)) + + assert.NoError(t, list.add(m1)) + assert.Equal(t, 1, len(*list)) + + assert.NoError(t, list.add(m2)) + assert.NoError(t, list.add(m3)) + assert.Equal(t, 3, len(*list)) + + assert.Error(t, list.add(m2)) + assert.Equal(t, 3, len(*list)) + + assert.NoError(t, list.removeByID(m3.ID)) + assert.Equal(t, 2, len(*list)) + assert.False(t, list.ContainsID(m3.ID)) + assert.Equal(t, m1.ID, (*list)[0].ID) + assert.Equal(t, m2.ID, (*list)[1].ID) + + m2.PodName = "foo" + assert.NoError(t, list.update(m2)) + assert.Equal(t, 2, len(*list)) + assert.True(t, list.ContainsID(m2.ID)) + x, found := list.ElementByPodName("foo") + assert.True(t, found) + assert.Equal(t, "foo", x.PodName) + assert.Equal(t, m2.ID, x.ID) + + assert.NoError(t, list.add(m3)) + assert.Equal(t, 3, len(*list)) + assert.Equal(t, m1.ID, (*list)[0].ID) + assert.Equal(t, m2.ID, (*list)[1].ID) + assert.Equal(t, m3.ID, (*list)[2].ID) + + list2 := &MemberStatusList{m3, m2, m1} + assert.True(t, list.Equal(*list2)) + assert.True(t, list2.Equal(*list)) + + list3 := &MemberStatusList{m3, m1} + assert.False(t, list.Equal(*list3)) + assert.False(t, list3.Equal(*list)) + + list4 := MemberStatusList{m3, m2, m1} + list4[1].Phase = "something-else" + assert.False(t, list.Equal(list4)) + assert.False(t, list4.Equal(*list)) + + m4 := MemberStatus{ID: "m4"} + list5 := &MemberStatusList{m1, m2, m4} + assert.False(t, list.Equal(*list5)) + assert.False(t, list5.Equal(*list)) + + list6 := &MemberStatusList{m1, m2, m3, m4} + assert.False(t, list.Equal(*list6)) + assert.False(t, list6.Equal(*list)) +} diff --git a/pkg/apis/deployment/v2alpha1/member_status_test.go b/pkg/apis/deployment/v2alpha1/member_status_test.go new file mode 100644 index 000000000..4fb4cc240 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/member_status_test.go @@ -0,0 +1,74 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// TestMemberStatusRecentTerminations tests the functions related to MemberStatus.RecentTerminations. +func TestMemberStatusRecentTerminations(t *testing.T) { + relTime := func(delta time.Duration) metav1.Time { + return metav1.Time{Time: time.Now().Add(delta)} + } + + s := MemberStatus{} + assert.Equal(t, 0, s.RecentTerminationsSince(time.Now().Add(-time.Hour))) + assert.Equal(t, 0, s.RemoveTerminationsBefore(time.Now())) + + s.RecentTerminations = []metav1.Time{metav1.Now()} + assert.Equal(t, 1, s.RecentTerminationsSince(time.Now().Add(-time.Minute))) + assert.Equal(t, 0, s.RecentTerminationsSince(time.Now().Add(time.Minute))) + assert.Equal(t, 0, s.RemoveTerminationsBefore(time.Now().Add(-time.Hour))) + + s.RecentTerminations = []metav1.Time{relTime(-time.Hour), relTime(-time.Minute), relTime(time.Minute)} + assert.Equal(t, 3, s.RecentTerminationsSince(time.Now().Add(-time.Hour*2))) + assert.Equal(t, 2, s.RecentTerminationsSince(time.Now().Add(-time.Minute*2))) + assert.Equal(t, 2, s.RemoveTerminationsBefore(time.Now())) + assert.Len(t, s.RecentTerminations, 1) +} + +// TestMemberStatusIsNotReadySince tests the functions related to MemberStatus.IsNotReadySince. +func TestMemberStatusIsNotReadySince(t *testing.T) { + s := MemberStatus{ + CreatedAt: metav1.Now(), + } + assert.False(t, s.IsNotReadySince(time.Now().Add(-time.Hour))) + + s.CreatedAt.Time = time.Now().Add(-time.Hour) + assert.False(t, s.IsNotReadySince(time.Now().Add(-2*time.Hour))) + assert.True(t, s.IsNotReadySince(time.Now().Add(-(time.Hour - time.Minute)))) + + s.CreatedAt = metav1.Now() + s.Conditions.Update(ConditionTypeReady, true, "", "") + assert.False(t, s.IsNotReadySince(time.Now().Add(-time.Minute))) + assert.False(t, s.IsNotReadySince(time.Now().Add(time.Minute))) + + s.Conditions.Update(ConditionTypeReady, false, "", "") + assert.False(t, s.IsNotReadySince(time.Now().Add(-time.Minute))) + assert.True(t, s.IsNotReadySince(time.Now().Add(time.Minute))) +} diff --git a/pkg/apis/deployment/v2alpha1/metrics_spec.go b/pkg/apis/deployment/v2alpha1/metrics_spec.go new file mode 100644 index 000000000..56def7bf7 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/metrics_spec.go @@ -0,0 +1,161 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// +// Adam Janikowski +// + +package v2alpha1 + +import ( + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" + v1 "k8s.io/api/core/v1" +) + +// MetricsAuthenticationSpec contains spec for authentication with arangodb +type MetricsAuthenticationSpec struct { + // JWTTokenSecretName contains the name of the JWT kubernetes secret used for authentication + JWTTokenSecretName *string `json:"jwtTokenSecretName,omitempty"` +} + +// MetricsMode defines mode for metrics exporter +type MetricsMode string + +func (m MetricsMode) New() *MetricsMode { + return &m +} + +func (m MetricsMode) GetMetricsEndpoint() string { + switch m { + case MetricsModeInternal: + return k8sutil.ArangoExporterInternalEndpoint + default: + return k8sutil.ArangoExporterDefaultEndpoint + } +} + +const ( + // MetricsModeExporter exporter mode for old exporter type + MetricsModeExporter MetricsMode = "exporter" + MetricsModeSidecar MetricsMode = "sidecar" + MetricsModeInternal MetricsMode = "internal" +) + +func (m *MetricsMode) Get() MetricsMode { + if m == nil { + return MetricsModeExporter + } + + return *m +} + +// MetricsSpec contains spec for arangodb exporter +type MetricsSpec struct { + Enabled *bool `json:"enabled,omitempty"` + Image *string `json:"image,omitempty"` + Authentication MetricsAuthenticationSpec `json:"authentication,omitempty"` + Resources v1.ResourceRequirements `json:"resources,omitempty"` + Mode *MetricsMode `json:"mode,omitempty"` + TLS *bool `json:"tls,omitempty"` + + Port *uint16 `json:"port,omitempty"` +} + +func (s *MetricsSpec) IsTLS() bool { + if s == nil || s.TLS == nil { + return true + } + + return *s.TLS +} + +func (s *MetricsSpec) GetPort() uint16 { + if s == nil || s.Port == nil { + return k8sutil.ArangoExporterPort + } + + return *s.Port +} + +// IsEnabled returns whether metrics are enabled or not +func (s *MetricsSpec) IsEnabled() bool { + return util.BoolOrDefault(s.Enabled, false) +} + +// HasImage returns whether a image was specified or not +func (s *MetricsSpec) HasImage() bool { + return s.Image != nil +} + +// GetImage returns the Image or empty string +func (s *MetricsSpec) GetImage() string { + return util.StringOrDefault(s.Image) +} + +// SetDefaults sets default values +func (s *MetricsSpec) SetDefaults(defaultTokenName string, isAuthenticated bool) { + if s.Enabled == nil { + s.Enabled = util.NewBool(false) + } + if s.GetJWTTokenSecretName() == "" { + s.Authentication.JWTTokenSecretName = util.NewString(defaultTokenName) + } +} + +// GetJWTTokenSecretName returns the token secret name or empty string +func (s *MetricsSpec) GetJWTTokenSecretName() string { + return util.StringOrDefault(s.Authentication.JWTTokenSecretName) +} + +// HasJWTTokenSecretName returns true if a secret name was specified +func (s *MetricsSpec) HasJWTTokenSecretName() bool { + return s.Authentication.JWTTokenSecretName != nil +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *MetricsSpec) SetDefaultsFrom(source MetricsSpec) { + if s.Enabled == nil { + s.Enabled = util.NewBoolOrNil(source.Enabled) + } + if s.Image == nil { + s.Image = util.NewStringOrNil(source.Image) + } + if s.Authentication.JWTTokenSecretName == nil { + s.Authentication.JWTTokenSecretName = util.NewStringOrNil(source.Authentication.JWTTokenSecretName) + } + setDefaultsFromResourceList(&s.Resources.Limits, source.Resources.Limits) + setDefaultsFromResourceList(&s.Resources.Requests, source.Resources.Requests) +} + +// Validate the given spec +func (s *MetricsSpec) Validate() error { + + if s.HasJWTTokenSecretName() { + if err := k8sutil.ValidateResourceName(s.GetJWTTokenSecretName()); err != nil { + return err + } + } + + return nil +} + +// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. +func (s MetricsSpec) ResetImmutableFields(fieldPrefix string, target *MetricsSpec) []string { + return nil +} diff --git a/pkg/apis/deployment/v2alpha1/monitoring_spec.go b/pkg/apis/deployment/v2alpha1/monitoring_spec.go new file mode 100644 index 000000000..23d41421a --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/monitoring_spec.go @@ -0,0 +1,62 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" +) + +// MonitoringSpec holds monitoring specific configuration settings +type MonitoringSpec struct { + TokenSecretName *string `json:"tokenSecretName,omitempty"` +} + +// GetTokenSecretName returns the value of tokenSecretName. +func (s MonitoringSpec) GetTokenSecretName() string { + return util.StringOrDefault(s.TokenSecretName) +} + +// Validate the given spec +func (s MonitoringSpec) Validate() error { + if err := k8sutil.ValidateOptionalResourceName(s.GetTokenSecretName()); err != nil { + return maskAny(err) + } + return nil +} + +// SetDefaults fills in missing defaults +func (s *MonitoringSpec) SetDefaults(defaultTokenSecretName string) { + if s.GetTokenSecretName() == "" { + // Note that we don't check for nil here, since even a specified, but empty + // string should result in the default value. + s.TokenSecretName = util.NewString(defaultTokenSecretName) + } +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *MonitoringSpec) SetDefaultsFrom(source MonitoringSpec) { + if s.TokenSecretName == nil { + s.TokenSecretName = util.NewStringOrNil(source.TokenSecretName) + } +} diff --git a/pkg/apis/deployment/v2alpha1/monitoring_spec_test.go b/pkg/apis/deployment/v2alpha1/monitoring_spec_test.go new file mode 100644 index 000000000..7dd639d15 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/monitoring_spec_test.go @@ -0,0 +1,56 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/stretchr/testify/assert" +) + +func TestMonitoringSpecValidate(t *testing.T) { + // Valid + assert.Nil(t, MonitoringSpec{TokenSecretName: nil}.Validate()) + assert.Nil(t, MonitoringSpec{TokenSecretName: util.NewString("")}.Validate()) + assert.Nil(t, MonitoringSpec{TokenSecretName: util.NewString("foo")}.Validate()) + assert.Nil(t, MonitoringSpec{TokenSecretName: util.NewString("foo")}.Validate()) + + // Not valid + assert.Error(t, MonitoringSpec{TokenSecretName: util.NewString("Foo")}.Validate()) +} + +func TestMonitoringSpecSetDefaults(t *testing.T) { + def := func(spec MonitoringSpec) MonitoringSpec { + spec.SetDefaults("") + return spec + } + def2 := func(spec MonitoringSpec) MonitoringSpec { + spec.SetDefaults("def2") + return spec + } + + assert.Equal(t, "", def(MonitoringSpec{}).GetTokenSecretName()) + assert.Equal(t, "def2", def2(MonitoringSpec{}).GetTokenSecretName()) + assert.Equal(t, "foo", def(MonitoringSpec{TokenSecretName: util.NewString("foo")}).GetTokenSecretName()) +} diff --git a/pkg/apis/deployment/v2alpha1/percent.go b/pkg/apis/deployment/v2alpha1/percent.go new file mode 100644 index 000000000..07e9d9b3e --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/percent.go @@ -0,0 +1,62 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/pkg/errors" +) + +// Percent is a percentage between 0 and 100. +type Percent int + +// Validate the given percentage. +func (p Percent) Validate() error { + if p < 0 || p > 100 { + return maskAny(errors.Wrapf(ValidationError, "Percentage must be between 0 and 100, got %d", int(p))) + } + return nil +} + +// NewPercent returns a reference to a percent with given value. +func NewPercent(input Percent) *Percent { + return &input +} + +// NewPercentOrNil returns nil if input is nil, otherwise returns a clone of the given value. +func NewPercentOrNil(input *Percent) *Percent { + if input == nil { + return nil + } + return NewPercent(*input) +} + +// PercentOrDefault returns the default value or 0 if input is nil, otherwise returns the referenced value. +func PercentOrDefault(input *Percent, defaultValue ...Percent) Percent { + if input == nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return 0 + } + return *input +} diff --git a/pkg/apis/deployment/v2alpha1/plan.go b/pkg/apis/deployment/v2alpha1/plan.go new file mode 100644 index 000000000..28ab34b5d --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/plan.go @@ -0,0 +1,245 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/dchest/uniuri" + "k8s.io/apimachinery/pkg/api/equality" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ActionType is a strongly typed name for a plan action item +type ActionType string + +func (a ActionType) String() string { + return string(a) +} + +const ( + // ActionTypeIdle causes a plan to be recalculated. + ActionTypeIdle ActionType = "Idle" + // ActionTypeAddMember causes a member to be added. + ActionTypeAddMember ActionType = "AddMember" + // ActionTypeMarkToRemoveMember marks member to be removed. + ActionTypeMarkToRemoveMember ActionType = "MarkToRemoveMember" + // ActionTypeRemoveMember causes a member to be removed. + ActionTypeRemoveMember ActionType = "RemoveMember" + // ActionTypeRecreateMember recreates member. Used when member is still owner of some shards. + ActionTypeRecreateMember ActionType = "RecreateMember" + // ActionTypeCleanOutMember causes a member to be cleaned out (dbserver only). + ActionTypeCleanOutMember ActionType = "CleanOutMember" + // ActionTypeShutdownMember causes a member to be shutdown and removed from the cluster. + ActionTypeShutdownMember ActionType = "ShutdownMember" + // ActionTypeRotateMember causes a member to be shutdown and have it's pod removed. + ActionTypeRotateMember ActionType = "RotateMember" + // ActionTypeRotateStartMember causes a member to be shutdown and have it's pod removed. Do not wait to pod recover. + ActionTypeRotateStartMember ActionType = "RotateStartMember" + // ActionTypeRotateMember causes a member to be restored. + ActionTypeRotateStopMember ActionType = "RotateStopMember" + // ActionTypeUpgradeMember causes a member to be shutdown and have it's pod removed, restarted with AutoUpgrade option, waited until termination and the restarted again. + ActionTypeUpgradeMember ActionType = "UpgradeMember" + // ActionTypeWaitForMemberUp causes the plan to wait until the member is considered "up". + ActionTypeWaitForMemberUp ActionType = "WaitForMemberUp" + // ActionTypeWaitForMemberInSync causes the plan to wait until members are considered "up" and cluster is healthy. + ActionTypeWaitForMemberInSync ActionType = "WaitForMemberInSync" + // ActionTypeRenewTLSCertificate causes the TLS certificate of a member to be renewed. + ActionTypeRenewTLSCertificate ActionType = "RenewTLSCertificate" + // ActionTypeRenewTLSCACertificate causes the TLS CA certificate of the entire deployment to be renewed. + ActionTypeRenewTLSCACertificate ActionType = "RenewTLSCACertificate" + // ActionTypeAppendTLSCACertificate add TLS CA certificate to local truststore. + ActionTypeAppendTLSCACertificate ActionType = "AppendTLSCACertificate" + // ActionTypeCleanTLSCACertificate clean TLS CA certificate from local truststore. + ActionTypeCleanTLSCACertificate ActionType = "CleanTLSCACertificate" + // ActionTypeCleanTLSKeyfileCertificate clean server keyfile + ActionTypeCleanTLSKeyfileCertificate ActionType = "CleanTLSKeyfileCertificate" + // ActionTypeRefreshTLSKeyfileCertificate refresh server keyfile using API + ActionTypeRefreshTLSKeyfileCertificate ActionType = "RefreshTLSKeyfileCertificate" + // ActionTypeTLSKeyStatusUpdate update status with current data from deployment + ActionTypeTLSKeyStatusUpdate ActionType = "TLSKeyStatusUpdate" + // ActionTypeTLSPropagated change propagated flag + ActionTypeTLSPropagated ActionType = "TLSPropagated" + // ActionTypeUpdateTLSSNI update SNI inplace. + ActionTypeUpdateTLSSNI ActionType = "UpdateTLSSNI" + // ActionTypeSetCurrentImage causes status.CurrentImage to be updated to the image given in the action. + ActionTypeSetCurrentImage ActionType = "SetCurrentImage" + // ActionTypeSetCurrentImage replace image of member to current one. + ActionTypeSetMemberCurrentImage ActionType = "SetMemberCurrentImage" + // ActionTypeDisableClusterScaling turns off scaling DBservers and coordinators + ActionTypeDisableClusterScaling ActionType = "ScalingDisabled" + // ActionTypeEnableClusterScaling turns on scaling DBservers and coordinators + ActionTypeEnableClusterScaling ActionType = "ScalingEnabled" + // ActionTypePVCResize resize event for PVC + ActionTypePVCResize ActionType = "PVCResize" + // ActionTypePVCResized waits for PVC to resize for defined time + ActionTypePVCResized ActionType = "PVCResized" + // UpToDateUpdateResized define up to date annotation in spec + UpToDateUpdate ActionType = "UpToDateUpdate" + // ActionTypeBackupRestore restore plan + ActionTypeBackupRestore ActionType = "BackupRestore" + // ActionTypeBackupRestoreClean restore plan + ActionTypeBackupRestoreClean ActionType = "BackupRestoreClean" + // ActionTypeEncryptionKeyAdd add new encryption key to list + ActionTypeEncryptionKeyAdd ActionType = "EncryptionKeyAdd" + // ActionTypeEncryptionKeyRemove removes encryption key to list + ActionTypeEncryptionKeyRemove ActionType = "EncryptionKeyRemove" + // ActionTypeEncryptionKeyRefresh refresh encryption keys + ActionTypeEncryptionKeyRefresh ActionType = "EncryptionKeyRefresh" + // ActionTypeEncryptionKeyStatusUpdate update status object with current encryption keys + ActionTypeEncryptionKeyStatusUpdate ActionType = "EncryptionKeyStatusUpdate" + // ActionTypeEncryptionKeyPropagated change propagated flag + ActionTypeEncryptionKeyPropagated ActionType = "EncryptionKeyPropagated" + // ActionTypeJWTStatusUpdate update status of JWT Secret + ActionTypeJWTStatusUpdate ActionType = "JWTStatusUpdate" + // ActionTypeJWTSetActive change active JWT key + ActionTypeJWTSetActive ActionType = "JWTSetActive" + // ActionTypeJWTAdd add new JWT key + ActionTypeJWTAdd ActionType = "JWTAdd" + // ActionTypeJWTClean Clean old JWT key + ActionTypeJWTClean ActionType = "JWTClean" + // ActionTypeJWTRefresh refresh jwt tokens + ActionTypeJWTRefresh ActionType = "JWTRefresh" + // ActionTypeJWTPropagated change propagated flag + ActionTypeJWTPropagated ActionType = "JWTPropagated" + // ActionTypeClusterMemberCleanup removes member from cluster + ActionTypeClusterMemberCleanup ActionType = "ClusterMemberCleanup" + // ActionTypeEnableMaintenance enables maintenance on cluster. + ActionTypeEnableMaintenance ActionType = "EnableMaintenance" + // ActionTypeEnableMaintenance disables maintenance on cluster. + ActionTypeDisableMaintenance ActionType = "DisableMaintenance" + // ActionTypeBootstrapUpdate update bootstrap status to true + ActionTypeBootstrapUpdate ActionType = "BootstrapUpdate" + // ActionTypeBootstrapSetPassword set password to the bootstrapped user + ActionTypeBootstrapSetPassword ActionType = "BootstrapSetPassword" +) + +const ( + // MemberIDPreviousAction is used for Action.MemberID when the MemberID + // should be derived from the previous action. + MemberIDPreviousAction = "@previous" +) + +// Action represents a single action to be taken to update a deployment. +type Action struct { + // ID of this action (unique for every action) + ID string `json:"id"` + // Type of action. + Type ActionType `json:"type"` + // ID reference of the member involved in this action (if any) + MemberID string `json:"memberID,omitempty"` + // Group involved in this action + Group ServerGroup `json:"group,omitempty"` + // CreationTime is set the when the action is created. + CreationTime metav1.Time `json:"creationTime"` + // StartTime is set the when the action has been started, but needs to wait to be finished. + StartTime *metav1.Time `json:"startTime,omitempty"` + // Reason for this action + Reason string `json:"reason,omitempty"` + // Image used in can of a SetCurrentImage action. + Image string `json:"image,omitempty"` + // Params additional parameters used for action + Params map[string]string `json:"params,omitempty"` +} + +// Equal compares two Actions +func (a Action) Equal(other Action) bool { + return a.ID == other.ID && + a.Type == other.Type && + a.MemberID == other.MemberID && + a.Group == other.Group && + util.TimeCompareEqual(a.CreationTime, other.CreationTime) && + util.TimeCompareEqualPointer(a.StartTime, other.StartTime) && + a.Reason == other.Reason && + a.Image == other.Image && + equality.Semantic.DeepEqual(a.Params, other.Params) +} + +// AddParam returns copy of action with set parameter +func (a Action) AddParam(key, value string) Action { + if a.Params == nil { + a.Params = map[string]string{} + } + + a.Params[key] = value + + return a +} + +// GetParam returns action parameter +func (a Action) GetParam(key string) (string, bool) { + if a.Params == nil { + return "", false + } + + i, ok := a.Params[key] + + return i, ok +} + +// NewAction instantiates a new Action. +func NewAction(actionType ActionType, group ServerGroup, memberID string, reason ...string) Action { + a := Action{ + ID: uniuri.New(), + Type: actionType, + MemberID: memberID, + Group: group, + CreationTime: metav1.Now(), + } + if len(reason) != 0 { + a.Reason = reason[0] + } + return a +} + +// SetImage sets the Image field to the given value and returns the modified +// action. +func (a Action) SetImage(image string) Action { + a.Image = image + return a +} + +// Plan is a list of actions that will be taken to update a deployment. +// Only 1 action is in progress at a time. The operator will wait for that +// action to be completely and then remove the action. +type Plan []Action + +// Equal compares two Plan +func (p Plan) Equal(other Plan) bool { + // For plan the order is relevant! + if len(p) != len(other) { + return false + } + + for i := 0; i < len(p); i++ { + if !p[i].Equal(other[i]) { + return false + } + } + + return true +} + +// IsEmpty checks if plan is empty +func (p Plan) IsEmpty() bool { + return len(p) == 0 +} diff --git a/pkg/apis/deployment/v2alpha1/pvc.go b/pkg/apis/deployment/v2alpha1/pvc.go new file mode 100644 index 000000000..2223399fb --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/pvc.go @@ -0,0 +1,42 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Adam Janikowski +// + +package v2alpha1 + +type PVCResizeMode string + +const ( + PVCResizeModeRuntime PVCResizeMode = "runtime" + PVCResizeModeRotate PVCResizeMode = "rotate" +) + +func (p *PVCResizeMode) Get() PVCResizeMode { + if p == nil { + return PVCResizeModeRuntime + } + + return *p +} + +func (p PVCResizeMode) String() string { + return string(p) +} diff --git a/pkg/apis/deployment/v2alpha1/recovery_spec.go b/pkg/apis/deployment/v2alpha1/recovery_spec.go new file mode 100644 index 000000000..c66d60a94 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/recovery_spec.go @@ -0,0 +1,41 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Adam Janikowski +// + +package v2alpha1 + +import "github.com/arangodb/kube-arangodb/pkg/util" + +type ArangoDeploymentRecoverySpec struct { + AutoRecover *bool `json:"autoRecover"` +} + +func (a *ArangoDeploymentRecoverySpec) Get() ArangoDeploymentRecoverySpec { + if a != nil { + return *a + } + + return ArangoDeploymentRecoverySpec{} +} + +func (a ArangoDeploymentRecoverySpec) GetAutoRecover() bool { + return util.BoolOrDefault(a.AutoRecover, false) +} diff --git a/pkg/apis/deployment/v2alpha1/register.go b/pkg/apis/deployment/v2alpha1/register.go new file mode 100644 index 000000000..f0e26301a --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/register.go @@ -0,0 +1,56 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/arangodb/kube-arangodb/pkg/apis/deployment" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +const ( + ArangoDeploymentVersion = "v2alpha1" +) + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme + + SchemeGroupVersion = schema.GroupVersion{Group: deployment.ArangoDeploymentGroupName, Version: ArangoDeploymentVersion} +) + +// Resource gets an ArangoCluster GroupResource for a specified resource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +// addKnownTypes adds the set of types defined in this package to the supplied scheme. +func addKnownTypes(s *runtime.Scheme) error { + s.AddKnownTypes(SchemeGroupVersion, + &ArangoDeployment{}, + &ArangoDeploymentList{}, + ) + metav1.AddToGroupVersion(s, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/deployment/v2alpha1/rocksdb_spec.go b/pkg/apis/deployment/v2alpha1/rocksdb_spec.go new file mode 100644 index 000000000..c4e3da739 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/rocksdb_spec.go @@ -0,0 +1,88 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" +) + +// RocksDBEncryptionSpec holds rocksdb encryption at rest specific configuration settings +type RocksDBEncryptionSpec struct { + KeySecretName *string `json:"keySecretName,omitempty"` +} + +// GetKeySecretName returns the value of keySecretName. +func (s RocksDBEncryptionSpec) GetKeySecretName() string { + return util.StringOrDefault(s.KeySecretName) +} + +// IsEncrypted returns true when an encryption key secret name is provided, +// false otherwise. +func (s RocksDBEncryptionSpec) IsEncrypted() bool { + return s.GetKeySecretName() != "" +} + +// RocksDBSpec holds rocksdb specific configuration settings +type RocksDBSpec struct { + Encryption RocksDBEncryptionSpec `json:"encryption"` +} + +// IsEncrypted returns true when an encryption key secret name is provided, +// false otherwise. +func (s RocksDBSpec) IsEncrypted() bool { + return s.Encryption.IsEncrypted() +} + +// Validate the given spec +func (s RocksDBSpec) Validate() error { + if err := k8sutil.ValidateOptionalResourceName(s.Encryption.GetKeySecretName()); err != nil { + return maskAny(err) + } + return nil +} + +// SetDefaults fills in missing defaults +func (s *RocksDBSpec) SetDefaults() { + // Nothing needed +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *RocksDBSpec) SetDefaultsFrom(source RocksDBSpec) { + if s.Encryption.KeySecretName == nil { + s.Encryption.KeySecretName = util.NewStringOrNil(source.Encryption.KeySecretName) + } +} + +// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. +// It returns a list of fields that have been reset. +// Field names are relative to given field prefix. +func (s RocksDBSpec) ResetImmutableFields(fieldPrefix string, target *RocksDBSpec) []string { + var resetFields []string + if s.IsEncrypted() != target.IsEncrypted() { + // Note: You can change the name, but not from empty to non-empty (or reverse). + target.Encryption.KeySecretName = util.NewStringOrNil(s.Encryption.KeySecretName) + resetFields = append(resetFields, fieldPrefix+".encryption.keySecretName") + } + return resetFields +} diff --git a/pkg/apis/deployment/v2alpha1/rocksdb_spec_test.go b/pkg/apis/deployment/v2alpha1/rocksdb_spec_test.go new file mode 100644 index 000000000..1f74ead03 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/rocksdb_spec_test.go @@ -0,0 +1,97 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/stretchr/testify/assert" +) + +func TestRocksDBSpecValidate(t *testing.T) { + // Valid + assert.Nil(t, RocksDBSpec{}.Validate()) + assert.Nil(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}.Validate()) + + // Not valid + assert.Error(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("Foo")}}.Validate()) +} + +func TestRocksDBSpecIsEncrypted(t *testing.T) { + assert.False(t, RocksDBSpec{}.IsEncrypted()) + assert.False(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("")}}.IsEncrypted()) + assert.True(t, RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}.IsEncrypted()) +} + +func TestRocksDBSpecSetDefaults(t *testing.T) { + def := func(spec RocksDBSpec) RocksDBSpec { + spec.SetDefaults() + return spec + } + + assert.Equal(t, "", def(RocksDBSpec{}).Encryption.GetKeySecretName()) +} + +func TestRocksDBSpecResetImmutableFields(t *testing.T) { + tests := []struct { + Original RocksDBSpec + Target RocksDBSpec + Expected RocksDBSpec + Result []string + }{ + // Valid "changes" + { + RocksDBSpec{}, + RocksDBSpec{}, + RocksDBSpec{}, + nil, + }, + { + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}, + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}, + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}, + nil, + }, + { + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}, + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo2")}}, + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo2")}}, + nil, + }, + + // Invalid changes + { + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}, + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("")}}, + RocksDBSpec{Encryption: RocksDBEncryptionSpec{KeySecretName: util.NewString("foo")}}, + []string{"test.encryption.keySecretName"}, + }, + } + + for _, test := range tests { + result := test.Original.ResetImmutableFields("test", &test.Target) + assert.Equal(t, test.Result, result) + assert.Equal(t, test.Expected, test.Target) + } +} diff --git a/pkg/apis/deployment/v2alpha1/secret_hashes.go b/pkg/apis/deployment/v2alpha1/secret_hashes.go new file mode 100644 index 000000000..bb90cbeb7 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/secret_hashes.go @@ -0,0 +1,88 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +// SecretHashes keeps track of the value of secrets +// so we can detect changes. +// For each used secret, a sha256 hash is stored. +type SecretHashes struct { + // AuthJWT contains the hash of the auth.jwtSecretName secret + AuthJWT string `json:"auth-jwt,omitempty"` + // RocksDBEncryptionKey contains the hash of the rocksdb.encryption.keySecretName secret + RocksDBEncryptionKey string `json:"rocksdb-encryption-key,omitempty"` + // TLSCA contains the hash of the tls.caSecretName secret + TLSCA string `json:"tls-ca,omitempty"` + // SyncTLSCA contains the hash of the sync.tls.caSecretName secret + SyncTLSCA string `json:"sync-tls-ca,omitempty"` + // User's map contains hashes for each user + Users map[string]string `json:"users,omitempty"` +} + +// Equal compares two SecretHashes +func (sh *SecretHashes) Equal(other *SecretHashes) bool { + if sh == nil || other == nil { + return false + } else if sh == other { + return true + } + + return sh.AuthJWT == other.AuthJWT && + sh.RocksDBEncryptionKey == other.RocksDBEncryptionKey && + sh.TLSCA == other.TLSCA && + sh.SyncTLSCA == other.SyncTLSCA && + isStringMapEqual(sh.Users, other.Users) +} + +// NewEmptySecretHashes creates new empty structure +func NewEmptySecretHashes() *SecretHashes { + sh := &SecretHashes{} + sh.Users = make(map[string]string) + return sh +} + +func isStringMapEqual(first map[string]string, second map[string]string) bool { + if first == nil && second == nil { + return true + } + + if first == nil || second == nil { + return false + } + + if len(first) != len(second) { + return false + } + + for key, valueF := range first { + valueS, ok := second[key] + if !ok { + return false + } + + if valueF != valueS { + return false + } + } + + return true +} diff --git a/pkg/apis/deployment/v2alpha1/secret_hashes_test.go b/pkg/apis/deployment/v2alpha1/secret_hashes_test.go new file mode 100644 index 000000000..4cbdbbe22 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/secret_hashes_test.go @@ -0,0 +1,164 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 tomasz@arangodb.con +// + +package v2alpha1 + +import ( + "github.com/magiconair/properties/assert" + + "testing" +) + +func TestSecretHashes_Equal(t *testing.T) { + // Arrange + sh := SecretHashes{} + testCases := []struct { + Name string + CompareFrom *SecretHashes + CompareTo *SecretHashes + Expected bool + }{ + { + Name: "Parameter can not be nil", + CompareFrom: &SecretHashes{}, + Expected: false, + }, + { + Name: "The addresses are the same", + CompareFrom: &sh, + CompareTo: &sh, + Expected: true, + }, + { + Name: "JWT token is different", + CompareFrom: &SecretHashes{ + AuthJWT: "1", + }, + CompareTo: &SecretHashes{ + AuthJWT: "2", + }, + Expected: false, + }, + { + Name: "Users are different", + CompareFrom: &SecretHashes{ + Users: map[string]string{ + "root": "", + }, + }, + CompareTo: &SecretHashes{}, + Expected: false, + }, + { + Name: "User's table size is different", + CompareFrom: &SecretHashes{ + Users: map[string]string{ + "root": "", + }, + }, + CompareTo: &SecretHashes{ + Users: map[string]string{ + "root": "", + "user": "", + }, + }, + Expected: false, + }, + { + Name: "User's table has got different users", + CompareFrom: &SecretHashes{ + Users: map[string]string{ + "root": "", + }, + }, + CompareTo: &SecretHashes{ + Users: map[string]string{ + "user": "", + }, + }, + Expected: false, + }, + { + Name: "User's table has got different hashes for users", + CompareFrom: &SecretHashes{ + Users: map[string]string{ + "root": "123", + }, + }, + CompareTo: &SecretHashes{ + Users: map[string]string{ + "root": "1234", + }, + }, + Expected: false, + }, + { + Name: "Secret hashes are the same", + CompareFrom: &SecretHashes{ + AuthJWT: "1", + RocksDBEncryptionKey: "2", + TLSCA: "3", + SyncTLSCA: "4", + Users: map[string]string{ + "root": "123", + }, + }, + CompareTo: &SecretHashes{ + AuthJWT: "1", + RocksDBEncryptionKey: "2", + TLSCA: "3", + SyncTLSCA: "4", + Users: map[string]string{ + "root": "123", + }, + }, + Expected: true, + }, + { + Name: "Secret hashes are the same without users", + CompareFrom: &SecretHashes{ + AuthJWT: "1", + RocksDBEncryptionKey: "2", + TLSCA: "3", + SyncTLSCA: "4", + }, + CompareTo: &SecretHashes{ + AuthJWT: "1", + RocksDBEncryptionKey: "2", + TLSCA: "3", + SyncTLSCA: "4", + }, + Expected: true, + }, + } + + for _, testCase := range testCases { + //nolint:scopelint + t.Run(testCase.Name, func(t *testing.T) { + // Act + expected := testCase.CompareFrom.Equal(testCase.CompareTo) + + // Assert + assert.Equal(t, testCase.Expected, expected) + }) + } +} diff --git a/pkg/apis/deployment/v2alpha1/server_group.go b/pkg/apis/deployment/v2alpha1/server_group.go new file mode 100644 index 000000000..b35d10474 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/server_group.go @@ -0,0 +1,200 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import "time" + +type ServerGroup int + +const ( + ServerGroupUnknown ServerGroup = 0 + ServerGroupSingle ServerGroup = 1 + ServerGroupAgents ServerGroup = 2 + ServerGroupDBServers ServerGroup = 3 + ServerGroupCoordinators ServerGroup = 4 + ServerGroupSyncMasters ServerGroup = 5 + ServerGroupSyncWorkers ServerGroup = 6 + ServerGroupImageDiscovery ServerGroup = -1 + + ServerGroupSingleString = "single" + ServerGroupAgentsString = "agent" + ServerGroupDBServersString = "dbserver" + ServerGroupCoordinatorsString = "coordinator" + ServerGroupSyncMastersString = "syncmaster" + ServerGroupSyncWorkersString = "syncworker" + ServerGroupImageDiscoveryString = "imagediscovery" + + ServerGroupSingleAbbreviatedString = "sngl" + ServerGroupAgentsAbbreviatedString = "agnt" + ServerGroupDBServersAbbreviatedString = "prmr" + ServerGroupCoordinatorsAbbreviatedString = "crdn" + ServerGroupSyncMastersAbbreviatedString = "syma" + ServerGroupSyncWorkersAbbreviatedString = "sywo" + ServerGroupImageDiscoveryAbbreviatedString = "id" +) + +var ( + // AllServerGroups contains a constant list of all known server groups + AllServerGroups = []ServerGroup{ + ServerGroupAgents, + ServerGroupSingle, + ServerGroupDBServers, + ServerGroupCoordinators, + ServerGroupSyncMasters, + ServerGroupSyncWorkers, + } +) + +// AsRole returns the "role" value for the given group. +func (g ServerGroup) AsRole() string { + switch g { + case ServerGroupSingle: + return ServerGroupSingleString + case ServerGroupAgents: + return ServerGroupAgentsString + case ServerGroupDBServers: + return ServerGroupDBServersString + case ServerGroupCoordinators: + return ServerGroupCoordinatorsString + case ServerGroupSyncMasters: + return ServerGroupSyncMastersString + case ServerGroupSyncWorkers: + return ServerGroupSyncWorkersString + default: + return "?" + } +} + +// AsRoleAbbreviated returns the abbreviation of the "role" value for the given group. +func (g ServerGroup) AsRoleAbbreviated() string { + switch g { + case ServerGroupSingle: + return ServerGroupSingleAbbreviatedString + case ServerGroupAgents: + return ServerGroupAgentsAbbreviatedString + case ServerGroupDBServers: + return ServerGroupDBServersAbbreviatedString + case ServerGroupCoordinators: + return ServerGroupCoordinatorsAbbreviatedString + case ServerGroupSyncMasters: + return ServerGroupSyncMastersAbbreviatedString + case ServerGroupSyncWorkers: + return ServerGroupSyncWorkersAbbreviatedString + default: + return "?" + } +} + +// DefaultTerminationGracePeriod returns the default period between SIGTERM & SIGKILL for a server in the given group. +func (g ServerGroup) DefaultTerminationGracePeriod() time.Duration { + switch g { + case ServerGroupSingle: + return time.Minute + case ServerGroupAgents: + return time.Minute + case ServerGroupDBServers: + return time.Hour + default: + return time.Second * 30 + } +} + +// IsStateless returns true when the groups runs servers without a persistent volume. +func (g ServerGroup) IsStateless() bool { + switch g { + case ServerGroupCoordinators, ServerGroupSyncMasters, ServerGroupSyncWorkers: + return true + default: + return false + } +} + +// IsArangod returns true when the groups runs servers of type `arangod`. +func (g ServerGroup) IsArangod() bool { + switch g { + case ServerGroupSingle, ServerGroupAgents, ServerGroupDBServers, ServerGroupCoordinators: + return true + default: + return false + } +} + +// IsArangosync returns true when the groups runs servers of type `arangosync`. +func (g ServerGroup) IsArangosync() bool { + switch g { + case ServerGroupSyncMasters, ServerGroupSyncWorkers: + return true + default: + return false + } +} + +// IsExportMetrics return true when the group can be used with the arangodbexporter +func (g ServerGroup) IsExportMetrics() bool { + switch g { + case ServerGroupCoordinators, ServerGroupDBServers, ServerGroupSingle: + return true + default: + return false + } +} + +// ServerGroupFromAbbreviatedRole returns ServerGroup from abbreviated role +func ServerGroupFromAbbreviatedRole(label string) ServerGroup { + switch label { + case ServerGroupSingleAbbreviatedString: + return ServerGroupSingle + case ServerGroupAgentsAbbreviatedString: + return ServerGroupAgents + case ServerGroupDBServersAbbreviatedString: + return ServerGroupDBServers + case ServerGroupCoordinatorsAbbreviatedString: + return ServerGroupCoordinators + case ServerGroupSyncMastersAbbreviatedString: + return ServerGroupSyncMasters + case ServerGroupSyncWorkersAbbreviatedString: + return ServerGroupSyncWorkers + default: + return ServerGroupUnknown + } +} + +// ServerGroupFromAbbreviatedRole returns ServerGroup from role +func ServerGroupFromRole(label string) ServerGroup { + switch label { + case ServerGroupSingleString: + return ServerGroupSingle + case ServerGroupAgentsString: + return ServerGroupAgents + case ServerGroupDBServersString: + return ServerGroupDBServers + case ServerGroupCoordinatorsString: + return ServerGroupCoordinators + case ServerGroupSyncMastersString: + return ServerGroupSyncMasters + case ServerGroupSyncWorkersString: + return ServerGroupSyncWorkers + default: + return ServerGroupUnknown + } +} diff --git a/pkg/apis/deployment/v2alpha1/server_group_env_var.go b/pkg/apis/deployment/v2alpha1/server_group_env_var.go new file mode 100644 index 000000000..f2a91c32e --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/server_group_env_var.go @@ -0,0 +1,30 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Adam Janikowski +// + +package v2alpha1 + +type ServerGroupEnvVars []ServerGroupEnvVar + +type ServerGroupEnvVar struct { + Name string `json:"name" protobuf:"bytes,1,opt,name=name"` + Value string `json:"value,omitempty"` +} diff --git a/pkg/apis/deployment/v2alpha1/server_group_init_containers.go b/pkg/apis/deployment/v2alpha1/server_group_init_containers.go new file mode 100644 index 000000000..6eee4f8ce --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/server_group_init_containers.go @@ -0,0 +1,127 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Adam Janikowski +// + +package v2alpha1 + +import ( + "github.com/arangodb/kube-arangodb/pkg/apis/shared" + sharedv1 "github.com/arangodb/kube-arangodb/pkg/apis/shared/v1" + "github.com/pkg/errors" + core "k8s.io/api/core/v1" +) + +const ( + ServerGroupReservedInitContainerNameLifecycle = "init-lifecycle" + ServerGroupReservedInitContainerNameUUID = "uuid" +) + +func IsReservedServerGroupInitContainerName(name string) bool { + switch name { + case ServerGroupReservedInitContainerNameLifecycle, ServerGroupReservedInitContainerNameUUID: + return true + default: + return false + } +} + +func ValidateServerGroupInitContainerName(name string) error { + if IsReservedServerGroupInitContainerName(name) { + return errors.Errorf("InitContainer name %s is restricted", name) + } + + return sharedv1.AsKubernetesResourceName(&name).Validate() +} + +type ServerGroupInitContainerMode string + +func (s *ServerGroupInitContainerMode) Get() ServerGroupInitContainerMode { + if s == nil { + return ServerGroupInitContainerUpdateMode // default + } + + return *s +} + +func (s ServerGroupInitContainerMode) New() *ServerGroupInitContainerMode { + return &s +} + +func (s *ServerGroupInitContainerMode) Validate() error { + switch v := s.Get(); v { + case ServerGroupInitContainerIgnoreMode, ServerGroupInitContainerUpdateMode: + return nil + default: + return errors.Errorf("Unknown serverGroupInitContainerMode %s", v) + } +} + +const ( + // ServerGroupInitContainerIgnoreMode ignores init container changes in pod recreation flow + ServerGroupInitContainerIgnoreMode ServerGroupInitContainerMode = "ignore" + // ServerGroupInitContainerUpdateMode enforce update of pod if init container has been changed + ServerGroupInitContainerUpdateMode ServerGroupInitContainerMode = "update" +) + +type ServerGroupInitContainers struct { + // Containers contains list of containers + Containers []core.Container `json:"containers,omitempty"` + + // Mode keep container replace mode + Mode *ServerGroupInitContainerMode `json:"mode,omitempty"` +} + +func (s *ServerGroupInitContainers) GetMode() *ServerGroupInitContainerMode { + if s == nil { + return nil + } + + return s.Mode +} + +func (s *ServerGroupInitContainers) GetContainers() []core.Container { + if s == nil { + return nil + } + + return s.Containers +} + +func (s *ServerGroupInitContainers) Validate() error { + if s == nil { + return nil + } + + return shared.WithErrors( + shared.PrefixResourceError("mode", s.Mode.Validate()), + shared.PrefixResourceError("containers", s.validateInitContainers()), + ) +} + +func (s *ServerGroupInitContainers) validateInitContainers() error { + for _, c := range s.Containers { + if err := ValidateServerGroupInitContainerName(c.Name); err != nil { + return err + } + } + + return nil +} diff --git a/pkg/apis/deployment/v2alpha1/server_group_spec.go b/pkg/apis/deployment/v2alpha1/server_group_spec.go new file mode 100644 index 000000000..aaa27596e --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/server_group_spec.go @@ -0,0 +1,635 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "math" + "strings" + + "github.com/arangodb/kube-arangodb/pkg/apis/shared" + + "github.com/pkg/errors" + core "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + + "github.com/arangodb/kube-arangodb/pkg/util" + arangodOptions "github.com/arangodb/kube-arangodb/pkg/util/arangod/options" + arangosyncOptions "github.com/arangodb/kube-arangodb/pkg/util/arangosync/options" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" +) + +// ServerGroupSpec contains the specification for all servers in a specific group (e.g. all agents) +type ServerGroupSpec struct { + // Count holds the requested number of servers + Count *int `json:"count,omitempty"` + // MinCount specifies a lower limit for count + MinCount *int `json:"minCount,omitempty"` + // MaxCount specifies a upper limit for count + MaxCount *int `json:"maxCount,omitempty"` + // Args holds additional commandline arguments + Args []string `json:"args,omitempty"` + // Entrypoint overrides container executable + Entrypoint *string `json:"entrypoint,omitempty"` + // StorageClassName specifies the classname for storage of the servers. + StorageClassName *string `json:"storageClassName,omitempty"` + // Resources holds resource requests & limits + Resources core.ResourceRequirements `json:"resources,omitempty"` + // OverrideDetectedTotalMemory determines if memory should be overrided based on values in resources. + OverrideDetectedTotalMemory *bool `json:"overrideDetectedTotalMemory,omitempty"` + // OverrideDetectedNumberOfCores determines if number of cores should be overrided based on values in resources. + OverrideDetectedNumberOfCores *bool `json:"overrideDetectedNumberOfCores,omitempty"` + // Tolerations specifies the tolerations added to Pods in this group. + Tolerations []core.Toleration `json:"tolerations,omitempty"` + // Annotations specified the annotations added to Pods in this group. + Annotations map[string]string `json:"annotations,omitempty"` + // AnnotationsIgnoreList list regexp or plain definitions which annotations should be ignored + AnnotationsIgnoreList []string `json:"annotationsIgnoreList,omitempty"` + // AnnotationsMode Define annotations mode which should be use while overriding annotations + AnnotationsMode *LabelsMode `json:"annotationsMode,omitempty"` + // Labels specified the labels added to Pods in this group. + Labels map[string]string `json:"labels,omitempty"` + // LabelsIgnoreList list regexp or plain definitions which labels should be ignored + LabelsIgnoreList []string `json:"labelsIgnoreList,omitempty"` + // LabelsMode Define labels mode which should be use while overriding labels + LabelsMode *LabelsMode `json:"labelsMode,omitempty"` + // Envs allow to specify additional envs in this group. + Envs ServerGroupEnvVars `json:"envs,omitempty"` + // ServiceAccountName specifies the name of the service account used for Pods in this group. + ServiceAccountName *string `json:"serviceAccountName,omitempty"` + // NodeSelector speficies a set of selectors for nodes + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + // Probes specifies additional behaviour for probes + Probes *ServerGroupProbesSpec `json:"probes,omitempty"` + // PriorityClassName specifies a priority class name + PriorityClassName string `json:"priorityClassName,omitempty"` + // VolumeClaimTemplate specifies a template for volume claims + VolumeClaimTemplate *core.PersistentVolumeClaim `json:"volumeClaimTemplate,omitempty"` + // VolumeResizeMode specified resize mode for pvc + VolumeResizeMode *PVCResizeMode `json:"pvcResizeMode,omitempty"` + VolumeAllowShrink *bool `json:"volumeAllowShrink,omitempty"` + // AntiAffinity specified additional antiAffinity settings in ArangoDB Pod definitions + AntiAffinity *core.PodAntiAffinity `json:"antiAffinity,omitempty"` + // Affinity specified additional affinity settings in ArangoDB Pod definitions + Affinity *core.PodAffinity `json:"affinity,omitempty"` + // NodeAffinity specified additional nodeAffinity settings in ArangoDB Pod definitions + NodeAffinity *core.NodeAffinity `json:"nodeAffinity,omitempty"` + // Sidecars specifies a list of additional containers to be started + Sidecars []core.Container `json:"sidecars,omitempty"` + // SecurityContext specifies security context for group + SecurityContext *ServerGroupSpecSecurityContext `json:"securityContext,omitempty"` + // Volumes define list of volumes mounted to pod + Volumes ServerGroupSpecVolumes `json:"volumes,omitempty"` + // VolumeMounts define list of volume mounts mounted into server container + VolumeMounts ServerGroupSpecVolumeMounts `json:"volumeMounts,omitempty"` + // ExtendedRotationCheck extend checks for rotation + ExtendedRotationCheck *bool `json:"extendedRotationCheck,omitempty"` + // InitContainers Init containers specification + InitContainers *ServerGroupInitContainers `json:"initContainers,omitempty"` +} + +// ServerGroupSpecSecurityContext contains specification for pod security context +type ServerGroupSpecSecurityContext struct { + // DropAllCapabilities specifies if capabilities should be dropped for this pod containers + // + // Deprecated: This field is added for backward compatibility. Will be removed in 1.1.0. + DropAllCapabilities *bool `json:"dropAllCapabilities,omitempty"` + // AddCapabilities add new capabilities to containers + AddCapabilities []core.Capability `json:"addCapabilities,omitempty"` + + AllowPrivilegeEscalation *bool `json:"allowPrivilegeEscalation,omitempty"` + Privileged *bool `json:"privileged,omitempty"` + ReadOnlyRootFilesystem *bool `json:"readOnlyRootFilesystem,omitempty"` + RunAsNonRoot *bool `json:"runAsNonRoot,omitempty"` + RunAsUser *int64 `json:"runAsUser,omitempty"` + RunAsGroup *int64 `json:"runAsGroup,omitempty"` + + SupplementalGroups []int64 `json:"supplementalGroups,omitempty"` + FSGroup *int64 `json:"fsGroup,omitempty"` +} + +// GetDropAllCapabilities returns flag if capabilities should be dropped +// +// Deprecated: This function is added for backward compatibility. Will be removed in 1.1.0. +func (s *ServerGroupSpecSecurityContext) GetDropAllCapabilities() bool { + if s == nil { + return true + } + + if s.DropAllCapabilities == nil { + return true + } + + return *s.DropAllCapabilities +} + +// GetAddCapabilities add capabilities to pod context +func (s *ServerGroupSpecSecurityContext) GetAddCapabilities() []core.Capability { + if s == nil { + return nil + } + + if s.AddCapabilities == nil { + return nil + } + + return s.AddCapabilities +} + +// NewSecurityContext creates new pod security context +func (s *ServerGroupSpecSecurityContext) NewPodSecurityContext() *core.PodSecurityContext { + if s == nil { + return nil + } + + if s.FSGroup == nil && len(s.SupplementalGroups) == 0 { + return nil + } + + return &core.PodSecurityContext{ + SupplementalGroups: s.SupplementalGroups, + FSGroup: s.FSGroup, + } +} + +// NewSecurityContext creates new security context +func (s *ServerGroupSpecSecurityContext) NewSecurityContext() *core.SecurityContext { + r := &core.SecurityContext{} + + if s != nil { + r.AllowPrivilegeEscalation = s.AllowPrivilegeEscalation + r.Privileged = s.Privileged + r.ReadOnlyRootFilesystem = s.ReadOnlyRootFilesystem + r.RunAsNonRoot = s.RunAsNonRoot + r.RunAsUser = s.RunAsUser + r.RunAsGroup = s.RunAsGroup + } + + capabilities := &core.Capabilities{} + + if s.GetDropAllCapabilities() { + capabilities.Drop = []core.Capability{ + "ALL", + } + } + + if caps := s.GetAddCapabilities(); caps != nil { + capabilities.Add = []core.Capability{} + + capabilities.Add = append(capabilities.Add, caps...) + } + + r.Capabilities = capabilities + + return r +} + +// ServerGroupProbesSpec contains specification for probes for pods of the server group +type ServerGroupProbesSpec struct { + // LivenessProbeDisabled if true livenessProbes are disabled + LivenessProbeDisabled *bool `json:"livenessProbeDisabled,omitempty"` + // LivenessProbeSpec override liveness probe configuration + LivenessProbeSpec *ServerGroupProbeSpec `json:"livenessProbeSpec,omitempty"` + + // OldReadinessProbeDisabled if true readinessProbes are disabled + // + // Deprecated: This field is deprecated, keept only for backward compatibility. + OldReadinessProbeDisabled *bool `json:"ReadinessProbeDisabled,omitempty"` + // ReadinessProbeDisabled override flag for probe disabled in good manner (lowercase) with backward compatibility + ReadinessProbeDisabled *bool `json:"readinessProbeDisabled,omitempty"` + // ReadinessProbeSpec override readiness probe configuration + ReadinessProbeSpec *ServerGroupProbeSpec `json:"readinessProbeSpec,omitempty"` +} + +// GetReadinessProbeDisabled returns in proper manner readiness probe flag with backward compatibility. +func (s ServerGroupProbesSpec) GetReadinessProbeDisabled() *bool { + if s.OldReadinessProbeDisabled != nil { + return s.OldReadinessProbeDisabled + } + + return s.ReadinessProbeDisabled +} + +// ServerGroupProbeSpec +type ServerGroupProbeSpec struct { + InitialDelaySeconds *int32 `json:"initialDelaySeconds,omitempty"` + PeriodSeconds *int32 `json:"periodSeconds,omitempty"` + TimeoutSeconds *int32 `json:"timeoutSeconds,omitempty"` + SuccessThreshold *int32 `json:"successThreshold,omitempty"` + FailureThreshold *int32 `json:"failureThreshold,omitempty"` +} + +// GetInitialDelaySeconds return InitialDelaySeconds valid value. In case if InitialDelaySeconds is nil default is returned. +func (s *ServerGroupProbeSpec) GetInitialDelaySeconds(d int32) int32 { + if s == nil || s.InitialDelaySeconds == nil { + return d // Default Kubernetes value + } + + return *s.InitialDelaySeconds +} + +// GetPeriodSeconds return PeriodSeconds valid value. In case if PeriodSeconds is nil default is returned. +func (s *ServerGroupProbeSpec) GetPeriodSeconds(d int32) int32 { + if s == nil || s.PeriodSeconds == nil { + return d + } + + if *s.PeriodSeconds <= 0 { + return 1 // Value 0 is not allowed + } + + return *s.PeriodSeconds +} + +// GetTimeoutSeconds return TimeoutSeconds valid value. In case if TimeoutSeconds is nil default is returned. +func (s *ServerGroupProbeSpec) GetTimeoutSeconds(d int32) int32 { + if s == nil || s.TimeoutSeconds == nil { + return d + } + + if *s.TimeoutSeconds <= 0 { + return 1 // Value 0 is not allowed + } + + return *s.TimeoutSeconds +} + +// GetSuccessThreshold return SuccessThreshold valid value. In case if SuccessThreshold is nil default is returned. +func (s *ServerGroupProbeSpec) GetSuccessThreshold(d int32) int32 { + if s == nil || s.SuccessThreshold == nil { + return d + } + + if *s.SuccessThreshold <= 0 { + return 1 // Value 0 is not allowed + } + + return *s.SuccessThreshold +} + +// GetFailureThreshold return FailureThreshold valid value. In case if FailureThreshold is nil default is returned. +func (s *ServerGroupProbeSpec) GetFailureThreshold(d int32) int32 { + if s == nil || s.FailureThreshold == nil { + return d + } + + if *s.FailureThreshold <= 0 { + return 1 // Value 0 is not allowed + } + + return *s.FailureThreshold +} + +// GetSidecars returns a list of sidecars the use wish to add +func (s ServerGroupSpec) GetSidecars() []core.Container { + return s.Sidecars +} + +// HasVolumeClaimTemplate returns whether there is a volumeClaimTemplate or not +func (s ServerGroupSpec) HasVolumeClaimTemplate() bool { + return s.VolumeClaimTemplate != nil +} + +// GetVolumeClaimTemplate returns a pointer to a volume claim template or nil if none is specified +func (s ServerGroupSpec) GetVolumeClaimTemplate() *core.PersistentVolumeClaim { + return s.VolumeClaimTemplate +} + +// GetCount returns the value of count. +func (s ServerGroupSpec) GetCount() int { + return util.IntOrDefault(s.Count) +} + +// GetMinCount returns MinCount or 1 if not set +func (s ServerGroupSpec) GetMinCount() int { + return util.IntOrDefault(s.MinCount, 1) +} + +// GetMaxCount returns MaxCount or +func (s ServerGroupSpec) GetMaxCount() int { + return util.IntOrDefault(s.MaxCount, math.MaxInt32) +} + +// GetNodeSelector returns the selectors for nodes of this group +func (s ServerGroupSpec) GetNodeSelector() map[string]string { + return s.NodeSelector +} + +// GetAnnotations returns the annotations of this group +func (s ServerGroupSpec) GetAnnotations() map[string]string { + return s.Annotations +} + +// GetArgs returns the value of args. +func (s ServerGroupSpec) GetArgs() []string { + return s.Args +} + +// GetStorageClassName returns the value of storageClassName. +func (s ServerGroupSpec) GetStorageClassName() string { + if pvc := s.GetVolumeClaimTemplate(); pvc != nil { + return util.StringOrDefault(pvc.Spec.StorageClassName) + } + return util.StringOrDefault(s.StorageClassName) +} + +// GetTolerations returns the value of tolerations. +func (s ServerGroupSpec) GetTolerations() []core.Toleration { + return s.Tolerations +} + +// GetServiceAccountName returns the value of serviceAccountName. +func (s ServerGroupSpec) GetServiceAccountName() string { + return util.StringOrDefault(s.ServiceAccountName) +} + +// HasProbesSpec returns true if Probes is non nil +func (s ServerGroupSpec) HasProbesSpec() bool { + return s.Probes != nil +} + +// GetProbesSpec returns the Probes spec or the nil value if not set +func (s ServerGroupSpec) GetProbesSpec() ServerGroupProbesSpec { + if s.HasProbesSpec() { + return *s.Probes + } + return ServerGroupProbesSpec{} +} + +// GetOverrideDetectedTotalMemory returns OverrideDetectedTotalMemory with default value (false) +func (s ServerGroupSpec) GetOverrideDetectedTotalMemory() bool { + if s.OverrideDetectedTotalMemory == nil { + return true + } + + return *s.OverrideDetectedTotalMemory +} + +// OverrideDetectedNumberOfCores returns OverrideDetectedNumberOfCores with default value (false) +func (s ServerGroupSpec) GetOverrideDetectedNumberOfCores() bool { + if s.OverrideDetectedNumberOfCores == nil { + return true + } + + return *s.OverrideDetectedNumberOfCores +} + +// Validate the given group spec +func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentMode, env Environment) error { + if used { + minCount := 1 + if env == EnvironmentProduction { + // Set validation boundaries for production mode + switch group { + case ServerGroupSingle: + if mode == DeploymentModeActiveFailover { + minCount = 2 + } + case ServerGroupAgents: + minCount = 3 + case ServerGroupDBServers, ServerGroupCoordinators, ServerGroupSyncMasters, ServerGroupSyncWorkers: + minCount = 2 + } + } else { + // Set validation boundaries for development mode + switch group { + case ServerGroupSingle: + if mode == DeploymentModeActiveFailover { + minCount = 2 + } + case ServerGroupDBServers: + minCount = 2 + } + } + if s.GetMinCount() > s.GetMaxCount() { + return maskAny(errors.Wrapf(ValidationError, "Invalid min/maxCount. Min (%d) bigger than Max (%d)", s.GetMinCount(), s.GetMaxCount())) + } + if s.GetCount() < s.GetMinCount() { + return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected >= %d", s.GetCount(), s.GetMinCount())) + } + if s.GetCount() > s.GetMaxCount() { + return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected <= %d", s.GetCount(), s.GetMaxCount())) + } + if s.GetCount() < minCount { + return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected >= %d (implicit minimum; by deployment mode)", s.GetCount(), minCount)) + } + if s.GetCount() > 1 && group == ServerGroupSingle && mode == DeploymentModeSingle { + return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected 1", s.GetCount())) + } + if name := s.GetServiceAccountName(); name != "" { + if err := k8sutil.ValidateOptionalResourceName(name); err != nil { + return maskAny(errors.Wrapf(ValidationError, "Invalid serviceAccountName: %s", err)) + } + } + if name := s.GetStorageClassName(); name != "" { + if err := k8sutil.ValidateOptionalResourceName(name); err != nil { + return maskAny(errors.Wrapf(ValidationError, "Invalid storageClassName: %s", err)) + } + } + for _, arg := range s.Args { + parts := strings.Split(arg, "=") + optionKey := strings.TrimSpace(parts[0]) + if group.IsArangod() { + if arangodOptions.IsCriticalOption(optionKey) { + return maskAny(errors.Wrapf(ValidationError, "Critical option '%s' cannot be overriden", optionKey)) + } + } else if group.IsArangosync() { + if arangosyncOptions.IsCriticalOption(optionKey) { + return maskAny(errors.Wrapf(ValidationError, "Critical option '%s' cannot be overriden", optionKey)) + } + } + } + + if err := s.validate(); err != nil { + return maskAny(err) + } + } else if s.GetCount() != 0 { + return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d for un-used group. Expected 0", s.GetCount())) + } + return nil +} + +func (s *ServerGroupSpec) validate() error { + if s == nil { + return nil + } + + return shared.WithErrors( + shared.PrefixResourceError("volumes", s.Volumes.Validate()), + shared.PrefixResourceError("volumeMounts", s.VolumeMounts.Validate()), + shared.PrefixResourceError("initContainers", s.InitContainers.Validate()), + s.validateVolumes(), + ) +} + +func (s *ServerGroupSpec) validateVolumes() error { + volumes := map[string]bool{} + + for _, volume := range s.Volumes { + volumes[volume.Name] = true + } + + volumes["arangod-data"] = true + + for _, mount := range s.VolumeMounts { + if _, ok := volumes[mount.Name]; !ok { + return errors.Errorf("Volume %s is not defined, but required by mount", mount.Name) + } + } + + for _, container := range s.InitContainers.GetContainers() { + for _, mount := range container.VolumeMounts { + if _, ok := volumes[mount.Name]; !ok { + return errors.Errorf("Volume %s is not defined, but required by mount in init container %s", mount.Name, container.Name) + } + } + } + + for _, container := range s.Sidecars { + for _, mount := range s.VolumeMounts { + if _, ok := volumes[mount.Name]; !ok { + return errors.Errorf("Volume %s is not defined, but required by mount in sidecar %s", mount.Name, container.Name) + } + } + } + + return nil +} + +// SetDefaults fills in missing defaults +func (s *ServerGroupSpec) SetDefaults(group ServerGroup, used bool, mode DeploymentMode) { + if s.GetCount() == 0 && used { + switch group { + case ServerGroupSingle: + if mode == DeploymentModeSingle { + s.Count = util.NewInt(1) // Single server + } else { + s.Count = util.NewInt(2) // ActiveFailover + } + default: + s.Count = util.NewInt(3) + } + } else if s.GetCount() > 0 && !used { + s.Count = nil + s.MinCount = nil + s.MaxCount = nil + } + if !s.HasVolumeClaimTemplate() { + if _, found := s.Resources.Requests[core.ResourceStorage]; !found { + switch group { + case ServerGroupSingle, ServerGroupAgents, ServerGroupDBServers: + volumeMode := core.PersistentVolumeFilesystem + s.VolumeClaimTemplate = &core.PersistentVolumeClaim{ + Spec: core.PersistentVolumeClaimSpec{ + AccessModes: []core.PersistentVolumeAccessMode{ + core.ReadWriteOnce, + }, + VolumeMode: &volumeMode, + Resources: core.ResourceRequirements{ + Requests: core.ResourceList{ + core.ResourceStorage: resource.MustParse("8Gi"), + }, + }, + }, + } + } + } + } +} + +// setDefaultsFromResourceList fills unspecified fields with a value from given source spec. +func setDefaultsFromResourceList(s *core.ResourceList, source core.ResourceList) { + for k, v := range source { + if *s == nil { + *s = make(core.ResourceList) + } + if _, found := (*s)[k]; !found { + (*s)[k] = v + } + } +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *ServerGroupSpec) SetDefaultsFrom(source ServerGroupSpec) { + if s.Count == nil { + s.Count = util.NewIntOrNil(source.Count) + } + if s.MinCount == nil { + s.MinCount = util.NewIntOrNil(source.MinCount) + } + if s.MaxCount == nil { + s.MaxCount = util.NewIntOrNil(source.MaxCount) + } + if s.Args == nil { + s.Args = source.Args + } + if s.StorageClassName == nil { + s.StorageClassName = util.NewStringOrNil(source.StorageClassName) + } + if s.Tolerations == nil { + s.Tolerations = source.Tolerations + } + if s.ServiceAccountName == nil { + s.ServiceAccountName = util.NewStringOrNil(source.ServiceAccountName) + } + if s.NodeSelector == nil { + s.NodeSelector = source.NodeSelector + } + setDefaultsFromResourceList(&s.Resources.Limits, source.Resources.Limits) + setDefaultsFromResourceList(&s.Resources.Requests, source.Resources.Requests) + if s.VolumeClaimTemplate == nil { + s.VolumeClaimTemplate = source.VolumeClaimTemplate.DeepCopy() + } +} + +// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. +// It returns a list of fields that have been reset. +func (s ServerGroupSpec) ResetImmutableFields(group ServerGroup, fieldPrefix string, target *ServerGroupSpec) []string { + var resetFields []string + if group == ServerGroupAgents { + if s.GetCount() != target.GetCount() { + target.Count = util.NewIntOrNil(s.Count) + resetFields = append(resetFields, fieldPrefix+".count") + } + } + if s.HasVolumeClaimTemplate() != target.HasVolumeClaimTemplate() { + target.VolumeClaimTemplate = s.GetVolumeClaimTemplate() + resetFields = append(resetFields, fieldPrefix+".volumeClaimTemplate") + } + return resetFields +} + +func (s ServerGroupSpec) GetVolumeAllowShrink() bool { + if s.VolumeAllowShrink == nil { + return false // Default value + } + + return *s.VolumeAllowShrink +} + +func (s *ServerGroupSpec) GetEntrypoint(defaultEntrypoint string) string { + if s == nil || s.Entrypoint == nil { + return defaultEntrypoint + } + + return *s.Entrypoint +} diff --git a/pkg/apis/deployment/v2alpha1/server_group_spec_test.go b/pkg/apis/deployment/v2alpha1/server_group_spec_test.go new file mode 100644 index 000000000..7fe77931f --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/server_group_spec_test.go @@ -0,0 +1,133 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/stretchr/testify/assert" +) + +func TestServerGroupSpecValidateCount(t *testing.T) { + // Valid + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupSingle, true, DeploymentModeSingle, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(0)}.Validate(ServerGroupSingle, false, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(3)}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupAgents, true, DeploymentModeActiveFailover, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(3)}.Validate(ServerGroupAgents, true, DeploymentModeActiveFailover, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(6)}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(3)}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(3)}.Validate(ServerGroupAgents, true, DeploymentModeActiveFailover, EnvironmentProduction)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentProduction)) + + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2), MinCount: util.NewInt(2), MaxCount: util.NewInt(5)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(1), MaxCount: util.NewInt(5)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(6), MinCount: util.NewInt(2)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(5), MinCount: util.NewInt(5), MaxCount: util.NewInt(5)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + + // Invalid + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupSingle, false, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupSingle, true, DeploymentModeSingle, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupSingle, true, DeploymentModeActiveFailover, EnvironmentProduction)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(0)}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(0)}.Validate(ServerGroupAgents, true, DeploymentModeActiveFailover, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(0)}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(0)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(0)}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(0)}.Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(-1)}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(-1)}.Validate(ServerGroupAgents, true, DeploymentModeActiveFailover, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(-1)}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(-1)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(-1)}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(-1)}.Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupAgents, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(2)}.Validate(ServerGroupAgents, true, DeploymentModeActiveFailover, EnvironmentProduction)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupDBServers, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentProduction)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1)}.Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentProduction)) + + assert.Error(t, ServerGroupSpec{Count: util.NewInt(2), MinCount: util.NewInt(5), MaxCount: util.NewInt(1)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(6), MaxCount: util.NewInt(5)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1), MinCount: util.NewInt(2)}.Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) + +} + +func TestServerGroupSpecDefault(t *testing.T) { + def := func(spec ServerGroupSpec, group ServerGroup, used bool, mode DeploymentMode) ServerGroupSpec { + spec.SetDefaults(group, used, mode) + return spec + } + + assert.Equal(t, 1, def(ServerGroupSpec{}, ServerGroupSingle, true, DeploymentModeSingle).GetCount()) + assert.Equal(t, 2, def(ServerGroupSpec{}, ServerGroupSingle, true, DeploymentModeActiveFailover).GetCount()) + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSingle, false, DeploymentModeCluster).GetCount()) + + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupAgents, false, DeploymentModeSingle).GetCount()) + assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupAgents, true, DeploymentModeActiveFailover).GetCount()) + assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupAgents, true, DeploymentModeCluster).GetCount()) + + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupDBServers, false, DeploymentModeSingle).GetCount()) + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupDBServers, false, DeploymentModeActiveFailover).GetCount()) + assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupDBServers, true, DeploymentModeCluster).GetCount()) + + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupCoordinators, false, DeploymentModeSingle).GetCount()) + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupCoordinators, false, DeploymentModeActiveFailover).GetCount()) + assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupCoordinators, true, DeploymentModeCluster).GetCount()) + + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncMasters, false, DeploymentModeSingle).GetCount()) + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncMasters, false, DeploymentModeActiveFailover).GetCount()) + assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupSyncMasters, true, DeploymentModeCluster).GetCount()) + + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncWorkers, false, DeploymentModeSingle).GetCount()) + assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncWorkers, false, DeploymentModeActiveFailover).GetCount()) + assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupSyncWorkers, true, DeploymentModeCluster).GetCount()) + + for _, g := range AllServerGroups { + assert.Equal(t, 0, len(def(ServerGroupSpec{}, g, true, DeploymentModeSingle).Args)) + assert.Equal(t, "", def(ServerGroupSpec{}, g, true, DeploymentModeSingle).GetStorageClassName()) + } +} + +func TestServerGroupSpecValidateArgs(t *testing.T) { + // Valid + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(1), Args: []string{}}.Validate(ServerGroupSingle, true, DeploymentModeSingle, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(1), Args: []string{"--master.endpoint"}}.Validate(ServerGroupSingle, true, DeploymentModeSingle, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(1), Args: []string{}}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Nil(t, ServerGroupSpec{Count: util.NewInt(1), Args: []string{"--server.authentication=true"}}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentDevelopment)) + // Invalid + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1), Args: []string{"--server.authentication=true"}}.Validate(ServerGroupSingle, true, DeploymentModeSingle, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1), Args: []string{"--server.authentication", "true"}}.Validate(ServerGroupSingle, true, DeploymentModeSingle, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1), Args: []string{"--master.endpoint=http://something"}}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentDevelopment)) + assert.Error(t, ServerGroupSpec{Count: util.NewInt(1), Args: []string{"--mq.type=strange"}}.Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentDevelopment)) +} diff --git a/pkg/apis/deployment/v2alpha1/server_group_test.go b/pkg/apis/deployment/v2alpha1/server_group_test.go new file mode 100644 index 000000000..14c981704 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/server_group_test.go @@ -0,0 +1,65 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestServerGroupAsRole(t *testing.T) { + assert.Equal(t, "single", ServerGroupSingle.AsRole()) + assert.Equal(t, "agent", ServerGroupAgents.AsRole()) + assert.Equal(t, "dbserver", ServerGroupDBServers.AsRole()) + assert.Equal(t, "coordinator", ServerGroupCoordinators.AsRole()) + assert.Equal(t, "syncmaster", ServerGroupSyncMasters.AsRole()) + assert.Equal(t, "syncworker", ServerGroupSyncWorkers.AsRole()) +} + +func TestServerGroupAsRoleAbbreviated(t *testing.T) { + assert.Equal(t, "sngl", ServerGroupSingle.AsRoleAbbreviated()) + assert.Equal(t, "agnt", ServerGroupAgents.AsRoleAbbreviated()) + assert.Equal(t, "prmr", ServerGroupDBServers.AsRoleAbbreviated()) + assert.Equal(t, "crdn", ServerGroupCoordinators.AsRoleAbbreviated()) + assert.Equal(t, "syma", ServerGroupSyncMasters.AsRoleAbbreviated()) + assert.Equal(t, "sywo", ServerGroupSyncWorkers.AsRoleAbbreviated()) +} + +func TestServerGroupIsArangod(t *testing.T) { + assert.True(t, ServerGroupSingle.IsArangod()) + assert.True(t, ServerGroupAgents.IsArangod()) + assert.True(t, ServerGroupDBServers.IsArangod()) + assert.True(t, ServerGroupCoordinators.IsArangod()) + assert.False(t, ServerGroupSyncMasters.IsArangod()) + assert.False(t, ServerGroupSyncWorkers.IsArangod()) +} + +func TestServerGroupIsArangosync(t *testing.T) { + assert.False(t, ServerGroupSingle.IsArangosync()) + assert.False(t, ServerGroupAgents.IsArangosync()) + assert.False(t, ServerGroupDBServers.IsArangosync()) + assert.False(t, ServerGroupCoordinators.IsArangosync()) + assert.True(t, ServerGroupSyncMasters.IsArangosync()) + assert.True(t, ServerGroupSyncWorkers.IsArangosync()) +} diff --git a/pkg/apis/deployment/v2alpha1/server_group_volume.go b/pkg/apis/deployment/v2alpha1/server_group_volume.go new file mode 100644 index 000000000..e60779cee --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/server_group_volume.go @@ -0,0 +1,207 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Adam Janikowski +// + +package v2alpha1 + +import ( + "fmt" + + "github.com/arangodb/kube-arangodb/pkg/apis/shared" + sharedv1 "github.com/arangodb/kube-arangodb/pkg/apis/shared/v1" + + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" + "github.com/pkg/errors" + core "k8s.io/api/core/v1" +) + +var ( + restrictedVolumeNames = []string{ + k8sutil.ArangodVolumeName, + k8sutil.TlsKeyfileVolumeName, + k8sutil.RocksdbEncryptionVolumeName, + k8sutil.ExporterJWTVolumeName, + k8sutil.ClusterJWTSecretVolumeName, + "lifecycle", + } +) + +// IsRestrictedVolumeName check of volume name is restricted, for example for originally mounted volumes +func IsRestrictedVolumeName(name string) bool { + for _, restrictedVolumeName := range restrictedVolumeNames { + if restrictedVolumeName == name { + return true + } + } + + return false +} + +// ServerGroupSpecVolumes definition of volume list which need to be mounted to Pod +type ServerGroupSpecVolumes []ServerGroupSpecVolume + +// Validate if ServerGroupSpec volumes are valid and does not collide +func (s ServerGroupSpecVolumes) Validate() error { + var validationErrors []error + + mappedVolumes := map[string]int{} + + for id, volume := range s { + if i, ok := mappedVolumes[volume.Name]; ok { + mappedVolumes[volume.Name] = i + 1 + } else { + mappedVolumes[volume.Name] = 1 + } + + if err := volume.Validate(); err != nil { + validationErrors = append(validationErrors, shared.PrefixResourceErrors(fmt.Sprintf("%d", id), err)) + } + } + + for volumeName, count := range mappedVolumes { + if IsRestrictedVolumeName(volumeName) { + validationErrors = append(validationErrors, errors.Errorf("volume with name %s is restricted", volumeName)) + } + + if count == 1 { + continue + } + + validationErrors = append(validationErrors, errors.Errorf("volume with name %s defined more than once: %d", volumeName, count)) + } + + return shared.WithErrors(validationErrors...) +} + +// Volumes create volumes +func (s ServerGroupSpecVolumes) Volumes() []core.Volume { + volumes := make([]core.Volume, len(s)) + + for id, volume := range s { + volumes[id] = volume.Volume() + } + + return volumes +} + +// ServerGroupSpecVolume definition of volume which need to be mounted to Pod +type ServerGroupSpecVolume struct { + // Name of volume + Name string `json:"name"` + + // Secret which should be mounted into pod + Secret *ServerGroupSpecVolumeSecret `json:"secret,omitempty"` + + // ConfigMap which should be mounted into pod + ConfigMap *ServerGroupSpecVolumeConfigMap `json:"configMap,omitempty"` + + // EmptyDir + EmptyDir *ServerGroupSpecVolumeEmptyDir `json:"emptyDir,omitempty"` +} + +// Validate if ServerGroupSpec volume is valid +func (s *ServerGroupSpecVolume) Validate() error { + if s == nil { + return nil + } + + return shared.WithErrors( + shared.PrefixResourceErrors("name", sharedv1.AsKubernetesResourceName(&s.Name).Validate()), + shared.PrefixResourceErrors("secret", s.Secret.Validate()), + shared.PrefixResourceErrors("configMap", s.ConfigMap.Validate()), + shared.PrefixResourceErrors("emptyDir", s.EmptyDir.Validate()), + s.validate(), + ) +} + +// Volume create Pod Volume object +func (s ServerGroupSpecVolume) Volume() core.Volume { + return core.Volume{ + Name: s.Name, + VolumeSource: core.VolumeSource{ + ConfigMap: (*core.ConfigMapVolumeSource)(s.ConfigMap), + Secret: (*core.SecretVolumeSource)(s.Secret), + EmptyDir: (*core.EmptyDirVolumeSource)(s.EmptyDir), + }, + } +} + +func (s *ServerGroupSpecVolume) validate() error { + count := s.notNilFields() + + if count == 0 { + return errors.Errorf("at least one option need to be defined: secret, configMap or emptyDir") + } + + if count > 1 { + return errors.Errorf("only one option can be defined: secret, configMap or emptyDir") + } + + return nil +} + +func (s *ServerGroupSpecVolume) notNilFields() int { + i := 0 + + if s.ConfigMap != nil { + i++ + } + + if s.Secret != nil { + i++ + } + + if s.EmptyDir != nil { + i++ + } + + return i +} + +type ServerGroupSpecVolumeSecret core.SecretVolumeSource + +func (s *ServerGroupSpecVolumeSecret) Validate() error { + if s == nil { + return nil + } + + return shared.WithErrors( + shared.PrefixResourceError("secretName", sharedv1.AsKubernetesResourceName(&s.SecretName).Validate()), + ) +} + +type ServerGroupSpecVolumeConfigMap core.ConfigMapVolumeSource + +func (s *ServerGroupSpecVolumeConfigMap) Validate() error { + if s == nil { + return nil + } + + return shared.WithErrors( + shared.PrefixResourceError("name", sharedv1.AsKubernetesResourceName(&s.Name).Validate()), + ) +} + +type ServerGroupSpecVolumeEmptyDir core.EmptyDirVolumeSource + +func (s *ServerGroupSpecVolumeEmptyDir) Validate() error { + return nil +} diff --git a/pkg/apis/deployment/v2alpha1/server_group_volume_mount.go b/pkg/apis/deployment/v2alpha1/server_group_volume_mount.go new file mode 100644 index 000000000..54cd192f5 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/server_group_volume_mount.go @@ -0,0 +1,74 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Adam Janikowski +// + +package v2alpha1 + +import ( + "fmt" + + "github.com/arangodb/kube-arangodb/pkg/apis/shared" + sharedv1 "github.com/arangodb/kube-arangodb/pkg/apis/shared/v1" + + core "k8s.io/api/core/v1" +) + +type ServerGroupSpecVolumeMounts []ServerGroupSpecVolumeMount + +func (s ServerGroupSpecVolumeMounts) VolumeMounts() []core.VolumeMount { + mounts := make([]core.VolumeMount, len(s)) + + for id, mount := range s { + mounts[id] = mount.VolumeMount() + } + + return mounts +} + +func (s ServerGroupSpecVolumeMounts) Validate() error { + if s == nil { + return nil + } + + validateErrors := make([]error, len(s)) + + for id, mount := range s { + validateErrors[id] = shared.PrefixResourceErrors(fmt.Sprintf("%d", id), mount.Validate()) + } + + return shared.WithErrors(validateErrors...) +} + +type ServerGroupSpecVolumeMount core.VolumeMount + +func (s ServerGroupSpecVolumeMount) VolumeMount() core.VolumeMount { + return core.VolumeMount(s) +} + +func (s *ServerGroupSpecVolumeMount) Validate() error { + if s == nil { + return nil + } + + return shared.WithErrors( + shared.PrefixResourceError("name", sharedv1.AsKubernetesResourceName(&s.Name).Validate()), + ) +} diff --git a/pkg/apis/deployment/v2alpha1/server_group_volume_test.go b/pkg/apis/deployment/v2alpha1/server_group_volume_test.go new file mode 100644 index 000000000..4a8f29003 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/server_group_volume_test.go @@ -0,0 +1,184 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Adam Janikowski +// + +package v2alpha1 + +import ( + "fmt" + "testing" + + "github.com/arangodb/kube-arangodb/pkg/apis/shared" + + "github.com/stretchr/testify/require" + core "k8s.io/api/core/v1" +) + +const ( + labelValidationError = "Validation of label failed: a DNS-1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')" + invalidName = "-invalid" + validName = "valid" +) + +func Test_Volume_Validation(t *testing.T) { + cases := []struct { + name string + volumes ServerGroupSpecVolumes + fail bool + failedFields map[string]string + }{ + { + name: "Nil definition", + }, + { + name: "Invalid name", + + fail: true, + failedFields: map[string]string{ + "0.name": labelValidationError, + "0.secret.secretName": labelValidationError, + }, + + volumes: []ServerGroupSpecVolume{ + { + Name: invalidName, + Secret: &ServerGroupSpecVolumeSecret{ + SecretName: invalidName, + }, + }, + }, + }, + { + name: "Restricted name", + + fail: true, + failedFields: map[string]string{ + "": fmt.Sprintf("volume with name %s is restricted", restrictedVolumeNames[0]), + }, + + volumes: []ServerGroupSpecVolume{ + { + Name: restrictedVolumeNames[0], + Secret: &ServerGroupSpecVolumeSecret{ + SecretName: validName, + }, + }, + }, + }, + { + name: "Defined multiple sources", + + fail: true, + failedFields: map[string]string{ + "0": "only one option can be defined: secret, configMap or emptyDir", + }, + + volumes: []ServerGroupSpecVolume{ + { + Name: validName, + Secret: &ServerGroupSpecVolumeSecret{ + SecretName: validName, + }, + ConfigMap: &ServerGroupSpecVolumeConfigMap{ + LocalObjectReference: core.LocalObjectReference{ + Name: validName, + }, + }, + }, + }, + }, + { + name: "Defined multiple volumes with same name", + + fail: true, + failedFields: map[string]string{ + "": "volume with name valid defined more than once: 2", + }, + + volumes: []ServerGroupSpecVolume{ + { + Name: validName, + Secret: &ServerGroupSpecVolumeSecret{ + SecretName: validName, + }, + }, + { + Name: validName, + Secret: &ServerGroupSpecVolumeSecret{ + SecretName: validName, + }, + }, + }, + }, + { + name: "Defined multiple volumes", + + volumes: []ServerGroupSpecVolume{ + { + Name: validName, + Secret: &ServerGroupSpecVolumeSecret{ + SecretName: validName, + }, + }, + { + Name: "valid-2", + ConfigMap: &ServerGroupSpecVolumeConfigMap{ + LocalObjectReference: core.LocalObjectReference{ + Name: validName, + }, + }, + }, + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + err := c.volumes.Validate() + + if c.fail { + require.Error(t, err) + + mergedErr, ok := err.(shared.MergedErrors) + require.True(t, ok, "Is not MergedError type") + + require.Equal(t, len(mergedErr.Errors()), len(c.failedFields), "Count of expected fields and merged errors does not match") + + for _, fieldError := range mergedErr.Errors() { + resourceErr, ok := fieldError.(shared.ResourceError) + if !ok { + resourceErr = shared.ResourceError{ + Prefix: "", + Err: fieldError, + } + } + + errValue, ok := c.failedFields[resourceErr.Prefix] + require.True(t, ok, "unexpected prefix %s", resourceErr.Prefix) + + require.EqualError(t, resourceErr.Err, errValue) + } + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/pkg/apis/deployment/v2alpha1/server_id_group_spec.go b/pkg/apis/deployment/v2alpha1/server_id_group_spec.go new file mode 100644 index 000000000..48f0a6e65 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/server_id_group_spec.go @@ -0,0 +1,84 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Adam Janikowski +// + +package v2alpha1 + +import core "k8s.io/api/core/v1" + +// ServerIDGroupSpec contains the specification for Image Discovery image. +type ServerIDGroupSpec struct { + // Entrypoint overrides container executable + Entrypoint *string `json:"entrypoint,omitempty"` + // Tolerations specifies the tolerations added to Pods in this group. + Tolerations []core.Toleration `json:"tolerations,omitempty"` + // NodeSelector speficies a set of selectors for nodes + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + // PriorityClassName specifies a priority class name + PriorityClassName string `json:"priorityClassName,omitempty"` + // AntiAffinity specified additional antiAffinity settings in ArangoDB Pod definitions + AntiAffinity *core.PodAntiAffinity `json:"antiAffinity,omitempty"` + // Affinity specified additional affinity settings in ArangoDB Pod definitions + Affinity *core.PodAffinity `json:"affinity,omitempty"` + // NodeAffinity specified additional nodeAffinity settings in ArangoDB Pod definitions + NodeAffinity *core.NodeAffinity `json:"nodeAffinity,omitempty"` + // ServiceAccountName specifies the name of the service account used for Pods in this group. + ServiceAccountName *string `json:"serviceAccountName,omitempty"` + // SecurityContext specifies security context for group + SecurityContext *ServerGroupSpecSecurityContext `json:"securityContext,omitempty"` + // Resources holds resource requests & limits + Resources *core.ResourceRequirements `json:"resources,omitempty"` +} + +func (s *ServerIDGroupSpec) Get() ServerIDGroupSpec { + if s != nil { + return *s + } + + return ServerIDGroupSpec{} +} + +func (s *ServerIDGroupSpec) GetServiceAccountName() string { + if s == nil || s.ServiceAccountName == nil { + return "" + } + + return *s.ServiceAccountName +} + +func (s *ServerIDGroupSpec) GetResources() core.ResourceRequirements { + if s == nil || s.Resources == nil { + return core.ResourceRequirements{ + Limits: make(core.ResourceList), + Requests: make(core.ResourceList), + } + } + + return *s.Resources +} + +func (s *ServerIDGroupSpec) GetEntrypoint(defaultEntrypoint string) string { + if s == nil || s.Entrypoint == nil { + return defaultEntrypoint + } + + return *s.Entrypoint +} diff --git a/pkg/apis/deployment/v2alpha1/storage_engine.go b/pkg/apis/deployment/v2alpha1/storage_engine.go new file mode 100644 index 000000000..e0ec3c545 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/storage_engine.go @@ -0,0 +1,80 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "strings" + + "github.com/pkg/errors" +) + +// StorageEngine specifies the type of storage engine used by the cluster +type StorageEngine string + +const ( + // StorageEngineMMFiles yields a cluster using the mmfiles storage engine + StorageEngineMMFiles StorageEngine = "MMFiles" + // StorageEngineRocksDB yields a cluster using the rocksdb storage engine + StorageEngineRocksDB StorageEngine = "RocksDB" +) + +// Validate the storage engine. +// Return errors when validation fails, nil on success. +func (se StorageEngine) Validate() error { + switch se { + case StorageEngineMMFiles, StorageEngineRocksDB: + return nil + default: + return maskAny(errors.Wrapf(ValidationError, "Unknown storage engine: '%s'", string(se))) + } +} + +// AsArangoArgument returns the value for the given storage engine as it is to be used +// for arangod's --server.storage-engine option. +func (se StorageEngine) AsArangoArgument() string { + return strings.ToLower(string(se)) +} + +// NewStorageEngine returns a reference to a string with given value. +func NewStorageEngine(input StorageEngine) *StorageEngine { + return &input +} + +// NewStorageEngineOrNil returns nil if input is nil, otherwise returns a clone of the given value. +func NewStorageEngineOrNil(input *StorageEngine) *StorageEngine { + if input == nil { + return nil + } + return NewStorageEngine(*input) +} + +// StorageEngineOrDefault returns the default value (or empty string) if input is nil, otherwise returns the referenced value. +func StorageEngineOrDefault(input *StorageEngine, defaultValue ...StorageEngine) StorageEngine { + if input == nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return "" + } + return *input +} diff --git a/pkg/apis/deployment/v2alpha1/storage_engine_test.go b/pkg/apis/deployment/v2alpha1/storage_engine_test.go new file mode 100644 index 000000000..676957ab1 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/storage_engine_test.go @@ -0,0 +1,41 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStorageEngineValidate(t *testing.T) { + // Valid + assert.Nil(t, StorageEngine("MMFiles").Validate()) + assert.Nil(t, StorageEngine("RocksDB").Validate()) + + // Not valid + assert.Error(t, StorageEngine("").Validate()) + assert.Error(t, StorageEngine(" mmfiles").Validate()) + assert.Error(t, StorageEngine("mmfiles").Validate()) + assert.Error(t, StorageEngine("rocksdb").Validate()) +} diff --git a/pkg/apis/deployment/v2alpha1/sync_authentication_spec.go b/pkg/apis/deployment/v2alpha1/sync_authentication_spec.go new file mode 100644 index 000000000..9fce31f4b --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/sync_authentication_spec.go @@ -0,0 +1,87 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" +) + +// SyncAuthenticationSpec holds dc2dc sync authentication specific configuration settings +type SyncAuthenticationSpec struct { + JWTSecretName *string `json:"jwtSecretName,omitempty"` // JWT secret for sync masters + ClientCASecretName *string `json:"clientCASecretName,omitempty"` // Secret containing client authentication CA +} + +// GetJWTSecretName returns the value of jwtSecretName. +func (s SyncAuthenticationSpec) GetJWTSecretName() string { + return util.StringOrDefault(s.JWTSecretName) +} + +// GetClientCASecretName returns the value of clientCASecretName. +func (s SyncAuthenticationSpec) GetClientCASecretName() string { + return util.StringOrDefault(s.ClientCASecretName) +} + +// Validate the given spec +func (s SyncAuthenticationSpec) Validate() error { + if err := k8sutil.ValidateResourceName(s.GetJWTSecretName()); err != nil { + return maskAny(err) + } + if err := k8sutil.ValidateResourceName(s.GetClientCASecretName()); err != nil { + return maskAny(err) + } + return nil +} + +// SetDefaults fills in missing defaults +func (s *SyncAuthenticationSpec) SetDefaults(defaultJWTSecretName, defaultClientCASecretName string) { + if s.GetJWTSecretName() == "" { + // Note that we don't check for nil here, since even a specified, but empty + // string should result in the default value. + s.JWTSecretName = util.NewString(defaultJWTSecretName) + } + if s.GetClientCASecretName() == "" { + // Note that we don't check for nil here, since even a specified, but empty + // string should result in the default value. + s.ClientCASecretName = util.NewString(defaultClientCASecretName) + } +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *SyncAuthenticationSpec) SetDefaultsFrom(source SyncAuthenticationSpec) { + if s.JWTSecretName == nil { + s.JWTSecretName = util.NewStringOrNil(source.JWTSecretName) + } + if s.ClientCASecretName == nil { + s.ClientCASecretName = util.NewStringOrNil(source.ClientCASecretName) + } +} + +// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. +// It returns a list of fields that have been reset. +// Field names are relative to given field prefix. +func (s SyncAuthenticationSpec) ResetImmutableFields(fieldPrefix string, target *SyncAuthenticationSpec) []string { + var resetFields []string + return resetFields +} diff --git a/pkg/apis/deployment/v2alpha1/sync_external_access_spec.go b/pkg/apis/deployment/v2alpha1/sync_external_access_spec.go new file mode 100644 index 000000000..b41e84832 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/sync_external_access_spec.go @@ -0,0 +1,102 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "fmt" + "net" + "net/url" + "strconv" + + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" +) + +// SyncExternalAccessSpec holds configuration for the external access provided for the sync deployment. +type SyncExternalAccessSpec struct { + ExternalAccessSpec + MasterEndpoint []string `json:"masterEndpoint,omitempty"` + AccessPackageSecretNames []string `json:"accessPackageSecretNames,omitempty"` +} + +// GetMasterEndpoint returns the value of masterEndpoint. +func (s SyncExternalAccessSpec) GetMasterEndpoint() []string { + return s.MasterEndpoint +} + +// GetAccessPackageSecretNames returns the value of accessPackageSecretNames. +func (s SyncExternalAccessSpec) GetAccessPackageSecretNames() []string { + return s.AccessPackageSecretNames +} + +// ResolveMasterEndpoint returns the value of `--master.endpoint` option passed to arangosync. +func (s SyncExternalAccessSpec) ResolveMasterEndpoint(syncServiceHostName string, syncServicePort int) []string { + if len(s.MasterEndpoint) > 0 { + return s.MasterEndpoint + } + if ip := s.GetLoadBalancerIP(); ip != "" { + syncServiceHostName = ip + } + return []string{"https://" + net.JoinHostPort(syncServiceHostName, strconv.Itoa(syncServicePort))} +} + +// Validate the given spec +func (s SyncExternalAccessSpec) Validate() error { + if err := s.ExternalAccessSpec.Validate(); err != nil { + return maskAny(err) + } + for _, ep := range s.MasterEndpoint { + if _, err := url.Parse(ep); err != nil { + return maskAny(fmt.Errorf("Failed to parse master endpoint '%s': %s", ep, err)) + } + } + for _, name := range s.AccessPackageSecretNames { + if err := k8sutil.ValidateResourceName(name); err != nil { + return maskAny(fmt.Errorf("Invalid name '%s' in accessPackageSecretNames: %s", name, err)) + } + } + return nil +} + +// SetDefaults fills in missing defaults +func (s *SyncExternalAccessSpec) SetDefaults() { + s.ExternalAccessSpec.SetDefaults() +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *SyncExternalAccessSpec) SetDefaultsFrom(source SyncExternalAccessSpec) { + s.ExternalAccessSpec.SetDefaultsFrom(source.ExternalAccessSpec) + if s.MasterEndpoint == nil && source.MasterEndpoint != nil { + s.MasterEndpoint = append([]string{}, source.MasterEndpoint...) + } + if s.AccessPackageSecretNames == nil && source.AccessPackageSecretNames != nil { + s.AccessPackageSecretNames = append([]string{}, source.AccessPackageSecretNames...) + } +} + +// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. +// It returns a list of fields that have been reset. +// Field names are relative to given field prefix. +func (s SyncExternalAccessSpec) ResetImmutableFields(fieldPrefix string, target *SyncExternalAccessSpec) []string { + result := s.ExternalAccessSpec.ResetImmutableFields(fieldPrefix, &s.ExternalAccessSpec) + return result +} diff --git a/pkg/apis/deployment/v2alpha1/sync_spec.go b/pkg/apis/deployment/v2alpha1/sync_spec.go new file mode 100644 index 000000000..c5a107f2e --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/sync_spec.go @@ -0,0 +1,116 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/pkg/errors" + + "github.com/arangodb/kube-arangodb/pkg/util" +) + +// SyncSpec holds dc2dc replication specific configuration settings +type SyncSpec struct { + Enabled *bool `json:"enabled,omitempty"` + + ExternalAccess SyncExternalAccessSpec `json:"externalAccess"` + Authentication SyncAuthenticationSpec `json:"auth"` + TLS TLSSpec `json:"tls"` + Monitoring MonitoringSpec `json:"monitoring"` + Image *string `json:"image"` +} + +// IsEnabled returns the value of enabled. +func (s SyncSpec) IsEnabled() bool { + return util.BoolOrDefault(s.Enabled) +} + +// GetSyncImage returns the syncer image or empty string +func (s SyncSpec) GetSyncImage() string { + return util.StringOrDefault(s.Image) +} + +// HasSyncImage returns whether a special sync image is set +func (s SyncSpec) HasSyncImage() bool { + return s.GetSyncImage() != "" +} + +// Validate the given spec +func (s SyncSpec) Validate(mode DeploymentMode) error { + if s.IsEnabled() && !mode.SupportsSync() { + return maskAny(errors.Wrapf(ValidationError, "Cannot enable sync with mode: '%s'", mode)) + } + if s.IsEnabled() { + if err := s.ExternalAccess.Validate(); err != nil { + return maskAny(err) + } + if err := s.Authentication.Validate(); err != nil { + return maskAny(err) + } + if err := s.TLS.Validate(); err != nil { + return maskAny(err) + } + } + if err := s.Monitoring.Validate(); err != nil { + return maskAny(err) + } + return nil +} + +// SetDefaults fills in missing defaults +func (s *SyncSpec) SetDefaults(defaultJWTSecretName, defaultClientAuthCASecretName, defaultTLSCASecretName, defaultMonitoringSecretName string) { + s.ExternalAccess.SetDefaults() + s.Authentication.SetDefaults(defaultJWTSecretName, defaultClientAuthCASecretName) + s.TLS.SetDefaults(defaultTLSCASecretName) + s.Monitoring.SetDefaults(defaultMonitoringSecretName) +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *SyncSpec) SetDefaultsFrom(source SyncSpec) { + if s.Enabled == nil { + s.Enabled = util.NewBoolOrNil(source.Enabled) + } + if s.Image == nil { + s.Image = util.NewStringOrNil(source.Image) + } + s.ExternalAccess.SetDefaultsFrom(source.ExternalAccess) + s.Authentication.SetDefaultsFrom(source.Authentication) + s.TLS.SetDefaultsFrom(source.TLS) + s.Monitoring.SetDefaultsFrom(source.Monitoring) +} + +// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. +// It returns a list of fields that have been reset. +// Field names are relative to given field prefix. +func (s SyncSpec) ResetImmutableFields(fieldPrefix string, target *SyncSpec) []string { + var resetFields []string + if list := s.ExternalAccess.ResetImmutableFields(fieldPrefix+".externalAccess", &target.ExternalAccess); len(list) > 0 { + resetFields = append(resetFields, list...) + } + if list := s.Authentication.ResetImmutableFields(fieldPrefix+".auth", &target.Authentication); len(list) > 0 { + resetFields = append(resetFields, list...) + } + if s.GetSyncImage() != target.GetSyncImage() { + resetFields = append(resetFields, fieldPrefix+".image") + } + return resetFields +} diff --git a/pkg/apis/deployment/v2alpha1/sync_spec_test.go b/pkg/apis/deployment/v2alpha1/sync_spec_test.go new file mode 100644 index 000000000..c69680dd9 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/sync_spec_test.go @@ -0,0 +1,105 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/stretchr/testify/assert" +) + +func TestSyncSpecValidate(t *testing.T) { + // Valid + auth := SyncAuthenticationSpec{JWTSecretName: util.NewString("foo"), ClientCASecretName: util.NewString("foo-client")} + tls := TLSSpec{CASecretName: util.NewString("None")} + assert.Nil(t, SyncSpec{Authentication: auth}.Validate(DeploymentModeSingle)) + assert.Nil(t, SyncSpec{Authentication: auth}.Validate(DeploymentModeActiveFailover)) + assert.Nil(t, SyncSpec{Authentication: auth}.Validate(DeploymentModeCluster)) + assert.Nil(t, SyncSpec{Authentication: auth, TLS: tls, Enabled: util.NewBool(true)}.Validate(DeploymentModeCluster)) + + // Not valid + assert.Error(t, SyncSpec{Authentication: auth, TLS: tls, Enabled: util.NewBool(true)}.Validate(DeploymentModeSingle)) + assert.Error(t, SyncSpec{Authentication: auth, TLS: tls, Enabled: util.NewBool(true)}.Validate(DeploymentModeActiveFailover)) +} + +func TestSyncSpecSetDefaults(t *testing.T) { + def := func(spec SyncSpec) SyncSpec { + spec.SetDefaults("test-jwt", "test-client-auth-ca", "test-tls-ca", "test-mon") + return spec + } + + assert.False(t, def(SyncSpec{}).IsEnabled()) + assert.False(t, def(SyncSpec{Enabled: util.NewBool(false)}).IsEnabled()) + assert.True(t, def(SyncSpec{Enabled: util.NewBool(true)}).IsEnabled()) + assert.Equal(t, "test-jwt", def(SyncSpec{}).Authentication.GetJWTSecretName()) + assert.Equal(t, "test-mon", def(SyncSpec{}).Monitoring.GetTokenSecretName()) + assert.Equal(t, "foo", def(SyncSpec{Authentication: SyncAuthenticationSpec{JWTSecretName: util.NewString("foo")}}).Authentication.GetJWTSecretName()) +} + +func TestSyncSpecResetImmutableFields(t *testing.T) { + tests := []struct { + Original SyncSpec + Target SyncSpec + Expected SyncSpec + Result []string + }{ + // Valid "changes" + { + SyncSpec{Enabled: util.NewBool(false)}, + SyncSpec{Enabled: util.NewBool(true)}, + SyncSpec{Enabled: util.NewBool(true)}, + nil, + }, + { + SyncSpec{Enabled: util.NewBool(true)}, + SyncSpec{Enabled: util.NewBool(false)}, + SyncSpec{Enabled: util.NewBool(false)}, + nil, + }, + { + SyncSpec{Authentication: SyncAuthenticationSpec{JWTSecretName: util.NewString("None"), ClientCASecretName: util.NewString("some")}}, + SyncSpec{Authentication: SyncAuthenticationSpec{JWTSecretName: util.NewString("None"), ClientCASecretName: util.NewString("some")}}, + SyncSpec{Authentication: SyncAuthenticationSpec{JWTSecretName: util.NewString("None"), ClientCASecretName: util.NewString("some")}}, + nil, + }, + { + SyncSpec{Authentication: SyncAuthenticationSpec{JWTSecretName: util.NewString("foo"), ClientCASecretName: util.NewString("some")}}, + SyncSpec{Authentication: SyncAuthenticationSpec{JWTSecretName: util.NewString("foo"), ClientCASecretName: util.NewString("some")}}, + SyncSpec{Authentication: SyncAuthenticationSpec{JWTSecretName: util.NewString("foo"), ClientCASecretName: util.NewString("some")}}, + nil, + }, + { + SyncSpec{Authentication: SyncAuthenticationSpec{JWTSecretName: util.NewString("foo"), ClientCASecretName: util.NewString("some")}}, + SyncSpec{Authentication: SyncAuthenticationSpec{JWTSecretName: util.NewString("foo2"), ClientCASecretName: util.NewString("some")}}, + SyncSpec{Authentication: SyncAuthenticationSpec{JWTSecretName: util.NewString("foo2"), ClientCASecretName: util.NewString("some")}}, + nil, + }, + } + + for _, test := range tests { + result := test.Original.ResetImmutableFields("test", &test.Target) + assert.Equal(t, test.Result, result) + assert.Equal(t, test.Expected, test.Target) + } +} diff --git a/pkg/apis/deployment/v2alpha1/timeouts.go b/pkg/apis/deployment/v2alpha1/timeouts.go new file mode 100644 index 000000000..ef6b73205 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/timeouts.go @@ -0,0 +1,55 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 Adam Janikowski +// + +package v2alpha1 + +import ( + "time" + + meta "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + addMemberTimeout = time.Minute * 5 +) + +type Timeouts struct { + AddMember *Timeout `json:"addMember,omitempty"` +} + +func (t *Timeouts) Get() Timeouts { + if t == nil { + return Timeouts{} + } + + return *t +} + +type Timeout meta.Duration + +func (t *Timeout) Get(d time.Duration) time.Duration { + if t == nil { + return d + } + + return t.Duration +} diff --git a/pkg/apis/deployment/v2alpha1/tls_sni_spec.go b/pkg/apis/deployment/v2alpha1/tls_sni_spec.go new file mode 100644 index 000000000..0dd739713 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/tls_sni_spec.go @@ -0,0 +1,65 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + shared "github.com/arangodb/kube-arangodb/pkg/apis/shared/v1" + "github.com/pkg/errors" +) + +// TLSSNISpec holds TLS SNI additional certificates +type TLSSNISpec struct { + Mapping map[string][]string `json:"mapping,omitempty"` +} + +func (s TLSSNISpec) Validate() error { + mapped := map[string]interface{}{} + + for key, values := range s.Mapping { + if err := shared.IsValidName(key); err != nil { + return err + } + + for _, value := range values { + if _, exists := mapped[value]; exists { + return errors.Errorf("sni for host %s is already defined", value) + } + + // Mark value as existing + mapped[value] = nil + + if err := shared.IsValidDomain(value); err != nil { + return err + } + } + } + + return nil +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *TLSSNISpec) SetDefaultsFrom(source *TLSSNISpec) { + if source == nil { + return + } +} diff --git a/pkg/apis/deployment/v2alpha1/tls_spec.go b/pkg/apis/deployment/v2alpha1/tls_spec.go new file mode 100644 index 000000000..bc6126362 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/tls_spec.go @@ -0,0 +1,160 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "fmt" + "net" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" + "github.com/arangodb/kube-arangodb/pkg/util/validation" +) + +type TLSRotateMode string + +func (t *TLSRotateMode) Get() TLSRotateMode { + if t == nil { + return TLSRotateModeInPlace + } + + return *t +} + +func (t TLSRotateMode) New() *TLSRotateMode { + return &t +} + +const ( + TLSRotateModeInPlace TLSRotateMode = "inplace" + TLSRotateModeRecreate TLSRotateMode = "recreate" +) + +const ( + defaultTLSTTL = Duration("2610h") // About 3 month +) + +// TLSSpec holds TLS specific configuration settings +type TLSSpec struct { + CASecretName *string `json:"caSecretName,omitempty"` + AltNames []string `json:"altNames,omitempty"` + TTL *Duration `json:"ttl,omitempty"` + SNI *TLSSNISpec `json:"sni,omitempty"` + Mode *TLSRotateMode `json:"mode,omitempty"` +} + +const ( + // CASecretNameDisabled is the value of CASecretName to use for disabling authentication. + CASecretNameDisabled = "None" +) + +// GetCASecretName returns the value of caSecretName. +func (s TLSSpec) GetCASecretName() string { + return util.StringOrDefault(s.CASecretName) +} + +// GetAltNames returns the value of altNames. +func (s TLSSpec) GetAltNames() []string { + return s.AltNames +} + +// GetTTL returns the value of ttl. +func (s TLSSpec) GetTTL() Duration { + return DurationOrDefault(s.TTL) +} + +func (a TLSSpec) GetSNI() TLSSNISpec { + if a.SNI == nil { + return TLSSNISpec{} + } + + return *a.SNI +} + +// IsSecure returns true when a CA secret has been set, false otherwise. +func (s TLSSpec) IsSecure() bool { + return s.GetCASecretName() != CASecretNameDisabled +} + +// GetParsedAltNames splits the list of AltNames into DNS names, IP addresses & email addresses. +// When an entry is not valid for any of those categories, an error is returned. +func (s TLSSpec) GetParsedAltNames() (dnsNames, ipAddresses, emailAddresses []string, err error) { + for _, name := range s.GetAltNames() { + if net.ParseIP(name) != nil { + ipAddresses = append(ipAddresses, name) + } else if validation.IsValidDNSName(name) { + dnsNames = append(dnsNames, name) + } else if validation.IsValidEmailAddress(name) { + emailAddresses = append(emailAddresses, name) + } else { + return nil, nil, nil, maskAny(fmt.Errorf("'%s' is not a valid alternate name", name)) + } + } + return dnsNames, ipAddresses, emailAddresses, nil +} + +// Validate the given spec +func (s TLSSpec) Validate() error { + if s.IsSecure() { + if err := k8sutil.ValidateResourceName(s.GetCASecretName()); err != nil { + return maskAny(err) + } + if _, _, _, err := s.GetParsedAltNames(); err != nil { + return maskAny(err) + } + if err := s.GetTTL().Validate(); err != nil { + return maskAny(err) + } + } + return nil +} + +// SetDefaults fills in missing defaults +func (s *TLSSpec) SetDefaults(defaultCASecretName string) { + if s.GetCASecretName() == "" { + // Note that we don't check for nil here, since even a specified, but empty + // string should result in the default value. + s.CASecretName = util.NewString(defaultCASecretName) + } + if s.GetTTL() == "" { + // Note that we don't check for nil here, since even a specified, but zero + // should result in the default value. + s.TTL = NewDuration(defaultTLSTTL) + } +} + +// SetDefaultsFrom fills unspecified fields with a value from given source spec. +func (s *TLSSpec) SetDefaultsFrom(source TLSSpec) { + if s.CASecretName == nil { + s.CASecretName = util.NewStringOrNil(source.CASecretName) + } + if s.AltNames == nil { + s.AltNames = source.AltNames + } + if s.TTL == nil { + s.TTL = NewDurationOrNil(source.TTL) + } + if s.SNI == nil { + s.SNI = source.SNI.DeepCopy() + } +} diff --git a/pkg/apis/deployment/v2alpha1/tls_spec_test.go b/pkg/apis/deployment/v2alpha1/tls_spec_test.go new file mode 100644 index 000000000..bee4ad421 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/tls_spec_test.go @@ -0,0 +1,66 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + "time" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/stretchr/testify/assert" +) + +func TestTLSSpecValidate(t *testing.T) { + // Valid + assert.Nil(t, TLSSpec{CASecretName: util.NewString("foo")}.Validate()) + assert.Nil(t, TLSSpec{CASecretName: util.NewString("None")}.Validate()) + assert.Nil(t, TLSSpec{CASecretName: util.NewString("None"), AltNames: []string{}}.Validate()) + assert.Nil(t, TLSSpec{CASecretName: util.NewString("None"), AltNames: []string{"foo"}}.Validate()) + assert.Nil(t, TLSSpec{CASecretName: util.NewString("None"), AltNames: []string{"email@example.com", "127.0.0.1"}}.Validate()) + + // Not valid + assert.Error(t, TLSSpec{CASecretName: nil}.Validate()) + assert.Error(t, TLSSpec{CASecretName: util.NewString("")}.Validate()) + assert.Error(t, TLSSpec{CASecretName: util.NewString("Foo")}.Validate()) + assert.Error(t, TLSSpec{CASecretName: util.NewString("foo"), AltNames: []string{"@@"}}.Validate()) +} + +func TestTLSSpecIsSecure(t *testing.T) { + assert.True(t, TLSSpec{CASecretName: util.NewString("")}.IsSecure()) + assert.True(t, TLSSpec{CASecretName: util.NewString("foo")}.IsSecure()) + assert.False(t, TLSSpec{CASecretName: util.NewString("None")}.IsSecure()) +} + +func TestTLSSpecSetDefaults(t *testing.T) { + def := func(spec TLSSpec) TLSSpec { + spec.SetDefaults("") + return spec + } + + assert.Equal(t, "", def(TLSSpec{}).GetCASecretName()) + assert.Equal(t, "foo", def(TLSSpec{CASecretName: util.NewString("foo")}).GetCASecretName()) + assert.Len(t, def(TLSSpec{}).GetAltNames(), 0) + assert.Len(t, def(TLSSpec{AltNames: []string{"foo.local"}}).GetAltNames(), 1) + assert.Equal(t, defaultTLSTTL, def(TLSSpec{}).GetTTL()) + assert.Equal(t, time.Hour, def(TLSSpec{TTL: NewDuration("1h")}).GetTTL().AsDuration()) +} diff --git a/pkg/apis/deployment/v2alpha1/zz_generated.deepcopy.go b/pkg/apis/deployment/v2alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..125a4691b --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/zz_generated.deepcopy.go @@ -0,0 +1,1889 @@ +// +build !ignore_autogenerated + +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + time "time" + + sharedv1 "github.com/arangodb/kube-arangodb/pkg/apis/shared/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Action) DeepCopyInto(out *Action) { + *out = *in + in.CreationTime.DeepCopyInto(&out.CreationTime) + if in.StartTime != nil { + in, out := &in.StartTime, &out.StartTime + *out = (*in).DeepCopy() + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Action. +func (in *Action) DeepCopy() *Action { + if in == nil { + return nil + } + out := new(Action) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ArangoDeployment) DeepCopyInto(out *ArangoDeployment) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoDeployment. +func (in *ArangoDeployment) DeepCopy() *ArangoDeployment { + if in == nil { + return nil + } + out := new(ArangoDeployment) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ArangoDeployment) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ArangoDeploymentList) DeepCopyInto(out *ArangoDeploymentList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ArangoDeployment, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoDeploymentList. +func (in *ArangoDeploymentList) DeepCopy() *ArangoDeploymentList { + if in == nil { + return nil + } + out := new(ArangoDeploymentList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ArangoDeploymentList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ArangoDeploymentRecoverySpec) DeepCopyInto(out *ArangoDeploymentRecoverySpec) { + *out = *in + if in.AutoRecover != nil { + in, out := &in.AutoRecover, &out.AutoRecover + *out = new(bool) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoDeploymentRecoverySpec. +func (in *ArangoDeploymentRecoverySpec) DeepCopy() *ArangoDeploymentRecoverySpec { + if in == nil { + return nil + } + out := new(ArangoDeploymentRecoverySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AuthenticationSpec) DeepCopyInto(out *AuthenticationSpec) { + *out = *in + if in.JWTSecretName != nil { + in, out := &in.JWTSecretName, &out.JWTSecretName + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthenticationSpec. +func (in *AuthenticationSpec) DeepCopy() *AuthenticationSpec { + if in == nil { + return nil + } + out := new(AuthenticationSpec) + in.DeepCopyInto(out) + 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 + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + *out = new(time.Duration) + **out = **in + } + if in.KillPodProbability != nil { + in, out := &in.KillPodProbability, &out.KillPodProbability + *out = new(Percent) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChaosSpec. +func (in *ChaosSpec) DeepCopy() *ChaosSpec { + if in == nil { + return nil + } + out := new(ChaosSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Condition) DeepCopyInto(out *Condition) { + *out = *in + in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. +func (in *Condition) DeepCopy() *Condition { + if in == nil { + return nil + } + out := new(Condition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in ConditionList) DeepCopyInto(out *ConditionList) { + { + in := &in + *out = make(ConditionList, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConditionList. +func (in ConditionList) DeepCopy() ConditionList { + if in == nil { + return nil + } + out := new(ConditionList) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseSpec) DeepCopyInto(out *DatabaseSpec) { + *out = *in + if in.Maintenance != nil { + in, out := &in.Maintenance, &out.Maintenance + *out = new(bool) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseSpec. +func (in *DatabaseSpec) DeepCopy() *DatabaseSpec { + if in == nil { + return nil + } + out := new(DatabaseSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentFeatures) DeepCopyInto(out *DeploymentFeatures) { + *out = *in + if in.FoxxQueues != nil { + in, out := &in.FoxxQueues, &out.FoxxQueues + *out = new(bool) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentFeatures. +func (in *DeploymentFeatures) DeepCopy() *DeploymentFeatures { + if in == nil { + return nil + } + out := new(DeploymentFeatures) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentRestoreResult) DeepCopyInto(out *DeploymentRestoreResult) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentRestoreResult. +func (in *DeploymentRestoreResult) DeepCopy() *DeploymentRestoreResult { + if in == nil { + return nil + } + out := new(DeploymentRestoreResult) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) { + *out = *in + if in.Mode != nil { + in, out := &in.Mode, &out.Mode + *out = new(DeploymentMode) + **out = **in + } + if in.Environment != nil { + in, out := &in.Environment, &out.Environment + *out = new(Environment) + **out = **in + } + if in.StorageEngine != nil { + in, out := &in.StorageEngine, &out.StorageEngine + *out = new(StorageEngine) + **out = **in + } + if in.Image != nil { + in, out := &in.Image, &out.Image + *out = new(string) + **out = **in + } + if in.ImagePullPolicy != nil { + in, out := &in.ImagePullPolicy, &out.ImagePullPolicy + *out = new(v1.PullPolicy) + **out = **in + } + if in.ImagePullSecrets != nil { + in, out := &in.ImagePullSecrets, &out.ImagePullSecrets + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ImageDiscoveryMode != nil { + in, out := &in.ImageDiscoveryMode, &out.ImageDiscoveryMode + *out = new(DeploymentImageDiscoveryModeSpec) + **out = **in + } + if in.DowntimeAllowed != nil { + in, out := &in.DowntimeAllowed, &out.DowntimeAllowed + *out = new(bool) + **out = **in + } + if in.DisableIPv6 != nil { + in, out := &in.DisableIPv6, &out.DisableIPv6 + *out = new(bool) + **out = **in + } + if in.Features != nil { + in, out := &in.Features, &out.Features + *out = new(DeploymentFeatures) + (*in).DeepCopyInto(*out) + } + if in.NetworkAttachedVolumes != nil { + in, out := &in.NetworkAttachedVolumes, &out.NetworkAttachedVolumes + *out = new(bool) + **out = **in + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.AnnotationsIgnoreList != nil { + in, out := &in.AnnotationsIgnoreList, &out.AnnotationsIgnoreList + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.AnnotationsMode != nil { + in, out := &in.AnnotationsMode, &out.AnnotationsMode + *out = new(LabelsMode) + **out = **in + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.LabelsIgnoreList != nil { + in, out := &in.LabelsIgnoreList, &out.LabelsIgnoreList + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.LabelsMode != nil { + in, out := &in.LabelsMode, &out.LabelsMode + *out = new(LabelsMode) + **out = **in + } + if in.RestoreFrom != nil { + in, out := &in.RestoreFrom, &out.RestoreFrom + *out = new(string) + **out = **in + } + if in.RestoreEncryptionSecret != nil { + in, out := &in.RestoreEncryptionSecret, &out.RestoreEncryptionSecret + *out = new(string) + **out = **in + } + if in.AllowUnsafeUpgrade != nil { + in, out := &in.AllowUnsafeUpgrade, &out.AllowUnsafeUpgrade + *out = new(bool) + **out = **in + } + in.ExternalAccess.DeepCopyInto(&out.ExternalAccess) + in.RocksDB.DeepCopyInto(&out.RocksDB) + in.Authentication.DeepCopyInto(&out.Authentication) + in.TLS.DeepCopyInto(&out.TLS) + in.Sync.DeepCopyInto(&out.Sync) + in.License.DeepCopyInto(&out.License) + in.Metrics.DeepCopyInto(&out.Metrics) + in.Lifecycle.DeepCopyInto(&out.Lifecycle) + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(ServerIDGroupSpec) + (*in).DeepCopyInto(*out) + } + if in.Database != nil { + in, out := &in.Database, &out.Database + *out = new(DatabaseSpec) + (*in).DeepCopyInto(*out) + } + in.Single.DeepCopyInto(&out.Single) + in.Agents.DeepCopyInto(&out.Agents) + in.DBServers.DeepCopyInto(&out.DBServers) + in.Coordinators.DeepCopyInto(&out.Coordinators) + in.SyncMasters.DeepCopyInto(&out.SyncMasters) + in.SyncWorkers.DeepCopyInto(&out.SyncWorkers) + in.Chaos.DeepCopyInto(&out.Chaos) + if in.Recovery != nil { + in, out := &in.Recovery, &out.Recovery + *out = new(ArangoDeploymentRecoverySpec) + (*in).DeepCopyInto(*out) + } + in.Bootstrap.DeepCopyInto(&out.Bootstrap) + if in.Timeouts != nil { + in, out := &in.Timeouts, &out.Timeouts + *out = new(Timeouts) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentSpec. +func (in *DeploymentSpec) DeepCopy() *DeploymentSpec { + if in == nil { + return nil + } + out := new(DeploymentSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) { + *out = *in + if in.Restore != nil { + in, out := &in.Restore, &out.Restore + *out = new(DeploymentRestoreResult) + **out = **in + } + if in.Images != nil { + in, out := &in.Images, &out.Images + *out = make(ImageInfoList, len(*in)) + copy(*out, *in) + } + if in.CurrentImage != nil { + in, out := &in.CurrentImage, &out.CurrentImage + *out = new(ImageInfo) + **out = **in + } + in.Members.DeepCopyInto(&out.Members) + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(ConditionList, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Plan != nil { + in, out := &in.Plan, &out.Plan + *out = make(Plan, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AcceptedSpec != nil { + in, out := &in.AcceptedSpec, &out.AcceptedSpec + *out = new(DeploymentSpec) + (*in).DeepCopyInto(*out) + } + if in.SecretHashes != nil { + in, out := &in.SecretHashes, &out.SecretHashes + *out = new(SecretHashes) + (*in).DeepCopyInto(*out) + } + in.Hashes.DeepCopyInto(&out.Hashes) + if in.ForceStatusReload != nil { + in, out := &in.ForceStatusReload, &out.ForceStatusReload + *out = new(bool) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentStatus. +func (in *DeploymentStatus) DeepCopy() *DeploymentStatus { + if in == nil { + return nil + } + out := new(DeploymentStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentStatusHashes) DeepCopyInto(out *DeploymentStatusHashes) { + *out = *in + in.Encryption.DeepCopyInto(&out.Encryption) + in.TLS.DeepCopyInto(&out.TLS) + in.JWT.DeepCopyInto(&out.JWT) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentStatusHashes. +func (in *DeploymentStatusHashes) DeepCopy() *DeploymentStatusHashes { + if in == nil { + return nil + } + out := new(DeploymentStatusHashes) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentStatusHashesEncryption) DeepCopyInto(out *DeploymentStatusHashesEncryption) { + *out = *in + if in.Keys != nil { + in, out := &in.Keys, &out.Keys + *out = make(sharedv1.HashList, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentStatusHashesEncryption. +func (in *DeploymentStatusHashesEncryption) DeepCopy() *DeploymentStatusHashesEncryption { + if in == nil { + return nil + } + out := new(DeploymentStatusHashesEncryption) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentStatusHashesJWT) DeepCopyInto(out *DeploymentStatusHashesJWT) { + *out = *in + if in.Passive != nil { + in, out := &in.Passive, &out.Passive + *out = make(sharedv1.HashList, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentStatusHashesJWT. +func (in *DeploymentStatusHashesJWT) DeepCopy() *DeploymentStatusHashesJWT { + if in == nil { + return nil + } + out := new(DeploymentStatusHashesJWT) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentStatusHashesTLS) DeepCopyInto(out *DeploymentStatusHashesTLS) { + *out = *in + if in.CA != nil { + in, out := &in.CA, &out.CA + *out = new(string) + **out = **in + } + if in.Truststore != nil { + in, out := &in.Truststore, &out.Truststore + *out = make(sharedv1.HashList, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentStatusHashesTLS. +func (in *DeploymentStatusHashesTLS) DeepCopy() *DeploymentStatusHashesTLS { + if in == nil { + return nil + } + out := new(DeploymentStatusHashesTLS) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentStatusMembers) DeepCopyInto(out *DeploymentStatusMembers) { + *out = *in + if in.Single != nil { + in, out := &in.Single, &out.Single + *out = make(MemberStatusList, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Agents != nil { + in, out := &in.Agents, &out.Agents + *out = make(MemberStatusList, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.DBServers != nil { + in, out := &in.DBServers, &out.DBServers + *out = make(MemberStatusList, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Coordinators != nil { + in, out := &in.Coordinators, &out.Coordinators + *out = make(MemberStatusList, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SyncMasters != nil { + in, out := &in.SyncMasters, &out.SyncMasters + *out = make(MemberStatusList, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SyncWorkers != nil { + in, out := &in.SyncWorkers, &out.SyncWorkers + *out = make(MemberStatusList, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentStatusMembers. +func (in *DeploymentStatusMembers) DeepCopy() *DeploymentStatusMembers { + if in == nil { + return nil + } + out := new(DeploymentStatusMembers) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExternalAccessSpec) DeepCopyInto(out *ExternalAccessSpec) { + *out = *in + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(ExternalAccessType) + **out = **in + } + if in.NodePort != nil { + in, out := &in.NodePort, &out.NodePort + *out = new(int) + **out = **in + } + if in.LoadBalancerIP != nil { + in, out := &in.LoadBalancerIP, &out.LoadBalancerIP + *out = new(string) + **out = **in + } + if in.LoadBalancerSourceRanges != nil { + in, out := &in.LoadBalancerSourceRanges, &out.LoadBalancerSourceRanges + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.AdvertisedEndpoint != nil { + in, out := &in.AdvertisedEndpoint, &out.AdvertisedEndpoint + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalAccessSpec. +func (in *ExternalAccessSpec) DeepCopy() *ExternalAccessSpec { + if in == nil { + return nil + } + out := new(ExternalAccessSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageInfo) DeepCopyInto(out *ImageInfo) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageInfo. +func (in *ImageInfo) DeepCopy() *ImageInfo { + if in == nil { + return nil + } + out := new(ImageInfo) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in ImageInfoList) DeepCopyInto(out *ImageInfoList) { + { + in := &in + *out = make(ImageInfoList, len(*in)) + copy(*out, *in) + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageInfoList. +func (in ImageInfoList) DeepCopy() ImageInfoList { + if in == nil { + return nil + } + out := new(ImageInfoList) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LicenseSpec) DeepCopyInto(out *LicenseSpec) { + *out = *in + if in.SecretName != nil { + in, out := &in.SecretName, &out.SecretName + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LicenseSpec. +func (in *LicenseSpec) DeepCopy() *LicenseSpec { + if in == nil { + return nil + } + out := new(LicenseSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LifecycleSpec) DeepCopyInto(out *LifecycleSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LifecycleSpec. +func (in *LifecycleSpec) DeepCopy() *LifecycleSpec { + if in == nil { + return nil + } + out := new(LifecycleSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MemberStatus) DeepCopyInto(out *MemberStatus) { + *out = *in + in.CreatedAt.DeepCopyInto(&out.CreatedAt) + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(ConditionList, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.RecentTerminations != nil { + in, out := &in.RecentTerminations, &out.RecentTerminations + *out = make([]metav1.Time, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SideCarSpecs != nil { + in, out := &in.SideCarSpecs, &out.SideCarSpecs + *out = make(map[string]v1.Container, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + if in.Image != nil { + in, out := &in.Image, &out.Image + *out = new(ImageInfo) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MemberStatus. +func (in *MemberStatus) DeepCopy() *MemberStatus { + if in == nil { + return nil + } + out := new(MemberStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in MemberStatusList) DeepCopyInto(out *MemberStatusList) { + { + in := &in + *out = make(MemberStatusList, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MemberStatusList. +func (in MemberStatusList) DeepCopy() MemberStatusList { + if in == nil { + return nil + } + out := new(MemberStatusList) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MetricsAuthenticationSpec) DeepCopyInto(out *MetricsAuthenticationSpec) { + *out = *in + if in.JWTTokenSecretName != nil { + in, out := &in.JWTTokenSecretName, &out.JWTTokenSecretName + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricsAuthenticationSpec. +func (in *MetricsAuthenticationSpec) DeepCopy() *MetricsAuthenticationSpec { + if in == nil { + return nil + } + out := new(MetricsAuthenticationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MetricsSpec) DeepCopyInto(out *MetricsSpec) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.Image != nil { + in, out := &in.Image, &out.Image + *out = new(string) + **out = **in + } + in.Authentication.DeepCopyInto(&out.Authentication) + in.Resources.DeepCopyInto(&out.Resources) + if in.Mode != nil { + in, out := &in.Mode, &out.Mode + *out = new(MetricsMode) + **out = **in + } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(bool) + **out = **in + } + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(uint16) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricsSpec. +func (in *MetricsSpec) DeepCopy() *MetricsSpec { + if in == nil { + return nil + } + out := new(MetricsSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonitoringSpec) DeepCopyInto(out *MonitoringSpec) { + *out = *in + if in.TokenSecretName != nil { + in, out := &in.TokenSecretName, &out.TokenSecretName + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonitoringSpec. +func (in *MonitoringSpec) DeepCopy() *MonitoringSpec { + if in == nil { + return nil + } + out := new(MonitoringSpec) + in.DeepCopyInto(out) + 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) { + { + in := &in + *out = make(Plan, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Plan. +func (in Plan) DeepCopy() Plan { + if in == nil { + return nil + } + out := new(Plan) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RocksDBEncryptionSpec) DeepCopyInto(out *RocksDBEncryptionSpec) { + *out = *in + if in.KeySecretName != nil { + in, out := &in.KeySecretName, &out.KeySecretName + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RocksDBEncryptionSpec. +func (in *RocksDBEncryptionSpec) DeepCopy() *RocksDBEncryptionSpec { + if in == nil { + return nil + } + out := new(RocksDBEncryptionSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RocksDBSpec) DeepCopyInto(out *RocksDBSpec) { + *out = *in + in.Encryption.DeepCopyInto(&out.Encryption) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RocksDBSpec. +func (in *RocksDBSpec) DeepCopy() *RocksDBSpec { + if in == nil { + return nil + } + out := new(RocksDBSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretHashes) DeepCopyInto(out *SecretHashes) { + *out = *in + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretHashes. +func (in *SecretHashes) DeepCopy() *SecretHashes { + if in == nil { + return nil + } + out := new(SecretHashes) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerGroupEnvVar) DeepCopyInto(out *ServerGroupEnvVar) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerGroupEnvVar. +func (in *ServerGroupEnvVar) DeepCopy() *ServerGroupEnvVar { + if in == nil { + return nil + } + out := new(ServerGroupEnvVar) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in ServerGroupEnvVars) DeepCopyInto(out *ServerGroupEnvVars) { + { + in := &in + *out = make(ServerGroupEnvVars, len(*in)) + copy(*out, *in) + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerGroupEnvVars. +func (in ServerGroupEnvVars) DeepCopy() ServerGroupEnvVars { + if in == nil { + return nil + } + out := new(ServerGroupEnvVars) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerGroupInitContainers) DeepCopyInto(out *ServerGroupInitContainers) { + *out = *in + if in.Containers != nil { + in, out := &in.Containers, &out.Containers + *out = make([]v1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Mode != nil { + in, out := &in.Mode, &out.Mode + *out = new(ServerGroupInitContainerMode) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerGroupInitContainers. +func (in *ServerGroupInitContainers) DeepCopy() *ServerGroupInitContainers { + if in == nil { + return nil + } + out := new(ServerGroupInitContainers) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerGroupProbeSpec) DeepCopyInto(out *ServerGroupProbeSpec) { + *out = *in + if in.InitialDelaySeconds != nil { + in, out := &in.InitialDelaySeconds, &out.InitialDelaySeconds + *out = new(int32) + **out = **in + } + if in.PeriodSeconds != nil { + in, out := &in.PeriodSeconds, &out.PeriodSeconds + *out = new(int32) + **out = **in + } + if in.TimeoutSeconds != nil { + in, out := &in.TimeoutSeconds, &out.TimeoutSeconds + *out = new(int32) + **out = **in + } + if in.SuccessThreshold != nil { + in, out := &in.SuccessThreshold, &out.SuccessThreshold + *out = new(int32) + **out = **in + } + if in.FailureThreshold != nil { + in, out := &in.FailureThreshold, &out.FailureThreshold + *out = new(int32) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerGroupProbeSpec. +func (in *ServerGroupProbeSpec) DeepCopy() *ServerGroupProbeSpec { + if in == nil { + return nil + } + out := new(ServerGroupProbeSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerGroupProbesSpec) DeepCopyInto(out *ServerGroupProbesSpec) { + *out = *in + if in.LivenessProbeDisabled != nil { + in, out := &in.LivenessProbeDisabled, &out.LivenessProbeDisabled + *out = new(bool) + **out = **in + } + if in.LivenessProbeSpec != nil { + in, out := &in.LivenessProbeSpec, &out.LivenessProbeSpec + *out = new(ServerGroupProbeSpec) + (*in).DeepCopyInto(*out) + } + if in.OldReadinessProbeDisabled != nil { + in, out := &in.OldReadinessProbeDisabled, &out.OldReadinessProbeDisabled + *out = new(bool) + **out = **in + } + if in.ReadinessProbeDisabled != nil { + in, out := &in.ReadinessProbeDisabled, &out.ReadinessProbeDisabled + *out = new(bool) + **out = **in + } + if in.ReadinessProbeSpec != nil { + in, out := &in.ReadinessProbeSpec, &out.ReadinessProbeSpec + *out = new(ServerGroupProbeSpec) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerGroupProbesSpec. +func (in *ServerGroupProbesSpec) DeepCopy() *ServerGroupProbesSpec { + if in == nil { + return nil + } + out := new(ServerGroupProbesSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerGroupSpec) DeepCopyInto(out *ServerGroupSpec) { + *out = *in + if in.Count != nil { + in, out := &in.Count, &out.Count + *out = new(int) + **out = **in + } + if in.MinCount != nil { + in, out := &in.MinCount, &out.MinCount + *out = new(int) + **out = **in + } + if in.MaxCount != nil { + in, out := &in.MaxCount, &out.MaxCount + *out = new(int) + **out = **in + } + if in.Args != nil { + in, out := &in.Args, &out.Args + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Entrypoint != nil { + in, out := &in.Entrypoint, &out.Entrypoint + *out = new(string) + **out = **in + } + if in.StorageClassName != nil { + in, out := &in.StorageClassName, &out.StorageClassName + *out = new(string) + **out = **in + } + in.Resources.DeepCopyInto(&out.Resources) + if in.OverrideDetectedTotalMemory != nil { + in, out := &in.OverrideDetectedTotalMemory, &out.OverrideDetectedTotalMemory + *out = new(bool) + **out = **in + } + if in.OverrideDetectedNumberOfCores != nil { + in, out := &in.OverrideDetectedNumberOfCores, &out.OverrideDetectedNumberOfCores + *out = new(bool) + **out = **in + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.AnnotationsIgnoreList != nil { + in, out := &in.AnnotationsIgnoreList, &out.AnnotationsIgnoreList + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.AnnotationsMode != nil { + in, out := &in.AnnotationsMode, &out.AnnotationsMode + *out = new(LabelsMode) + **out = **in + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.LabelsIgnoreList != nil { + in, out := &in.LabelsIgnoreList, &out.LabelsIgnoreList + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.LabelsMode != nil { + in, out := &in.LabelsMode, &out.LabelsMode + *out = new(LabelsMode) + **out = **in + } + if in.Envs != nil { + in, out := &in.Envs, &out.Envs + *out = make(ServerGroupEnvVars, len(*in)) + copy(*out, *in) + } + if in.ServiceAccountName != nil { + in, out := &in.ServiceAccountName, &out.ServiceAccountName + *out = new(string) + **out = **in + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Probes != nil { + in, out := &in.Probes, &out.Probes + *out = new(ServerGroupProbesSpec) + (*in).DeepCopyInto(*out) + } + if in.VolumeClaimTemplate != nil { + in, out := &in.VolumeClaimTemplate, &out.VolumeClaimTemplate + *out = new(v1.PersistentVolumeClaim) + (*in).DeepCopyInto(*out) + } + if in.VolumeResizeMode != nil { + in, out := &in.VolumeResizeMode, &out.VolumeResizeMode + *out = new(PVCResizeMode) + **out = **in + } + if in.VolumeAllowShrink != nil { + in, out := &in.VolumeAllowShrink, &out.VolumeAllowShrink + *out = new(bool) + **out = **in + } + if in.AntiAffinity != nil { + in, out := &in.AntiAffinity, &out.AntiAffinity + *out = new(v1.PodAntiAffinity) + (*in).DeepCopyInto(*out) + } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(v1.PodAffinity) + (*in).DeepCopyInto(*out) + } + if in.NodeAffinity != nil { + in, out := &in.NodeAffinity, &out.NodeAffinity + *out = new(v1.NodeAffinity) + (*in).DeepCopyInto(*out) + } + if in.Sidecars != nil { + in, out := &in.Sidecars, &out.Sidecars + *out = make([]v1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(ServerGroupSpecSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make(ServerGroupSpecVolumes, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.VolumeMounts != nil { + in, out := &in.VolumeMounts, &out.VolumeMounts + *out = make(ServerGroupSpecVolumeMounts, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ExtendedRotationCheck != nil { + in, out := &in.ExtendedRotationCheck, &out.ExtendedRotationCheck + *out = new(bool) + **out = **in + } + if in.InitContainers != nil { + in, out := &in.InitContainers, &out.InitContainers + *out = new(ServerGroupInitContainers) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerGroupSpec. +func (in *ServerGroupSpec) DeepCopy() *ServerGroupSpec { + if in == nil { + return nil + } + out := new(ServerGroupSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerGroupSpecSecurityContext) DeepCopyInto(out *ServerGroupSpecSecurityContext) { + *out = *in + if in.DropAllCapabilities != nil { + in, out := &in.DropAllCapabilities, &out.DropAllCapabilities + *out = new(bool) + **out = **in + } + if in.AddCapabilities != nil { + in, out := &in.AddCapabilities, &out.AddCapabilities + *out = make([]v1.Capability, len(*in)) + copy(*out, *in) + } + if in.AllowPrivilegeEscalation != nil { + in, out := &in.AllowPrivilegeEscalation, &out.AllowPrivilegeEscalation + *out = new(bool) + **out = **in + } + if in.Privileged != nil { + in, out := &in.Privileged, &out.Privileged + *out = new(bool) + **out = **in + } + if in.ReadOnlyRootFilesystem != nil { + in, out := &in.ReadOnlyRootFilesystem, &out.ReadOnlyRootFilesystem + *out = new(bool) + **out = **in + } + if in.RunAsNonRoot != nil { + in, out := &in.RunAsNonRoot, &out.RunAsNonRoot + *out = new(bool) + **out = **in + } + if in.RunAsUser != nil { + in, out := &in.RunAsUser, &out.RunAsUser + *out = new(int64) + **out = **in + } + if in.RunAsGroup != nil { + in, out := &in.RunAsGroup, &out.RunAsGroup + *out = new(int64) + **out = **in + } + if in.SupplementalGroups != nil { + in, out := &in.SupplementalGroups, &out.SupplementalGroups + *out = make([]int64, len(*in)) + copy(*out, *in) + } + if in.FSGroup != nil { + in, out := &in.FSGroup, &out.FSGroup + *out = new(int64) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerGroupSpecSecurityContext. +func (in *ServerGroupSpecSecurityContext) DeepCopy() *ServerGroupSpecSecurityContext { + if in == nil { + return nil + } + out := new(ServerGroupSpecSecurityContext) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerGroupSpecVolume) DeepCopyInto(out *ServerGroupSpecVolume) { + *out = *in + if in.Secret != nil { + in, out := &in.Secret, &out.Secret + *out = new(ServerGroupSpecVolumeSecret) + (*in).DeepCopyInto(*out) + } + if in.ConfigMap != nil { + in, out := &in.ConfigMap, &out.ConfigMap + *out = new(ServerGroupSpecVolumeConfigMap) + (*in).DeepCopyInto(*out) + } + if in.EmptyDir != nil { + in, out := &in.EmptyDir, &out.EmptyDir + *out = new(ServerGroupSpecVolumeEmptyDir) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerGroupSpecVolume. +func (in *ServerGroupSpecVolume) DeepCopy() *ServerGroupSpecVolume { + if in == nil { + return nil + } + out := new(ServerGroupSpecVolume) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerGroupSpecVolumeConfigMap) DeepCopyInto(out *ServerGroupSpecVolumeConfigMap) { + *out = *in + out.LocalObjectReference = in.LocalObjectReference + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1.KeyToPath, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.DefaultMode != nil { + in, out := &in.DefaultMode, &out.DefaultMode + *out = new(int32) + **out = **in + } + if in.Optional != nil { + in, out := &in.Optional, &out.Optional + *out = new(bool) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerGroupSpecVolumeConfigMap. +func (in *ServerGroupSpecVolumeConfigMap) DeepCopy() *ServerGroupSpecVolumeConfigMap { + if in == nil { + return nil + } + out := new(ServerGroupSpecVolumeConfigMap) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerGroupSpecVolumeEmptyDir) DeepCopyInto(out *ServerGroupSpecVolumeEmptyDir) { + *out = *in + if in.SizeLimit != nil { + in, out := &in.SizeLimit, &out.SizeLimit + x := (*in).DeepCopy() + *out = &x + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerGroupSpecVolumeEmptyDir. +func (in *ServerGroupSpecVolumeEmptyDir) DeepCopy() *ServerGroupSpecVolumeEmptyDir { + if in == nil { + return nil + } + out := new(ServerGroupSpecVolumeEmptyDir) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerGroupSpecVolumeMount) DeepCopyInto(out *ServerGroupSpecVolumeMount) { + *out = *in + if in.MountPropagation != nil { + in, out := &in.MountPropagation, &out.MountPropagation + *out = new(v1.MountPropagationMode) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerGroupSpecVolumeMount. +func (in *ServerGroupSpecVolumeMount) DeepCopy() *ServerGroupSpecVolumeMount { + if in == nil { + return nil + } + out := new(ServerGroupSpecVolumeMount) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in ServerGroupSpecVolumeMounts) DeepCopyInto(out *ServerGroupSpecVolumeMounts) { + { + in := &in + *out = make(ServerGroupSpecVolumeMounts, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerGroupSpecVolumeMounts. +func (in ServerGroupSpecVolumeMounts) DeepCopy() ServerGroupSpecVolumeMounts { + if in == nil { + return nil + } + out := new(ServerGroupSpecVolumeMounts) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerGroupSpecVolumeSecret) DeepCopyInto(out *ServerGroupSpecVolumeSecret) { + *out = *in + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1.KeyToPath, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.DefaultMode != nil { + in, out := &in.DefaultMode, &out.DefaultMode + *out = new(int32) + **out = **in + } + if in.Optional != nil { + in, out := &in.Optional, &out.Optional + *out = new(bool) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerGroupSpecVolumeSecret. +func (in *ServerGroupSpecVolumeSecret) DeepCopy() *ServerGroupSpecVolumeSecret { + if in == nil { + return nil + } + out := new(ServerGroupSpecVolumeSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in ServerGroupSpecVolumes) DeepCopyInto(out *ServerGroupSpecVolumes) { + { + in := &in + *out = make(ServerGroupSpecVolumes, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerGroupSpecVolumes. +func (in ServerGroupSpecVolumes) DeepCopy() ServerGroupSpecVolumes { + if in == nil { + return nil + } + out := new(ServerGroupSpecVolumes) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerIDGroupSpec) DeepCopyInto(out *ServerIDGroupSpec) { + *out = *in + if in.Entrypoint != nil { + in, out := &in.Entrypoint, &out.Entrypoint + *out = new(string) + **out = **in + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.AntiAffinity != nil { + in, out := &in.AntiAffinity, &out.AntiAffinity + *out = new(v1.PodAntiAffinity) + (*in).DeepCopyInto(*out) + } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(v1.PodAffinity) + (*in).DeepCopyInto(*out) + } + if in.NodeAffinity != nil { + in, out := &in.NodeAffinity, &out.NodeAffinity + *out = new(v1.NodeAffinity) + (*in).DeepCopyInto(*out) + } + if in.ServiceAccountName != nil { + in, out := &in.ServiceAccountName, &out.ServiceAccountName + *out = new(string) + **out = **in + } + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(ServerGroupSpecSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(v1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerIDGroupSpec. +func (in *ServerIDGroupSpec) DeepCopy() *ServerIDGroupSpec { + if in == nil { + return nil + } + out := new(ServerIDGroupSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SyncAuthenticationSpec) DeepCopyInto(out *SyncAuthenticationSpec) { + *out = *in + if in.JWTSecretName != nil { + in, out := &in.JWTSecretName, &out.JWTSecretName + *out = new(string) + **out = **in + } + if in.ClientCASecretName != nil { + in, out := &in.ClientCASecretName, &out.ClientCASecretName + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SyncAuthenticationSpec. +func (in *SyncAuthenticationSpec) DeepCopy() *SyncAuthenticationSpec { + if in == nil { + return nil + } + out := new(SyncAuthenticationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SyncExternalAccessSpec) DeepCopyInto(out *SyncExternalAccessSpec) { + *out = *in + in.ExternalAccessSpec.DeepCopyInto(&out.ExternalAccessSpec) + if in.MasterEndpoint != nil { + in, out := &in.MasterEndpoint, &out.MasterEndpoint + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.AccessPackageSecretNames != nil { + in, out := &in.AccessPackageSecretNames, &out.AccessPackageSecretNames + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SyncExternalAccessSpec. +func (in *SyncExternalAccessSpec) DeepCopy() *SyncExternalAccessSpec { + if in == nil { + return nil + } + out := new(SyncExternalAccessSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SyncSpec) DeepCopyInto(out *SyncSpec) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + in.ExternalAccess.DeepCopyInto(&out.ExternalAccess) + in.Authentication.DeepCopyInto(&out.Authentication) + in.TLS.DeepCopyInto(&out.TLS) + in.Monitoring.DeepCopyInto(&out.Monitoring) + if in.Image != nil { + in, out := &in.Image, &out.Image + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SyncSpec. +func (in *SyncSpec) DeepCopy() *SyncSpec { + if in == nil { + return nil + } + out := new(SyncSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSSNISpec) DeepCopyInto(out *TLSSNISpec) { + *out = *in + if in.Mapping != nil { + in, out := &in.Mapping, &out.Mapping + *out = make(map[string][]string, len(*in)) + for key, val := range *in { + var outVal []string + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = make([]string, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSSNISpec. +func (in *TLSSNISpec) DeepCopy() *TLSSNISpec { + if in == nil { + return nil + } + out := new(TLSSNISpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSSpec) DeepCopyInto(out *TLSSpec) { + *out = *in + if in.CASecretName != nil { + in, out := &in.CASecretName, &out.CASecretName + *out = new(string) + **out = **in + } + if in.AltNames != nil { + in, out := &in.AltNames, &out.AltNames + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.TTL != nil { + in, out := &in.TTL, &out.TTL + *out = new(Duration) + **out = **in + } + if in.SNI != nil { + in, out := &in.SNI, &out.SNI + *out = new(TLSSNISpec) + (*in).DeepCopyInto(*out) + } + if in.Mode != nil { + in, out := &in.Mode, &out.Mode + *out = new(TLSRotateMode) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSSpec. +func (in *TLSSpec) DeepCopy() *TLSSpec { + if in == nil { + return nil + } + out := new(TLSSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Timeout) DeepCopyInto(out *Timeout) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Timeout. +func (in *Timeout) DeepCopy() *Timeout { + if in == nil { + return nil + } + out := new(Timeout) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Timeouts) DeepCopyInto(out *Timeouts) { + *out = *in + if in.AddMember != nil { + in, out := &in.AddMember, &out.AddMember + *out = new(Timeout) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Timeouts. +func (in *Timeouts) DeepCopy() *Timeouts { + if in == nil { + return nil + } + out := new(Timeouts) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/replication/v2alpha1/collection_status.go b/pkg/apis/replication/v2alpha1/collection_status.go new file mode 100644 index 000000000..1186c211e --- /dev/null +++ b/pkg/apis/replication/v2alpha1/collection_status.go @@ -0,0 +1,32 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +// CollectionStatus contains the status of a single collection. +type CollectionStatus struct { + // Name of the collection + Name string `json:"name"` + // Replication status per shard. + // The list is ordered by shard index (0..noShards-1) + Shards []ShardStatus `json:"shards,omitempty"` +} diff --git a/pkg/apis/replication/v2alpha1/conditions.go b/pkg/apis/replication/v2alpha1/conditions.go new file mode 100644 index 000000000..951d2c442 --- /dev/null +++ b/pkg/apis/replication/v2alpha1/conditions.go @@ -0,0 +1,131 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ConditionType is a strongly typed condition name +type ConditionType string + +const ( + // ConditionTypeConfigured indicates that the replication has been configured. + ConditionTypeConfigured ConditionType = "Configured" +) + +// Condition represents one current condition of a deployment or deployment member. +// A condition might not show up if it is not happening. +// For example, if a cluster is not upgrading, the Upgrading condition would not show up. +type Condition struct { + // Type of condition. + Type ConditionType `json:"type"` + // Status of the condition, one of True, False, Unknown. + Status v1.ConditionStatus `json:"status"` + // The last time this condition was updated. + LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"` + // Last time the condition transitioned from one status to another. + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + // The reason for the condition's last transition. + Reason string `json:"reason,omitempty"` + // A human readable message indicating details about the transition. + Message string `json:"message,omitempty"` +} + +// ConditionList is a list of conditions. +// Each type is allowed only once. +type ConditionList []Condition + +// IsTrue return true when a condition with given type exists and its status is `True`. +func (list ConditionList) IsTrue(conditionType ConditionType) bool { + c, found := list.Get(conditionType) + return found && c.Status == v1.ConditionTrue +} + +// Get a condition by type. +// Returns true if found, false if not found. +func (list ConditionList) Get(conditionType ConditionType) (Condition, bool) { + for _, x := range list { + if x.Type == conditionType { + return x, true + } + } + // Not found + return Condition{}, false +} + +// Update the condition, replacing an old condition with same type (if any) +// Returns true when changes were made, false otherwise. +func (list *ConditionList) Update(conditionType ConditionType, status bool, reason, message string) bool { + src := *list + statusX := v1.ConditionFalse + if status { + statusX = v1.ConditionTrue + } + for i, x := range src { + if x.Type == conditionType { + if x.Status != statusX { + // Transition to another status + src[i].Status = statusX + now := metav1.Now() + src[i].LastTransitionTime = now + src[i].LastUpdateTime = now + src[i].Reason = reason + src[i].Message = message + } else if x.Reason != reason || x.Message != message { + src[i].LastUpdateTime = metav1.Now() + src[i].Reason = reason + src[i].Message = message + } else { + return false + } + return true + } + } + // Not found + now := metav1.Now() + *list = append(src, Condition{ + Type: conditionType, + LastUpdateTime: now, + LastTransitionTime: now, + Status: statusX, + Reason: reason, + Message: message, + }) + return true +} + +// Remove the condition with given type. +// Returns true if removed, or false if not found. +func (list *ConditionList) Remove(conditionType ConditionType) bool { + src := *list + for i, x := range src { + if x.Type == conditionType { + *list = append(src[:i], src[i+1:]...) + return true + } + } + // Not found + return false +} diff --git a/pkg/apis/replication/v2alpha1/conditions_test.go b/pkg/apis/replication/v2alpha1/conditions_test.go new file mode 100644 index 000000000..a6e9220be --- /dev/null +++ b/pkg/apis/replication/v2alpha1/conditions_test.go @@ -0,0 +1,95 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConditionListIsTrue(t *testing.T) { + assert.False(t, ConditionList{}.IsTrue(ConditionTypeConfigured)) + + cl := ConditionList{} + cl.Update(ConditionTypeConfigured, true, "test", "msg") + assert.True(t, cl.IsTrue(ConditionTypeConfigured)) + //assert.False(t, cl.IsTrue(ConditionTypeTerminated)) + + cl.Update(ConditionTypeConfigured, false, "test", "msg") + assert.False(t, cl.IsTrue(ConditionTypeConfigured)) + + cl.Remove(ConditionTypeConfigured) + assert.False(t, cl.IsTrue(ConditionTypeConfigured)) + assert.Equal(t, 0, len(cl)) +} + +func TestConditionListGet(t *testing.T) { + conv := func(c Condition, b bool) []interface{} { + return []interface{}{c, b} + } + + cl := ConditionList{} + assert.EqualValues(t, conv(Condition{}, false), conv(cl.Get(ConditionTypeConfigured))) + cl.Update(ConditionTypeConfigured, false, "test", "msg") + assert.EqualValues(t, conv(cl[0], true), conv(cl.Get(ConditionTypeConfigured))) +} + +func TestConditionListUpdate(t *testing.T) { + cl := ConditionList{} + assert.Equal(t, 0, len(cl)) + + assert.True(t, cl.Update(ConditionTypeConfigured, true, "test", "msg")) + assert.True(t, cl.IsTrue(ConditionTypeConfigured)) + assert.Equal(t, 1, len(cl)) + + assert.False(t, cl.Update(ConditionTypeConfigured, true, "test", "msg")) + assert.True(t, cl.IsTrue(ConditionTypeConfigured)) + assert.Equal(t, 1, len(cl)) + + assert.True(t, cl.Update(ConditionTypeConfigured, false, "test", "msg")) + assert.False(t, cl.IsTrue(ConditionTypeConfigured)) + assert.Equal(t, 1, len(cl)) + + assert.True(t, cl.Update(ConditionTypeConfigured, false, "test2", "msg")) + assert.False(t, cl.IsTrue(ConditionTypeConfigured)) + assert.Equal(t, 1, len(cl)) + + assert.True(t, cl.Update(ConditionTypeConfigured, false, "test2", "msg2")) + assert.False(t, cl.IsTrue(ConditionTypeConfigured)) + assert.Equal(t, 1, len(cl)) +} + +func TestConditionListRemove(t *testing.T) { + cl := ConditionList{} + assert.Equal(t, 0, len(cl)) + + cl.Update(ConditionTypeConfigured, true, "test", "msg") + assert.Equal(t, 1, len(cl)) + + assert.True(t, cl.Remove(ConditionTypeConfigured)) + assert.Equal(t, 0, len(cl)) + + assert.False(t, cl.Remove(ConditionTypeConfigured)) + assert.Equal(t, 0, len(cl)) +} diff --git a/pkg/apis/replication/v2alpha1/database_status.go b/pkg/apis/replication/v2alpha1/database_status.go new file mode 100644 index 000000000..794907faa --- /dev/null +++ b/pkg/apis/replication/v2alpha1/database_status.go @@ -0,0 +1,32 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +// DatabaseStatus contains the status of a single database. +type DatabaseStatus struct { + // Name of the database + Name string `json:"name"` + // Collections holds the replication status of each collection in the database. + // List is ordered by name of the collection. + Collections []CollectionStatus `json:"collections,omitempty"` +} diff --git a/pkg/apis/replication/v2alpha1/doc.go b/pkg/apis/replication/v2alpha1/doc.go new file mode 100644 index 000000000..505421f86 --- /dev/null +++ b/pkg/apis/replication/v2alpha1/doc.go @@ -0,0 +1,25 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// +k8s:deepcopy-gen=package +// +groupName=replication.database.arangodb.com +package v2alpha1 diff --git a/pkg/apis/replication/v2alpha1/endpoint_authentication_spec.go b/pkg/apis/replication/v2alpha1/endpoint_authentication_spec.go new file mode 100644 index 000000000..f6c3cb148 --- /dev/null +++ b/pkg/apis/replication/v2alpha1/endpoint_authentication_spec.go @@ -0,0 +1,89 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" + "github.com/pkg/errors" +) + +// EndpointAuthenticationSpec contains the specification to authentication with the syncmasters +// in either source or destination endpoint. +type EndpointAuthenticationSpec struct { + // KeyfileSecretName holds the name of a Secret containing a client authentication + // certificate formatted at keyfile in a `tls.keyfile` field. + KeyfileSecretName *string `json:"keyfileSecretName,omitempty"` + // UserSecretName holds the name of a Secret containing a `username` & `password` + // field used for basic authentication. + // The user identified by the username must have write access in the `_system` database + // of the ArangoDB cluster at the endpoint. + UserSecretName *string `json:"userSecretName,omitempty"` +} + +// GetKeyfileSecretName returns the value of keyfileSecretName. +func (s EndpointAuthenticationSpec) GetKeyfileSecretName() string { + return util.StringOrDefault(s.KeyfileSecretName) +} + +// GetUserSecretName returns the value of userSecretName. +func (s EndpointAuthenticationSpec) GetUserSecretName() string { + return util.StringOrDefault(s.UserSecretName) +} + +// Validate the given spec, returning an error on validation +// problems or nil if all ok. +func (s EndpointAuthenticationSpec) Validate(keyfileSecretNameRequired bool) error { + if err := k8sutil.ValidateOptionalResourceName(s.GetKeyfileSecretName()); err != nil { + return maskAny(err) + } + if err := k8sutil.ValidateOptionalResourceName(s.GetUserSecretName()); err != nil { + return maskAny(err) + } + if keyfileSecretNameRequired && s.GetKeyfileSecretName() == "" { + return maskAny(errors.Wrapf(ValidationError, "Provide a keyfileSecretName")) + } + return nil +} + +// SetDefaults fills empty field with default values. +func (s *EndpointAuthenticationSpec) SetDefaults() { +} + +// SetDefaultsFrom fills empty field with default values from the given source. +func (s *EndpointAuthenticationSpec) SetDefaultsFrom(source EndpointAuthenticationSpec) { + if s.KeyfileSecretName == nil { + s.KeyfileSecretName = util.NewStringOrNil(source.KeyfileSecretName) + } + if s.UserSecretName == nil { + s.UserSecretName = util.NewStringOrNil(source.UserSecretName) + } +} + +// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. +// It returns a list of fields that have been reset. +// Field names are relative to `spec.`. +func (s EndpointAuthenticationSpec) ResetImmutableFields(target *EndpointAuthenticationSpec, fieldPrefix string) []string { + var result []string + return result +} diff --git a/pkg/apis/replication/v2alpha1/endpoint_spec.go b/pkg/apis/replication/v2alpha1/endpoint_spec.go new file mode 100644 index 000000000..2428f9d97 --- /dev/null +++ b/pkg/apis/replication/v2alpha1/endpoint_spec.go @@ -0,0 +1,111 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "net/url" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" + "github.com/pkg/errors" +) + +// EndpointSpec contains the specification used to reach the syncmasters +// in either source or destination mode. +type EndpointSpec struct { + // DeploymentName holds the name of an ArangoDeployment resource. + // If set this provides default values for masterEndpoint, auth & tls. + DeploymentName *string `json:"deploymentName,omitempty"` + // MasterEndpoint holds a list of URLs used to reach the syncmaster(s). + MasterEndpoint []string `json:"masterEndpoint,omitempty"` + // Authentication holds settings needed to authentication at the syncmaster. + Authentication EndpointAuthenticationSpec `json:"auth"` + // TLS holds settings needed to verify the TLS connection to the syncmaster. + TLS EndpointTLSSpec `json:"tls"` +} + +// GetDeploymentName returns the value of deploymentName. +func (s EndpointSpec) GetDeploymentName() string { + return util.StringOrDefault(s.DeploymentName) +} + +// HasDeploymentName returns the true when a non-empty deployment name it set. +func (s EndpointSpec) HasDeploymentName() bool { + return s.GetDeploymentName() != "" +} + +// Validate the given spec, returning an error on validation +// problems or nil if all ok. +func (s EndpointSpec) Validate(isSourceEndpoint bool) error { + if err := k8sutil.ValidateOptionalResourceName(s.GetDeploymentName()); err != nil { + return maskAny(err) + } + for _, ep := range s.MasterEndpoint { + if _, err := url.Parse(ep); err != nil { + return maskAny(errors.Wrapf(ValidationError, "Invalid master endpoint '%s': %s", ep, err)) + } + } + hasDeploymentName := s.HasDeploymentName() + if !hasDeploymentName && len(s.MasterEndpoint) == 0 { + return maskAny(errors.Wrapf(ValidationError, "Provide a deploy name or at least one master endpoint")) + } + if err := s.Authentication.Validate(isSourceEndpoint || !hasDeploymentName); err != nil { + return maskAny(err) + } + if err := s.TLS.Validate(!hasDeploymentName); err != nil { + return maskAny(err) + } + return nil +} + +// SetDefaults fills empty field with default values. +func (s *EndpointSpec) SetDefaults() { + s.Authentication.SetDefaults() + s.TLS.SetDefaults() +} + +// SetDefaultsFrom fills empty field with default values from the given source. +func (s *EndpointSpec) SetDefaultsFrom(source EndpointSpec) { + if s.DeploymentName == nil { + s.DeploymentName = util.NewStringOrNil(source.DeploymentName) + } + s.Authentication.SetDefaultsFrom(source.Authentication) + s.TLS.SetDefaultsFrom(source.TLS) +} + +// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. +// It returns a list of fields that have been reset. +// Field names are relative to `spec.`. +func (s EndpointSpec) ResetImmutableFields(target *EndpointSpec, fieldPrefix string) []string { + var result []string + if s.GetDeploymentName() != target.GetDeploymentName() { + result = append(result, fieldPrefix+"deploymentName") + } + if list := s.Authentication.ResetImmutableFields(&target.Authentication, fieldPrefix+"auth."); len(list) > 0 { + result = append(result, list...) + } + if list := s.TLS.ResetImmutableFields(&target.TLS, fieldPrefix+"tls."); len(list) > 0 { + result = append(result, list...) + } + return result +} diff --git a/pkg/apis/replication/v2alpha1/endpoint_status.go b/pkg/apis/replication/v2alpha1/endpoint_status.go new file mode 100644 index 000000000..8b95cfeb8 --- /dev/null +++ b/pkg/apis/replication/v2alpha1/endpoint_status.go @@ -0,0 +1,30 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +// EndpointStatus contains the status of either the source or destination endpoint. +type EndpointStatus struct { + // Databases holds the replication status of all databases from the point of view of this endpoint. + // List is ordered by name of the database. + Databases []DatabaseStatus `json:"databases,omitempty"` +} diff --git a/pkg/apis/replication/v2alpha1/endpoint_tls_spec.go b/pkg/apis/replication/v2alpha1/endpoint_tls_spec.go new file mode 100644 index 000000000..3b5514dfa --- /dev/null +++ b/pkg/apis/replication/v2alpha1/endpoint_tls_spec.go @@ -0,0 +1,72 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" + "github.com/pkg/errors" +) + +// EndpointTLSSpec contains the specification regarding the TLS connection to the syncmasters +// in either source or destination endpoint. +type EndpointTLSSpec struct { + // CASecretName holds the name of a Secret containing a ca.crt public key for TLS validation. + CASecretName *string `json:"caSecretName,omitempty"` +} + +// GetCASecretName returns the value of caSecretName. +func (s EndpointTLSSpec) GetCASecretName() string { + return util.StringOrDefault(s.CASecretName) +} + +// Validate the given spec, returning an error on validation +// problems or nil if all ok. +func (s EndpointTLSSpec) Validate(caSecretNameRequired bool) error { + if err := k8sutil.ValidateOptionalResourceName(s.GetCASecretName()); err != nil { + return maskAny(err) + } + if caSecretNameRequired && s.GetCASecretName() == "" { + return maskAny(errors.Wrapf(ValidationError, "Provide a caSecretName")) + } + return nil +} + +// SetDefaults fills empty field with default values. +func (s *EndpointTLSSpec) SetDefaults() { +} + +// SetDefaultsFrom fills empty field with default values from the given source. +func (s *EndpointTLSSpec) SetDefaultsFrom(source EndpointTLSSpec) { + if s.CASecretName == nil { + s.CASecretName = util.NewStringOrNil(source.CASecretName) + } +} + +// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. +// It returns a list of fields that have been reset. +// Field names are relative to `spec.`. +func (s EndpointTLSSpec) ResetImmutableFields(target *EndpointTLSSpec, fieldPrefix string) []string { + var result []string + return result +} diff --git a/pkg/apis/replication/v2alpha1/errors.go b/pkg/apis/replication/v2alpha1/errors.go new file mode 100644 index 000000000..c82dd8a56 --- /dev/null +++ b/pkg/apis/replication/v2alpha1/errors.go @@ -0,0 +1,37 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import "github.com/pkg/errors" + +var ( + // ValidationError indicates a validation failure + ValidationError = errors.New("validation failed") + + maskAny = errors.WithStack +) + +// IsValidation return true when the given error is or is caused by a ValidationError. +func IsValidation(err error) bool { + return errors.Cause(err) == ValidationError +} diff --git a/pkg/apis/replication/v2alpha1/register.go b/pkg/apis/replication/v2alpha1/register.go new file mode 100644 index 000000000..9babf1149 --- /dev/null +++ b/pkg/apis/replication/v2alpha1/register.go @@ -0,0 +1,57 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/arangodb/kube-arangodb/pkg/apis/replication" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +const ( + ArangoDeploymentReplicationVersion = "v2alpha1" +) + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme + + SchemeGroupVersion = schema.GroupVersion{Group: replication.ArangoDeploymentReplicationGroupName, + Version: ArangoDeploymentReplicationVersion} +) + +// Resource gets an ArangoCluster GroupResource for a specified resource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +// addKnownTypes adds the set of types defined in this package to the supplied scheme. +func addKnownTypes(s *runtime.Scheme) error { + s.AddKnownTypes(SchemeGroupVersion, + &ArangoDeploymentReplication{}, + &ArangoDeploymentReplicationList{}, + ) + metav1.AddToGroupVersion(s, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/replication/v2alpha1/replication.go b/pkg/apis/replication/v2alpha1/replication.go new file mode 100644 index 000000000..2cee73fb4 --- /dev/null +++ b/pkg/apis/replication/v2alpha1/replication.go @@ -0,0 +1,64 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +import ( + "github.com/arangodb/kube-arangodb/pkg/apis/replication" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ArangoDeploymentReplicationList is a list of ArangoDB deployment replications. +type ArangoDeploymentReplicationList struct { + metav1.TypeMeta `json:",inline"` + // Standard list metadata + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + metav1.ListMeta `json:"metadata,omitempty"` + Items []ArangoDeploymentReplication `json:"items"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ArangoDeploymentReplication contains the entire Kubernetes info for an ArangoDB +// local storage provider. +type ArangoDeploymentReplication struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec DeploymentReplicationSpec `json:"spec"` + Status DeploymentReplicationStatus `json:"status"` +} + +// AsOwner creates an OwnerReference for the given replication +func (d *ArangoDeploymentReplication) AsOwner() metav1.OwnerReference { + trueVar := true + return metav1.OwnerReference{ + APIVersion: SchemeGroupVersion.String(), + Kind: replication.ArangoDeploymentReplicationResourceKind, + Name: d.Name, + UID: d.UID, + Controller: &trueVar, + BlockOwnerDeletion: &trueVar, + } +} diff --git a/pkg/apis/replication/v2alpha1/replication_phase.go b/pkg/apis/replication/v2alpha1/replication_phase.go new file mode 100644 index 000000000..12855f186 --- /dev/null +++ b/pkg/apis/replication/v2alpha1/replication_phase.go @@ -0,0 +1,39 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +// DeploymentReplicationPhase is a strongly typed lifetime phase of a deployment replication +type DeploymentReplicationPhase string + +const ( + // DeploymentReplicationPhaseNone indicates that the phase is not set yet + DeploymentReplicationPhaseNone DeploymentReplicationPhase = "" + // DeploymentReplicationPhaseFailed indicates that a deployment replication is in a failed state + // from which automatic recovery is impossible. Inspect `Reason` for more info. + DeploymentReplicationPhaseFailed DeploymentReplicationPhase = "Failed" +) + +// IsFailed returns true if given state is DeploymentStateFailed +func (cs DeploymentReplicationPhase) IsFailed() bool { + return cs == DeploymentReplicationPhaseFailed +} diff --git a/pkg/apis/replication/v2alpha1/replication_spec.go b/pkg/apis/replication/v2alpha1/replication_spec.go new file mode 100644 index 000000000..b7f53d9fc --- /dev/null +++ b/pkg/apis/replication/v2alpha1/replication_spec.go @@ -0,0 +1,68 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +// DeploymentReplicationSpec contains the specification part of +// an ArangoDeploymentReplication. +type DeploymentReplicationSpec struct { + Source EndpointSpec `json:"source"` + Destination EndpointSpec `json:"destination"` +} + +// Validate the given spec, returning an error on validation +// problems or nil if all ok. +func (s DeploymentReplicationSpec) Validate() error { + if err := s.Source.Validate(true); err != nil { + return maskAny(err) + } + if err := s.Destination.Validate(false); err != nil { + return maskAny(err) + } + return nil +} + +// SetDefaults fills empty field with default values. +func (s *DeploymentReplicationSpec) SetDefaults() { + s.Source.SetDefaults() + s.Destination.SetDefaults() +} + +// SetDefaultsFrom fills empty field with default values from the given source. +func (s *DeploymentReplicationSpec) SetDefaultsFrom(source DeploymentReplicationSpec) { + s.Source.SetDefaultsFrom(source.Source) + s.Destination.SetDefaultsFrom(source.Destination) +} + +// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec. +// It returns a list of fields that have been reset. +// Field names are relative to `spec.`. +func (s DeploymentReplicationSpec) ResetImmutableFields(target *DeploymentReplicationSpec) []string { + var result []string + if list := s.Source.ResetImmutableFields(&target.Source, "source."); len(list) > 0 { + result = append(result, list...) + } + if list := s.Destination.ResetImmutableFields(&target.Destination, "destination."); len(list) > 0 { + result = append(result, list...) + } + return result +} diff --git a/pkg/apis/replication/v2alpha1/replication_status.go b/pkg/apis/replication/v2alpha1/replication_status.go new file mode 100644 index 000000000..2f0fa4a35 --- /dev/null +++ b/pkg/apis/replication/v2alpha1/replication_status.go @@ -0,0 +1,44 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +// DeploymentReplicationStatus contains the status part of +// an ArangoDeploymentReplication. +type DeploymentReplicationStatus struct { + // Phase holds the current lifetime phase of the deployment replication + Phase DeploymentReplicationPhase `json:"phase,omitempty"` + // Reason contains a human readable reason for reaching the current phase (can be empty) + Reason string `json:"reason,omitempty"` // Reason for current phase + + // Conditions specific to the entire deployment replication + Conditions ConditionList `json:"conditions,omitempty"` + + // Source contains the detailed status of the source endpoint + Source EndpointStatus `json:"source"` + // Destination contains the detailed status of the destination endpoint + Destination EndpointStatus `json:"destination"` + + // CancelFailures records the number of times that the configuration was canceled + // which resulted in an error. + CancelFailures int `json:"cancel-failures,omitempty"` +} diff --git a/pkg/apis/replication/v2alpha1/shard_status.go b/pkg/apis/replication/v2alpha1/shard_status.go new file mode 100644 index 000000000..f607797a5 --- /dev/null +++ b/pkg/apis/replication/v2alpha1/shard_status.go @@ -0,0 +1,28 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 v2alpha1 + +// ShardStatus contains the status of a single shard. +type ShardStatus struct { + Status string `json:"status"` +} diff --git a/pkg/apis/replication/v2alpha1/zz_generated.deepcopy.go b/pkg/apis/replication/v2alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..d83b73de2 --- /dev/null +++ b/pkg/apis/replication/v2alpha1/zz_generated.deepcopy.go @@ -0,0 +1,331 @@ +// +build !ignore_autogenerated + +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ArangoDeploymentReplication) DeepCopyInto(out *ArangoDeploymentReplication) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoDeploymentReplication. +func (in *ArangoDeploymentReplication) DeepCopy() *ArangoDeploymentReplication { + if in == nil { + return nil + } + out := new(ArangoDeploymentReplication) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ArangoDeploymentReplication) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ArangoDeploymentReplicationList) DeepCopyInto(out *ArangoDeploymentReplicationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ArangoDeploymentReplication, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoDeploymentReplicationList. +func (in *ArangoDeploymentReplicationList) DeepCopy() *ArangoDeploymentReplicationList { + if in == nil { + return nil + } + out := new(ArangoDeploymentReplicationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ArangoDeploymentReplicationList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CollectionStatus) DeepCopyInto(out *CollectionStatus) { + *out = *in + if in.Shards != nil { + in, out := &in.Shards, &out.Shards + *out = make([]ShardStatus, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CollectionStatus. +func (in *CollectionStatus) DeepCopy() *CollectionStatus { + if in == nil { + return nil + } + out := new(CollectionStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Condition) DeepCopyInto(out *Condition) { + *out = *in + in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. +func (in *Condition) DeepCopy() *Condition { + if in == nil { + return nil + } + out := new(Condition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in ConditionList) DeepCopyInto(out *ConditionList) { + { + in := &in + *out = make(ConditionList, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConditionList. +func (in ConditionList) DeepCopy() ConditionList { + if in == nil { + return nil + } + out := new(ConditionList) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseStatus) DeepCopyInto(out *DatabaseStatus) { + *out = *in + if in.Collections != nil { + in, out := &in.Collections, &out.Collections + *out = make([]CollectionStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseStatus. +func (in *DatabaseStatus) DeepCopy() *DatabaseStatus { + if in == nil { + return nil + } + out := new(DatabaseStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentReplicationSpec) DeepCopyInto(out *DeploymentReplicationSpec) { + *out = *in + in.Source.DeepCopyInto(&out.Source) + in.Destination.DeepCopyInto(&out.Destination) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentReplicationSpec. +func (in *DeploymentReplicationSpec) DeepCopy() *DeploymentReplicationSpec { + if in == nil { + return nil + } + out := new(DeploymentReplicationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentReplicationStatus) DeepCopyInto(out *DeploymentReplicationStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(ConditionList, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Source.DeepCopyInto(&out.Source) + in.Destination.DeepCopyInto(&out.Destination) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentReplicationStatus. +func (in *DeploymentReplicationStatus) DeepCopy() *DeploymentReplicationStatus { + if in == nil { + return nil + } + out := new(DeploymentReplicationStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EndpointAuthenticationSpec) DeepCopyInto(out *EndpointAuthenticationSpec) { + *out = *in + if in.KeyfileSecretName != nil { + in, out := &in.KeyfileSecretName, &out.KeyfileSecretName + *out = new(string) + **out = **in + } + if in.UserSecretName != nil { + in, out := &in.UserSecretName, &out.UserSecretName + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EndpointAuthenticationSpec. +func (in *EndpointAuthenticationSpec) DeepCopy() *EndpointAuthenticationSpec { + if in == nil { + return nil + } + out := new(EndpointAuthenticationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EndpointSpec) DeepCopyInto(out *EndpointSpec) { + *out = *in + if in.DeploymentName != nil { + in, out := &in.DeploymentName, &out.DeploymentName + *out = new(string) + **out = **in + } + if in.MasterEndpoint != nil { + in, out := &in.MasterEndpoint, &out.MasterEndpoint + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.Authentication.DeepCopyInto(&out.Authentication) + in.TLS.DeepCopyInto(&out.TLS) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EndpointSpec. +func (in *EndpointSpec) DeepCopy() *EndpointSpec { + if in == nil { + return nil + } + out := new(EndpointSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EndpointStatus) DeepCopyInto(out *EndpointStatus) { + *out = *in + if in.Databases != nil { + in, out := &in.Databases, &out.Databases + *out = make([]DatabaseStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EndpointStatus. +func (in *EndpointStatus) DeepCopy() *EndpointStatus { + if in == nil { + return nil + } + out := new(EndpointStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EndpointTLSSpec) DeepCopyInto(out *EndpointTLSSpec) { + *out = *in + if in.CASecretName != nil { + in, out := &in.CASecretName, &out.CASecretName + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EndpointTLSSpec. +func (in *EndpointTLSSpec) DeepCopy() *EndpointTLSSpec { + if in == nil { + return nil + } + out := new(EndpointTLSSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShardStatus) DeepCopyInto(out *ShardStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShardStatus. +func (in *ShardStatus) DeepCopy() *ShardStatus { + if in == nil { + return nil + } + out := new(ShardStatus) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/generated/clientset/versioned/clientset.go b/pkg/generated/clientset/versioned/clientset.go index c05244250..9e0c8c550 100644 --- a/pkg/generated/clientset/versioned/clientset.go +++ b/pkg/generated/clientset/versioned/clientset.go @@ -27,7 +27,9 @@ import ( backupv1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/backup/v1" databasev1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/deployment/v1" + databasev2alpha1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/deployment/v2alpha1" replicationv1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/replication/v1" + replicationv2alpha1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/replication/v2alpha1" storagev1alpha "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/storage/v1alpha" discovery "k8s.io/client-go/discovery" rest "k8s.io/client-go/rest" @@ -38,7 +40,9 @@ type Interface interface { Discovery() discovery.DiscoveryInterface BackupV1() backupv1.BackupV1Interface DatabaseV1() databasev1.DatabaseV1Interface + DatabaseV2alpha1() databasev2alpha1.DatabaseV2alpha1Interface ReplicationV1() replicationv1.ReplicationV1Interface + ReplicationV2alpha1() replicationv2alpha1.ReplicationV2alpha1Interface StorageV1alpha() storagev1alpha.StorageV1alphaInterface } @@ -46,10 +50,12 @@ type Interface interface { // version included in a Clientset. type Clientset struct { *discovery.DiscoveryClient - backupV1 *backupv1.BackupV1Client - databaseV1 *databasev1.DatabaseV1Client - replicationV1 *replicationv1.ReplicationV1Client - storageV1alpha *storagev1alpha.StorageV1alphaClient + backupV1 *backupv1.BackupV1Client + databaseV1 *databasev1.DatabaseV1Client + databaseV2alpha1 *databasev2alpha1.DatabaseV2alpha1Client + replicationV1 *replicationv1.ReplicationV1Client + replicationV2alpha1 *replicationv2alpha1.ReplicationV2alpha1Client + storageV1alpha *storagev1alpha.StorageV1alphaClient } // BackupV1 retrieves the BackupV1Client @@ -62,11 +68,21 @@ func (c *Clientset) DatabaseV1() databasev1.DatabaseV1Interface { return c.databaseV1 } +// DatabaseV2alpha1 retrieves the DatabaseV2alpha1Client +func (c *Clientset) DatabaseV2alpha1() databasev2alpha1.DatabaseV2alpha1Interface { + return c.databaseV2alpha1 +} + // ReplicationV1 retrieves the ReplicationV1Client func (c *Clientset) ReplicationV1() replicationv1.ReplicationV1Interface { return c.replicationV1 } +// ReplicationV2alpha1 retrieves the ReplicationV2alpha1Client +func (c *Clientset) ReplicationV2alpha1() replicationv2alpha1.ReplicationV2alpha1Interface { + return c.replicationV2alpha1 +} + // StorageV1alpha retrieves the StorageV1alphaClient func (c *Clientset) StorageV1alpha() storagev1alpha.StorageV1alphaInterface { return c.storageV1alpha @@ -101,10 +117,18 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { if err != nil { return nil, err } + cs.databaseV2alpha1, err = databasev2alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } cs.replicationV1, err = replicationv1.NewForConfig(&configShallowCopy) if err != nil { return nil, err } + cs.replicationV2alpha1, err = replicationv2alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } cs.storageV1alpha, err = storagev1alpha.NewForConfig(&configShallowCopy) if err != nil { return nil, err @@ -123,7 +147,9 @@ func NewForConfigOrDie(c *rest.Config) *Clientset { var cs Clientset cs.backupV1 = backupv1.NewForConfigOrDie(c) cs.databaseV1 = databasev1.NewForConfigOrDie(c) + cs.databaseV2alpha1 = databasev2alpha1.NewForConfigOrDie(c) cs.replicationV1 = replicationv1.NewForConfigOrDie(c) + cs.replicationV2alpha1 = replicationv2alpha1.NewForConfigOrDie(c) cs.storageV1alpha = storagev1alpha.NewForConfigOrDie(c) cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) @@ -135,7 +161,9 @@ func New(c rest.Interface) *Clientset { var cs Clientset cs.backupV1 = backupv1.New(c) cs.databaseV1 = databasev1.New(c) + cs.databaseV2alpha1 = databasev2alpha1.New(c) cs.replicationV1 = replicationv1.New(c) + cs.replicationV2alpha1 = replicationv2alpha1.New(c) cs.storageV1alpha = storagev1alpha.New(c) cs.DiscoveryClient = discovery.NewDiscoveryClient(c) diff --git a/pkg/generated/clientset/versioned/fake/clientset_generated.go b/pkg/generated/clientset/versioned/fake/clientset_generated.go index e1de65123..f1d504337 100644 --- a/pkg/generated/clientset/versioned/fake/clientset_generated.go +++ b/pkg/generated/clientset/versioned/fake/clientset_generated.go @@ -28,8 +28,12 @@ import ( fakebackupv1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/backup/v1/fake" databasev1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/deployment/v1" fakedatabasev1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/deployment/v1/fake" + databasev2alpha1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/deployment/v2alpha1" + fakedatabasev2alpha1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/fake" replicationv1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/replication/v1" fakereplicationv1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/replication/v1/fake" + replicationv2alpha1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/replication/v2alpha1" + fakereplicationv2alpha1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/replication/v2alpha1/fake" storagev1alpha "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/storage/v1alpha" fakestoragev1alpha "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/storage/v1alpha/fake" "k8s.io/apimachinery/pkg/runtime" @@ -96,11 +100,21 @@ func (c *Clientset) DatabaseV1() databasev1.DatabaseV1Interface { return &fakedatabasev1.FakeDatabaseV1{Fake: &c.Fake} } +// DatabaseV2alpha1 retrieves the DatabaseV2alpha1Client +func (c *Clientset) DatabaseV2alpha1() databasev2alpha1.DatabaseV2alpha1Interface { + return &fakedatabasev2alpha1.FakeDatabaseV2alpha1{Fake: &c.Fake} +} + // ReplicationV1 retrieves the ReplicationV1Client func (c *Clientset) ReplicationV1() replicationv1.ReplicationV1Interface { return &fakereplicationv1.FakeReplicationV1{Fake: &c.Fake} } +// ReplicationV2alpha1 retrieves the ReplicationV2alpha1Client +func (c *Clientset) ReplicationV2alpha1() replicationv2alpha1.ReplicationV2alpha1Interface { + return &fakereplicationv2alpha1.FakeReplicationV2alpha1{Fake: &c.Fake} +} + // StorageV1alpha retrieves the StorageV1alphaClient func (c *Clientset) StorageV1alpha() storagev1alpha.StorageV1alphaInterface { return &fakestoragev1alpha.FakeStorageV1alpha{Fake: &c.Fake} diff --git a/pkg/generated/clientset/versioned/fake/register.go b/pkg/generated/clientset/versioned/fake/register.go index ae2044e55..8ec04ee19 100644 --- a/pkg/generated/clientset/versioned/fake/register.go +++ b/pkg/generated/clientset/versioned/fake/register.go @@ -25,7 +25,9 @@ package fake import ( backupv1 "github.com/arangodb/kube-arangodb/pkg/apis/backup/v1" databasev1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" + databasev2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v2alpha1" replicationv1 "github.com/arangodb/kube-arangodb/pkg/apis/replication/v1" + replicationv2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/replication/v2alpha1" storagev1alpha "github.com/arangodb/kube-arangodb/pkg/apis/storage/v1alpha" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" @@ -40,7 +42,9 @@ var parameterCodec = runtime.NewParameterCodec(scheme) var localSchemeBuilder = runtime.SchemeBuilder{ backupv1.AddToScheme, databasev1.AddToScheme, + databasev2alpha1.AddToScheme, replicationv1.AddToScheme, + replicationv2alpha1.AddToScheme, storagev1alpha.AddToScheme, } diff --git a/pkg/generated/clientset/versioned/scheme/register.go b/pkg/generated/clientset/versioned/scheme/register.go index 1d412bf77..3f4cd9502 100644 --- a/pkg/generated/clientset/versioned/scheme/register.go +++ b/pkg/generated/clientset/versioned/scheme/register.go @@ -25,7 +25,9 @@ package scheme import ( backupv1 "github.com/arangodb/kube-arangodb/pkg/apis/backup/v1" databasev1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" + databasev2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v2alpha1" replicationv1 "github.com/arangodb/kube-arangodb/pkg/apis/replication/v1" + replicationv2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/replication/v2alpha1" storagev1alpha "github.com/arangodb/kube-arangodb/pkg/apis/storage/v1alpha" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" @@ -40,7 +42,9 @@ var ParameterCodec = runtime.NewParameterCodec(Scheme) var localSchemeBuilder = runtime.SchemeBuilder{ backupv1.AddToScheme, databasev1.AddToScheme, + databasev2alpha1.AddToScheme, replicationv1.AddToScheme, + replicationv2alpha1.AddToScheme, storagev1alpha.AddToScheme, } diff --git a/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/arangodeployment.go b/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/arangodeployment.go new file mode 100644 index 000000000..c5c360dee --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/arangodeployment.go @@ -0,0 +1,195 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by client-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + "time" + + v2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v2alpha1" + scheme "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ArangoDeploymentsGetter has a method to return a ArangoDeploymentInterface. +// A group's client should implement this interface. +type ArangoDeploymentsGetter interface { + ArangoDeployments(namespace string) ArangoDeploymentInterface +} + +// ArangoDeploymentInterface has methods to work with ArangoDeployment resources. +type ArangoDeploymentInterface interface { + Create(*v2alpha1.ArangoDeployment) (*v2alpha1.ArangoDeployment, error) + Update(*v2alpha1.ArangoDeployment) (*v2alpha1.ArangoDeployment, error) + UpdateStatus(*v2alpha1.ArangoDeployment) (*v2alpha1.ArangoDeployment, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v2alpha1.ArangoDeployment, error) + List(opts v1.ListOptions) (*v2alpha1.ArangoDeploymentList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v2alpha1.ArangoDeployment, err error) + ArangoDeploymentExpansion +} + +// arangoDeployments implements ArangoDeploymentInterface +type arangoDeployments struct { + client rest.Interface + ns string +} + +// newArangoDeployments returns a ArangoDeployments +func newArangoDeployments(c *DatabaseV2alpha1Client, namespace string) *arangoDeployments { + return &arangoDeployments{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the arangoDeployment, and returns the corresponding arangoDeployment object, and an error if there is any. +func (c *arangoDeployments) Get(name string, options v1.GetOptions) (result *v2alpha1.ArangoDeployment, err error) { + result = &v2alpha1.ArangoDeployment{} + err = c.client.Get(). + Namespace(c.ns). + Resource("arangodeployments"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ArangoDeployments that match those selectors. +func (c *arangoDeployments) List(opts v1.ListOptions) (result *v2alpha1.ArangoDeploymentList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v2alpha1.ArangoDeploymentList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("arangodeployments"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested arangoDeployments. +func (c *arangoDeployments) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("arangodeployments"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a arangoDeployment and creates it. Returns the server's representation of the arangoDeployment, and an error, if there is any. +func (c *arangoDeployments) Create(arangoDeployment *v2alpha1.ArangoDeployment) (result *v2alpha1.ArangoDeployment, err error) { + result = &v2alpha1.ArangoDeployment{} + err = c.client.Post(). + Namespace(c.ns). + Resource("arangodeployments"). + Body(arangoDeployment). + Do(). + Into(result) + return +} + +// Update takes the representation of a arangoDeployment and updates it. Returns the server's representation of the arangoDeployment, and an error, if there is any. +func (c *arangoDeployments) Update(arangoDeployment *v2alpha1.ArangoDeployment) (result *v2alpha1.ArangoDeployment, err error) { + result = &v2alpha1.ArangoDeployment{} + err = c.client.Put(). + Namespace(c.ns). + Resource("arangodeployments"). + Name(arangoDeployment.Name). + Body(arangoDeployment). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *arangoDeployments) UpdateStatus(arangoDeployment *v2alpha1.ArangoDeployment) (result *v2alpha1.ArangoDeployment, err error) { + result = &v2alpha1.ArangoDeployment{} + err = c.client.Put(). + Namespace(c.ns). + Resource("arangodeployments"). + Name(arangoDeployment.Name). + SubResource("status"). + Body(arangoDeployment). + Do(). + Into(result) + return +} + +// Delete takes name of the arangoDeployment and deletes it. Returns an error if one occurs. +func (c *arangoDeployments) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("arangodeployments"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *arangoDeployments) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("arangodeployments"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched arangoDeployment. +func (c *arangoDeployments) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v2alpha1.ArangoDeployment, err error) { + result = &v2alpha1.ArangoDeployment{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("arangodeployments"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/deployment_client.go b/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/deployment_client.go new file mode 100644 index 000000000..d1261d6d5 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/deployment_client.go @@ -0,0 +1,93 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by client-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + v2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v2alpha1" + "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type DatabaseV2alpha1Interface interface { + RESTClient() rest.Interface + ArangoDeploymentsGetter +} + +// DatabaseV2alpha1Client is used to interact with features provided by the database.arangodb.com group. +type DatabaseV2alpha1Client struct { + restClient rest.Interface +} + +func (c *DatabaseV2alpha1Client) ArangoDeployments(namespace string) ArangoDeploymentInterface { + return newArangoDeployments(c, namespace) +} + +// NewForConfig creates a new DatabaseV2alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*DatabaseV2alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &DatabaseV2alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new DatabaseV2alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *DatabaseV2alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new DatabaseV2alpha1Client for the given RESTClient. +func New(c rest.Interface) *DatabaseV2alpha1Client { + return &DatabaseV2alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v2alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *DatabaseV2alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/doc.go b/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/doc.go new file mode 100644 index 000000000..3680b60d6 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/doc.go @@ -0,0 +1,24 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v2alpha1 diff --git a/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/fake/doc.go b/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/fake/doc.go new file mode 100644 index 000000000..dd105e835 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/fake/doc.go @@ -0,0 +1,24 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/fake/fake_arangodeployment.go b/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/fake/fake_arangodeployment.go new file mode 100644 index 000000000..65931a53b --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/fake/fake_arangodeployment.go @@ -0,0 +1,144 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v2alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeArangoDeployments implements ArangoDeploymentInterface +type FakeArangoDeployments struct { + Fake *FakeDatabaseV2alpha1 + ns string +} + +var arangodeploymentsResource = schema.GroupVersionResource{Group: "database.arangodb.com", Version: "v2alpha1", Resource: "arangodeployments"} + +var arangodeploymentsKind = schema.GroupVersionKind{Group: "database.arangodb.com", Version: "v2alpha1", Kind: "ArangoDeployment"} + +// Get takes name of the arangoDeployment, and returns the corresponding arangoDeployment object, and an error if there is any. +func (c *FakeArangoDeployments) Get(name string, options v1.GetOptions) (result *v2alpha1.ArangoDeployment, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(arangodeploymentsResource, c.ns, name), &v2alpha1.ArangoDeployment{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.ArangoDeployment), err +} + +// List takes label and field selectors, and returns the list of ArangoDeployments that match those selectors. +func (c *FakeArangoDeployments) List(opts v1.ListOptions) (result *v2alpha1.ArangoDeploymentList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(arangodeploymentsResource, arangodeploymentsKind, c.ns, opts), &v2alpha1.ArangoDeploymentList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v2alpha1.ArangoDeploymentList{ListMeta: obj.(*v2alpha1.ArangoDeploymentList).ListMeta} + for _, item := range obj.(*v2alpha1.ArangoDeploymentList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested arangoDeployments. +func (c *FakeArangoDeployments) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(arangodeploymentsResource, c.ns, opts)) + +} + +// Create takes the representation of a arangoDeployment and creates it. Returns the server's representation of the arangoDeployment, and an error, if there is any. +func (c *FakeArangoDeployments) Create(arangoDeployment *v2alpha1.ArangoDeployment) (result *v2alpha1.ArangoDeployment, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(arangodeploymentsResource, c.ns, arangoDeployment), &v2alpha1.ArangoDeployment{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.ArangoDeployment), err +} + +// Update takes the representation of a arangoDeployment and updates it. Returns the server's representation of the arangoDeployment, and an error, if there is any. +func (c *FakeArangoDeployments) Update(arangoDeployment *v2alpha1.ArangoDeployment) (result *v2alpha1.ArangoDeployment, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(arangodeploymentsResource, c.ns, arangoDeployment), &v2alpha1.ArangoDeployment{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.ArangoDeployment), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeArangoDeployments) UpdateStatus(arangoDeployment *v2alpha1.ArangoDeployment) (*v2alpha1.ArangoDeployment, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(arangodeploymentsResource, "status", c.ns, arangoDeployment), &v2alpha1.ArangoDeployment{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.ArangoDeployment), err +} + +// Delete takes name of the arangoDeployment and deletes it. Returns an error if one occurs. +func (c *FakeArangoDeployments) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(arangodeploymentsResource, c.ns, name), &v2alpha1.ArangoDeployment{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeArangoDeployments) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(arangodeploymentsResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v2alpha1.ArangoDeploymentList{}) + return err +} + +// Patch applies the patch and returns the patched arangoDeployment. +func (c *FakeArangoDeployments) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v2alpha1.ArangoDeployment, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(arangodeploymentsResource, c.ns, name, pt, data, subresources...), &v2alpha1.ArangoDeployment{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.ArangoDeployment), err +} diff --git a/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/fake/fake_deployment_client.go b/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/fake/fake_deployment_client.go new file mode 100644 index 000000000..e38932b3b --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/fake/fake_deployment_client.go @@ -0,0 +1,44 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v2alpha1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/deployment/v2alpha1" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeDatabaseV2alpha1 struct { + *testing.Fake +} + +func (c *FakeDatabaseV2alpha1) ArangoDeployments(namespace string) v2alpha1.ArangoDeploymentInterface { + return &FakeArangoDeployments{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeDatabaseV2alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/generated_expansion.go new file mode 100644 index 000000000..ec5a98f91 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/deployment/v2alpha1/generated_expansion.go @@ -0,0 +1,25 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by client-gen. DO NOT EDIT. + +package v2alpha1 + +type ArangoDeploymentExpansion interface{} diff --git a/pkg/generated/clientset/versioned/typed/replication/v2alpha1/arangodeploymentreplication.go b/pkg/generated/clientset/versioned/typed/replication/v2alpha1/arangodeploymentreplication.go new file mode 100644 index 000000000..d887da973 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/replication/v2alpha1/arangodeploymentreplication.go @@ -0,0 +1,195 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by client-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + "time" + + v2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/replication/v2alpha1" + scheme "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ArangoDeploymentReplicationsGetter has a method to return a ArangoDeploymentReplicationInterface. +// A group's client should implement this interface. +type ArangoDeploymentReplicationsGetter interface { + ArangoDeploymentReplications(namespace string) ArangoDeploymentReplicationInterface +} + +// ArangoDeploymentReplicationInterface has methods to work with ArangoDeploymentReplication resources. +type ArangoDeploymentReplicationInterface interface { + Create(*v2alpha1.ArangoDeploymentReplication) (*v2alpha1.ArangoDeploymentReplication, error) + Update(*v2alpha1.ArangoDeploymentReplication) (*v2alpha1.ArangoDeploymentReplication, error) + UpdateStatus(*v2alpha1.ArangoDeploymentReplication) (*v2alpha1.ArangoDeploymentReplication, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v2alpha1.ArangoDeploymentReplication, error) + List(opts v1.ListOptions) (*v2alpha1.ArangoDeploymentReplicationList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v2alpha1.ArangoDeploymentReplication, err error) + ArangoDeploymentReplicationExpansion +} + +// arangoDeploymentReplications implements ArangoDeploymentReplicationInterface +type arangoDeploymentReplications struct { + client rest.Interface + ns string +} + +// newArangoDeploymentReplications returns a ArangoDeploymentReplications +func newArangoDeploymentReplications(c *ReplicationV2alpha1Client, namespace string) *arangoDeploymentReplications { + return &arangoDeploymentReplications{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the arangoDeploymentReplication, and returns the corresponding arangoDeploymentReplication object, and an error if there is any. +func (c *arangoDeploymentReplications) Get(name string, options v1.GetOptions) (result *v2alpha1.ArangoDeploymentReplication, err error) { + result = &v2alpha1.ArangoDeploymentReplication{} + err = c.client.Get(). + Namespace(c.ns). + Resource("arangodeploymentreplications"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ArangoDeploymentReplications that match those selectors. +func (c *arangoDeploymentReplications) List(opts v1.ListOptions) (result *v2alpha1.ArangoDeploymentReplicationList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v2alpha1.ArangoDeploymentReplicationList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("arangodeploymentreplications"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested arangoDeploymentReplications. +func (c *arangoDeploymentReplications) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("arangodeploymentreplications"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a arangoDeploymentReplication and creates it. Returns the server's representation of the arangoDeploymentReplication, and an error, if there is any. +func (c *arangoDeploymentReplications) Create(arangoDeploymentReplication *v2alpha1.ArangoDeploymentReplication) (result *v2alpha1.ArangoDeploymentReplication, err error) { + result = &v2alpha1.ArangoDeploymentReplication{} + err = c.client.Post(). + Namespace(c.ns). + Resource("arangodeploymentreplications"). + Body(arangoDeploymentReplication). + Do(). + Into(result) + return +} + +// Update takes the representation of a arangoDeploymentReplication and updates it. Returns the server's representation of the arangoDeploymentReplication, and an error, if there is any. +func (c *arangoDeploymentReplications) Update(arangoDeploymentReplication *v2alpha1.ArangoDeploymentReplication) (result *v2alpha1.ArangoDeploymentReplication, err error) { + result = &v2alpha1.ArangoDeploymentReplication{} + err = c.client.Put(). + Namespace(c.ns). + Resource("arangodeploymentreplications"). + Name(arangoDeploymentReplication.Name). + Body(arangoDeploymentReplication). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *arangoDeploymentReplications) UpdateStatus(arangoDeploymentReplication *v2alpha1.ArangoDeploymentReplication) (result *v2alpha1.ArangoDeploymentReplication, err error) { + result = &v2alpha1.ArangoDeploymentReplication{} + err = c.client.Put(). + Namespace(c.ns). + Resource("arangodeploymentreplications"). + Name(arangoDeploymentReplication.Name). + SubResource("status"). + Body(arangoDeploymentReplication). + Do(). + Into(result) + return +} + +// Delete takes name of the arangoDeploymentReplication and deletes it. Returns an error if one occurs. +func (c *arangoDeploymentReplications) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("arangodeploymentreplications"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *arangoDeploymentReplications) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("arangodeploymentreplications"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched arangoDeploymentReplication. +func (c *arangoDeploymentReplications) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v2alpha1.ArangoDeploymentReplication, err error) { + result = &v2alpha1.ArangoDeploymentReplication{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("arangodeploymentreplications"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/generated/clientset/versioned/typed/replication/v2alpha1/doc.go b/pkg/generated/clientset/versioned/typed/replication/v2alpha1/doc.go new file mode 100644 index 000000000..3680b60d6 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/replication/v2alpha1/doc.go @@ -0,0 +1,24 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v2alpha1 diff --git a/pkg/generated/clientset/versioned/typed/replication/v2alpha1/fake/doc.go b/pkg/generated/clientset/versioned/typed/replication/v2alpha1/fake/doc.go new file mode 100644 index 000000000..dd105e835 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/replication/v2alpha1/fake/doc.go @@ -0,0 +1,24 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/generated/clientset/versioned/typed/replication/v2alpha1/fake/fake_arangodeploymentreplication.go b/pkg/generated/clientset/versioned/typed/replication/v2alpha1/fake/fake_arangodeploymentreplication.go new file mode 100644 index 000000000..4d70028d1 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/replication/v2alpha1/fake/fake_arangodeploymentreplication.go @@ -0,0 +1,144 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/replication/v2alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeArangoDeploymentReplications implements ArangoDeploymentReplicationInterface +type FakeArangoDeploymentReplications struct { + Fake *FakeReplicationV2alpha1 + ns string +} + +var arangodeploymentreplicationsResource = schema.GroupVersionResource{Group: "replication.database.arangodb.com", Version: "v2alpha1", Resource: "arangodeploymentreplications"} + +var arangodeploymentreplicationsKind = schema.GroupVersionKind{Group: "replication.database.arangodb.com", Version: "v2alpha1", Kind: "ArangoDeploymentReplication"} + +// Get takes name of the arangoDeploymentReplication, and returns the corresponding arangoDeploymentReplication object, and an error if there is any. +func (c *FakeArangoDeploymentReplications) Get(name string, options v1.GetOptions) (result *v2alpha1.ArangoDeploymentReplication, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(arangodeploymentreplicationsResource, c.ns, name), &v2alpha1.ArangoDeploymentReplication{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.ArangoDeploymentReplication), err +} + +// List takes label and field selectors, and returns the list of ArangoDeploymentReplications that match those selectors. +func (c *FakeArangoDeploymentReplications) List(opts v1.ListOptions) (result *v2alpha1.ArangoDeploymentReplicationList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(arangodeploymentreplicationsResource, arangodeploymentreplicationsKind, c.ns, opts), &v2alpha1.ArangoDeploymentReplicationList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v2alpha1.ArangoDeploymentReplicationList{ListMeta: obj.(*v2alpha1.ArangoDeploymentReplicationList).ListMeta} + for _, item := range obj.(*v2alpha1.ArangoDeploymentReplicationList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested arangoDeploymentReplications. +func (c *FakeArangoDeploymentReplications) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(arangodeploymentreplicationsResource, c.ns, opts)) + +} + +// Create takes the representation of a arangoDeploymentReplication and creates it. Returns the server's representation of the arangoDeploymentReplication, and an error, if there is any. +func (c *FakeArangoDeploymentReplications) Create(arangoDeploymentReplication *v2alpha1.ArangoDeploymentReplication) (result *v2alpha1.ArangoDeploymentReplication, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(arangodeploymentreplicationsResource, c.ns, arangoDeploymentReplication), &v2alpha1.ArangoDeploymentReplication{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.ArangoDeploymentReplication), err +} + +// Update takes the representation of a arangoDeploymentReplication and updates it. Returns the server's representation of the arangoDeploymentReplication, and an error, if there is any. +func (c *FakeArangoDeploymentReplications) Update(arangoDeploymentReplication *v2alpha1.ArangoDeploymentReplication) (result *v2alpha1.ArangoDeploymentReplication, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(arangodeploymentreplicationsResource, c.ns, arangoDeploymentReplication), &v2alpha1.ArangoDeploymentReplication{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.ArangoDeploymentReplication), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeArangoDeploymentReplications) UpdateStatus(arangoDeploymentReplication *v2alpha1.ArangoDeploymentReplication) (*v2alpha1.ArangoDeploymentReplication, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(arangodeploymentreplicationsResource, "status", c.ns, arangoDeploymentReplication), &v2alpha1.ArangoDeploymentReplication{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.ArangoDeploymentReplication), err +} + +// Delete takes name of the arangoDeploymentReplication and deletes it. Returns an error if one occurs. +func (c *FakeArangoDeploymentReplications) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(arangodeploymentreplicationsResource, c.ns, name), &v2alpha1.ArangoDeploymentReplication{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeArangoDeploymentReplications) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(arangodeploymentreplicationsResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v2alpha1.ArangoDeploymentReplicationList{}) + return err +} + +// Patch applies the patch and returns the patched arangoDeploymentReplication. +func (c *FakeArangoDeploymentReplications) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v2alpha1.ArangoDeploymentReplication, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(arangodeploymentreplicationsResource, c.ns, name, pt, data, subresources...), &v2alpha1.ArangoDeploymentReplication{}) + + if obj == nil { + return nil, err + } + return obj.(*v2alpha1.ArangoDeploymentReplication), err +} diff --git a/pkg/generated/clientset/versioned/typed/replication/v2alpha1/fake/fake_replication_client.go b/pkg/generated/clientset/versioned/typed/replication/v2alpha1/fake/fake_replication_client.go new file mode 100644 index 000000000..6c02625bc --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/replication/v2alpha1/fake/fake_replication_client.go @@ -0,0 +1,44 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v2alpha1 "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/replication/v2alpha1" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeReplicationV2alpha1 struct { + *testing.Fake +} + +func (c *FakeReplicationV2alpha1) ArangoDeploymentReplications(namespace string) v2alpha1.ArangoDeploymentReplicationInterface { + return &FakeArangoDeploymentReplications{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeReplicationV2alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/generated/clientset/versioned/typed/replication/v2alpha1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/replication/v2alpha1/generated_expansion.go new file mode 100644 index 000000000..e80d25843 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/replication/v2alpha1/generated_expansion.go @@ -0,0 +1,25 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by client-gen. DO NOT EDIT. + +package v2alpha1 + +type ArangoDeploymentReplicationExpansion interface{} diff --git a/pkg/generated/clientset/versioned/typed/replication/v2alpha1/replication_client.go b/pkg/generated/clientset/versioned/typed/replication/v2alpha1/replication_client.go new file mode 100644 index 000000000..2ef848718 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/replication/v2alpha1/replication_client.go @@ -0,0 +1,93 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by client-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + v2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/replication/v2alpha1" + "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type ReplicationV2alpha1Interface interface { + RESTClient() rest.Interface + ArangoDeploymentReplicationsGetter +} + +// ReplicationV2alpha1Client is used to interact with features provided by the replication.database.arangodb.com group. +type ReplicationV2alpha1Client struct { + restClient rest.Interface +} + +func (c *ReplicationV2alpha1Client) ArangoDeploymentReplications(namespace string) ArangoDeploymentReplicationInterface { + return newArangoDeploymentReplications(c, namespace) +} + +// NewForConfig creates a new ReplicationV2alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*ReplicationV2alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &ReplicationV2alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new ReplicationV2alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *ReplicationV2alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new ReplicationV2alpha1Client for the given RESTClient. +func New(c rest.Interface) *ReplicationV2alpha1Client { + return &ReplicationV2alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v2alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *ReplicationV2alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/generated/informers/externalversions/deployment/interface.go b/pkg/generated/informers/externalversions/deployment/interface.go index 50bb23847..7b77bfa01 100644 --- a/pkg/generated/informers/externalversions/deployment/interface.go +++ b/pkg/generated/informers/externalversions/deployment/interface.go @@ -24,6 +24,7 @@ package deployment import ( v1 "github.com/arangodb/kube-arangodb/pkg/generated/informers/externalversions/deployment/v1" + v2alpha1 "github.com/arangodb/kube-arangodb/pkg/generated/informers/externalversions/deployment/v2alpha1" internalinterfaces "github.com/arangodb/kube-arangodb/pkg/generated/informers/externalversions/internalinterfaces" ) @@ -31,6 +32,8 @@ import ( type Interface interface { // V1 provides access to shared informers for resources in V1. V1() v1.Interface + // V2alpha1 provides access to shared informers for resources in V2alpha1. + V2alpha1() v2alpha1.Interface } type group struct { @@ -48,3 +51,8 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList func (g *group) V1() v1.Interface { return v1.New(g.factory, g.namespace, g.tweakListOptions) } + +// V2alpha1 returns a new v2alpha1.Interface. +func (g *group) V2alpha1() v2alpha1.Interface { + return v2alpha1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/generated/informers/externalversions/deployment/v2alpha1/arangodeployment.go b/pkg/generated/informers/externalversions/deployment/v2alpha1/arangodeployment.go new file mode 100644 index 000000000..eb27436d9 --- /dev/null +++ b/pkg/generated/informers/externalversions/deployment/v2alpha1/arangodeployment.go @@ -0,0 +1,93 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by informer-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + time "time" + + deploymentv2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v2alpha1" + versioned "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned" + internalinterfaces "github.com/arangodb/kube-arangodb/pkg/generated/informers/externalversions/internalinterfaces" + v2alpha1 "github.com/arangodb/kube-arangodb/pkg/generated/listers/deployment/v2alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ArangoDeploymentInformer provides access to a shared informer and lister for +// ArangoDeployments. +type ArangoDeploymentInformer interface { + Informer() cache.SharedIndexInformer + Lister() v2alpha1.ArangoDeploymentLister +} + +type arangoDeploymentInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewArangoDeploymentInformer constructs a new informer for ArangoDeployment type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewArangoDeploymentInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredArangoDeploymentInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredArangoDeploymentInformer constructs a new informer for ArangoDeployment type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredArangoDeploymentInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.DatabaseV2alpha1().ArangoDeployments(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.DatabaseV2alpha1().ArangoDeployments(namespace).Watch(options) + }, + }, + &deploymentv2alpha1.ArangoDeployment{}, + resyncPeriod, + indexers, + ) +} + +func (f *arangoDeploymentInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredArangoDeploymentInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *arangoDeploymentInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&deploymentv2alpha1.ArangoDeployment{}, f.defaultInformer) +} + +func (f *arangoDeploymentInformer) Lister() v2alpha1.ArangoDeploymentLister { + return v2alpha1.NewArangoDeploymentLister(f.Informer().GetIndexer()) +} diff --git a/pkg/generated/informers/externalversions/deployment/v2alpha1/interface.go b/pkg/generated/informers/externalversions/deployment/v2alpha1/interface.go new file mode 100644 index 000000000..fa4f7d3c3 --- /dev/null +++ b/pkg/generated/informers/externalversions/deployment/v2alpha1/interface.go @@ -0,0 +1,49 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by informer-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + internalinterfaces "github.com/arangodb/kube-arangodb/pkg/generated/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // ArangoDeployments returns a ArangoDeploymentInformer. + ArangoDeployments() ArangoDeploymentInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// ArangoDeployments returns a ArangoDeploymentInformer. +func (v *version) ArangoDeployments() ArangoDeploymentInformer { + return &arangoDeploymentInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go index c277af4c0..e99686684 100644 --- a/pkg/generated/informers/externalversions/generic.go +++ b/pkg/generated/informers/externalversions/generic.go @@ -27,7 +27,9 @@ import ( v1 "github.com/arangodb/kube-arangodb/pkg/apis/backup/v1" deploymentv1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" + v2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v2alpha1" replicationv1 "github.com/arangodb/kube-arangodb/pkg/apis/replication/v1" + replicationv2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/replication/v2alpha1" v1alpha "github.com/arangodb/kube-arangodb/pkg/apis/storage/v1alpha" schema "k8s.io/apimachinery/pkg/runtime/schema" cache "k8s.io/client-go/tools/cache" @@ -69,10 +71,18 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource case deploymentv1.SchemeGroupVersion.WithResource("arangodeployments"): return &genericInformer{resource: resource.GroupResource(), informer: f.Database().V1().ArangoDeployments().Informer()}, nil + // Group=database.arangodb.com, Version=v2alpha1 + case v2alpha1.SchemeGroupVersion.WithResource("arangodeployments"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Database().V2alpha1().ArangoDeployments().Informer()}, nil + // Group=replication.database.arangodb.com, Version=v1 case replicationv1.SchemeGroupVersion.WithResource("arangodeploymentreplications"): return &genericInformer{resource: resource.GroupResource(), informer: f.Replication().V1().ArangoDeploymentReplications().Informer()}, nil + // Group=replication.database.arangodb.com, Version=v2alpha1 + case replicationv2alpha1.SchemeGroupVersion.WithResource("arangodeploymentreplications"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Replication().V2alpha1().ArangoDeploymentReplications().Informer()}, nil + // Group=storage.arangodb.com, Version=v1alpha case v1alpha.SchemeGroupVersion.WithResource("arangolocalstorages"): return &genericInformer{resource: resource.GroupResource(), informer: f.Storage().V1alpha().ArangoLocalStorages().Informer()}, nil diff --git a/pkg/generated/informers/externalversions/replication/interface.go b/pkg/generated/informers/externalversions/replication/interface.go index 347e74dc2..2ad7f598c 100644 --- a/pkg/generated/informers/externalversions/replication/interface.go +++ b/pkg/generated/informers/externalversions/replication/interface.go @@ -25,12 +25,15 @@ package replication import ( internalinterfaces "github.com/arangodb/kube-arangodb/pkg/generated/informers/externalversions/internalinterfaces" v1 "github.com/arangodb/kube-arangodb/pkg/generated/informers/externalversions/replication/v1" + v2alpha1 "github.com/arangodb/kube-arangodb/pkg/generated/informers/externalversions/replication/v2alpha1" ) // Interface provides access to each of this group's versions. type Interface interface { // V1 provides access to shared informers for resources in V1. V1() v1.Interface + // V2alpha1 provides access to shared informers for resources in V2alpha1. + V2alpha1() v2alpha1.Interface } type group struct { @@ -48,3 +51,8 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList func (g *group) V1() v1.Interface { return v1.New(g.factory, g.namespace, g.tweakListOptions) } + +// V2alpha1 returns a new v2alpha1.Interface. +func (g *group) V2alpha1() v2alpha1.Interface { + return v2alpha1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/generated/informers/externalversions/replication/v2alpha1/arangodeploymentreplication.go b/pkg/generated/informers/externalversions/replication/v2alpha1/arangodeploymentreplication.go new file mode 100644 index 000000000..38d251d56 --- /dev/null +++ b/pkg/generated/informers/externalversions/replication/v2alpha1/arangodeploymentreplication.go @@ -0,0 +1,93 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by informer-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + time "time" + + replicationv2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/replication/v2alpha1" + versioned "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned" + internalinterfaces "github.com/arangodb/kube-arangodb/pkg/generated/informers/externalversions/internalinterfaces" + v2alpha1 "github.com/arangodb/kube-arangodb/pkg/generated/listers/replication/v2alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ArangoDeploymentReplicationInformer provides access to a shared informer and lister for +// ArangoDeploymentReplications. +type ArangoDeploymentReplicationInformer interface { + Informer() cache.SharedIndexInformer + Lister() v2alpha1.ArangoDeploymentReplicationLister +} + +type arangoDeploymentReplicationInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewArangoDeploymentReplicationInformer constructs a new informer for ArangoDeploymentReplication type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewArangoDeploymentReplicationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredArangoDeploymentReplicationInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredArangoDeploymentReplicationInformer constructs a new informer for ArangoDeploymentReplication type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredArangoDeploymentReplicationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ReplicationV2alpha1().ArangoDeploymentReplications(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ReplicationV2alpha1().ArangoDeploymentReplications(namespace).Watch(options) + }, + }, + &replicationv2alpha1.ArangoDeploymentReplication{}, + resyncPeriod, + indexers, + ) +} + +func (f *arangoDeploymentReplicationInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredArangoDeploymentReplicationInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *arangoDeploymentReplicationInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&replicationv2alpha1.ArangoDeploymentReplication{}, f.defaultInformer) +} + +func (f *arangoDeploymentReplicationInformer) Lister() v2alpha1.ArangoDeploymentReplicationLister { + return v2alpha1.NewArangoDeploymentReplicationLister(f.Informer().GetIndexer()) +} diff --git a/pkg/generated/informers/externalversions/replication/v2alpha1/interface.go b/pkg/generated/informers/externalversions/replication/v2alpha1/interface.go new file mode 100644 index 000000000..36a2dc048 --- /dev/null +++ b/pkg/generated/informers/externalversions/replication/v2alpha1/interface.go @@ -0,0 +1,49 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by informer-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + internalinterfaces "github.com/arangodb/kube-arangodb/pkg/generated/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // ArangoDeploymentReplications returns a ArangoDeploymentReplicationInformer. + ArangoDeploymentReplications() ArangoDeploymentReplicationInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// ArangoDeploymentReplications returns a ArangoDeploymentReplicationInformer. +func (v *version) ArangoDeploymentReplications() ArangoDeploymentReplicationInformer { + return &arangoDeploymentReplicationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/generated/listers/deployment/v2alpha1/arangodeployment.go b/pkg/generated/listers/deployment/v2alpha1/arangodeployment.go new file mode 100644 index 000000000..a15adf825 --- /dev/null +++ b/pkg/generated/listers/deployment/v2alpha1/arangodeployment.go @@ -0,0 +1,98 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by lister-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + v2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v2alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ArangoDeploymentLister helps list ArangoDeployments. +type ArangoDeploymentLister interface { + // List lists all ArangoDeployments in the indexer. + List(selector labels.Selector) (ret []*v2alpha1.ArangoDeployment, err error) + // ArangoDeployments returns an object that can list and get ArangoDeployments. + ArangoDeployments(namespace string) ArangoDeploymentNamespaceLister + ArangoDeploymentListerExpansion +} + +// arangoDeploymentLister implements the ArangoDeploymentLister interface. +type arangoDeploymentLister struct { + indexer cache.Indexer +} + +// NewArangoDeploymentLister returns a new ArangoDeploymentLister. +func NewArangoDeploymentLister(indexer cache.Indexer) ArangoDeploymentLister { + return &arangoDeploymentLister{indexer: indexer} +} + +// List lists all ArangoDeployments in the indexer. +func (s *arangoDeploymentLister) List(selector labels.Selector) (ret []*v2alpha1.ArangoDeployment, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v2alpha1.ArangoDeployment)) + }) + return ret, err +} + +// ArangoDeployments returns an object that can list and get ArangoDeployments. +func (s *arangoDeploymentLister) ArangoDeployments(namespace string) ArangoDeploymentNamespaceLister { + return arangoDeploymentNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// ArangoDeploymentNamespaceLister helps list and get ArangoDeployments. +type ArangoDeploymentNamespaceLister interface { + // List lists all ArangoDeployments in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v2alpha1.ArangoDeployment, err error) + // Get retrieves the ArangoDeployment from the indexer for a given namespace and name. + Get(name string) (*v2alpha1.ArangoDeployment, error) + ArangoDeploymentNamespaceListerExpansion +} + +// arangoDeploymentNamespaceLister implements the ArangoDeploymentNamespaceLister +// interface. +type arangoDeploymentNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all ArangoDeployments in the indexer for a given namespace. +func (s arangoDeploymentNamespaceLister) List(selector labels.Selector) (ret []*v2alpha1.ArangoDeployment, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v2alpha1.ArangoDeployment)) + }) + return ret, err +} + +// Get retrieves the ArangoDeployment from the indexer for a given namespace and name. +func (s arangoDeploymentNamespaceLister) Get(name string) (*v2alpha1.ArangoDeployment, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v2alpha1.Resource("arangodeployment"), name) + } + return obj.(*v2alpha1.ArangoDeployment), nil +} diff --git a/pkg/generated/listers/deployment/v2alpha1/expansion_generated.go b/pkg/generated/listers/deployment/v2alpha1/expansion_generated.go new file mode 100644 index 000000000..3ff2151ff --- /dev/null +++ b/pkg/generated/listers/deployment/v2alpha1/expansion_generated.go @@ -0,0 +1,31 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by lister-gen. DO NOT EDIT. + +package v2alpha1 + +// ArangoDeploymentListerExpansion allows custom methods to be added to +// ArangoDeploymentLister. +type ArangoDeploymentListerExpansion interface{} + +// ArangoDeploymentNamespaceListerExpansion allows custom methods to be added to +// ArangoDeploymentNamespaceLister. +type ArangoDeploymentNamespaceListerExpansion interface{} diff --git a/pkg/generated/listers/replication/v2alpha1/arangodeploymentreplication.go b/pkg/generated/listers/replication/v2alpha1/arangodeploymentreplication.go new file mode 100644 index 000000000..1c90a9e07 --- /dev/null +++ b/pkg/generated/listers/replication/v2alpha1/arangodeploymentreplication.go @@ -0,0 +1,98 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by lister-gen. DO NOT EDIT. + +package v2alpha1 + +import ( + v2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/replication/v2alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ArangoDeploymentReplicationLister helps list ArangoDeploymentReplications. +type ArangoDeploymentReplicationLister interface { + // List lists all ArangoDeploymentReplications in the indexer. + List(selector labels.Selector) (ret []*v2alpha1.ArangoDeploymentReplication, err error) + // ArangoDeploymentReplications returns an object that can list and get ArangoDeploymentReplications. + ArangoDeploymentReplications(namespace string) ArangoDeploymentReplicationNamespaceLister + ArangoDeploymentReplicationListerExpansion +} + +// arangoDeploymentReplicationLister implements the ArangoDeploymentReplicationLister interface. +type arangoDeploymentReplicationLister struct { + indexer cache.Indexer +} + +// NewArangoDeploymentReplicationLister returns a new ArangoDeploymentReplicationLister. +func NewArangoDeploymentReplicationLister(indexer cache.Indexer) ArangoDeploymentReplicationLister { + return &arangoDeploymentReplicationLister{indexer: indexer} +} + +// List lists all ArangoDeploymentReplications in the indexer. +func (s *arangoDeploymentReplicationLister) List(selector labels.Selector) (ret []*v2alpha1.ArangoDeploymentReplication, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v2alpha1.ArangoDeploymentReplication)) + }) + return ret, err +} + +// ArangoDeploymentReplications returns an object that can list and get ArangoDeploymentReplications. +func (s *arangoDeploymentReplicationLister) ArangoDeploymentReplications(namespace string) ArangoDeploymentReplicationNamespaceLister { + return arangoDeploymentReplicationNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// ArangoDeploymentReplicationNamespaceLister helps list and get ArangoDeploymentReplications. +type ArangoDeploymentReplicationNamespaceLister interface { + // List lists all ArangoDeploymentReplications in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v2alpha1.ArangoDeploymentReplication, err error) + // Get retrieves the ArangoDeploymentReplication from the indexer for a given namespace and name. + Get(name string) (*v2alpha1.ArangoDeploymentReplication, error) + ArangoDeploymentReplicationNamespaceListerExpansion +} + +// arangoDeploymentReplicationNamespaceLister implements the ArangoDeploymentReplicationNamespaceLister +// interface. +type arangoDeploymentReplicationNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all ArangoDeploymentReplications in the indexer for a given namespace. +func (s arangoDeploymentReplicationNamespaceLister) List(selector labels.Selector) (ret []*v2alpha1.ArangoDeploymentReplication, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v2alpha1.ArangoDeploymentReplication)) + }) + return ret, err +} + +// Get retrieves the ArangoDeploymentReplication from the indexer for a given namespace and name. +func (s arangoDeploymentReplicationNamespaceLister) Get(name string) (*v2alpha1.ArangoDeploymentReplication, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v2alpha1.Resource("arangodeploymentreplication"), name) + } + return obj.(*v2alpha1.ArangoDeploymentReplication), nil +} diff --git a/pkg/generated/listers/replication/v2alpha1/expansion_generated.go b/pkg/generated/listers/replication/v2alpha1/expansion_generated.go new file mode 100644 index 000000000..c62ddddaf --- /dev/null +++ b/pkg/generated/listers/replication/v2alpha1/expansion_generated.go @@ -0,0 +1,31 @@ +// +// DISCLAIMER +// +// Copyright 2020 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 +// + +// Code generated by lister-gen. DO NOT EDIT. + +package v2alpha1 + +// ArangoDeploymentReplicationListerExpansion allows custom methods to be added to +// ArangoDeploymentReplicationLister. +type ArangoDeploymentReplicationListerExpansion interface{} + +// ArangoDeploymentReplicationNamespaceListerExpansion allows custom methods to be added to +// ArangoDeploymentReplicationNamespaceLister. +type ArangoDeploymentReplicationNamespaceListerExpansion interface{}