From d482e4283a10d52c32c8df2b6d28df776d802cfb Mon Sep 17 00:00:00 2001 From: Nikita Vaniasin Date: Fri, 22 Dec 2023 22:54:49 +0100 Subject: [PATCH] [Feature] [ML] Extension STS update propagation (#1548) --- docs/api/ArangoMLExtension.V1Alpha1.md | 12 ++++++ pkg/apis/deployment/v1/plan.go | 6 +-- pkg/apis/deployment/v2alpha1/plan.go | 6 +-- .../ml/v1alpha1/extension_spec_deployment.go | 5 ++- pkg/apis/ml/v1alpha1/extension_status.go | 3 ++ .../extension_status_reconciliation.go | 40 +++++++++++++++++ pkg/apis/ml/v1alpha1/zz_generated.deepcopy.go | 21 +++++++++ pkg/util/compare/compare.go | 2 +- pkg/util/compare/interfaces.go | 6 --- .../{helpers.go => compare/k8s/service.go} | 26 ++++++++--- pkg/util/compare/k8s/statefulset.go | 43 +++++++++++++++++++ pkg/util/list.go | 11 +---- 12 files changed, 150 insertions(+), 31 deletions(-) create mode 100644 pkg/apis/ml/v1alpha1/extension_status_reconciliation.go rename pkg/util/{helpers.go => compare/k8s/service.go} (61%) create mode 100644 pkg/util/compare/k8s/statefulset.go diff --git a/docs/api/ArangoMLExtension.V1Alpha1.md b/docs/api/ArangoMLExtension.V1Alpha1.md index f33f2fef5..32f8e1468 100644 --- a/docs/api/ArangoMLExtension.V1Alpha1.md +++ b/docs/api/ArangoMLExtension.V1Alpha1.md @@ -1427,6 +1427,18 @@ UID keeps the information about object UID *** +### .status.reconciliation.serviceChecksum + +Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.35/pkg/apis/ml/v1alpha1/extension_status_reconciliation.go#L25) + +*** + +### .status.reconciliation.statefulSetChecksum + +Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.35/pkg/apis/ml/v1alpha1/extension_status_reconciliation.go#L24) + +*** + ### .status.serviceAccount.cluster.binding.name Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.35/pkg/apis/shared/v1/object.go#L46) diff --git a/pkg/apis/deployment/v1/plan.go b/pkg/apis/deployment/v1/plan.go index 6ef50583f..07f627da0 100644 --- a/pkg/apis/deployment/v1/plan.go +++ b/pkg/apis/deployment/v1/plan.go @@ -82,9 +82,9 @@ type Action struct { 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 is set when the action is created. CreationTime meta.Time `json:"creationTime"` - // StartTime is set the when the action has been started, but needs to wait to be finished. + // StartTime is set when the action has been started, but needs to wait to be finished. StartTime *meta.Time `json:"startTime,omitempty"` // Reason for this action Reason string `json:"reason,omitempty"` @@ -98,7 +98,7 @@ type Action struct { TaskID types.UID `json:"taskID,omitempty"` // Architecture of the member involved in this action (if any) Architecture ArangoDeploymentArchitectureType `json:"arch,omitempty"` - // Progress describes what is a status of the current action. + // Progress describes the status of the current action. Progress string `json:"progress,omitempty"` } diff --git a/pkg/apis/deployment/v2alpha1/plan.go b/pkg/apis/deployment/v2alpha1/plan.go index c16a57c18..283ee4a0f 100644 --- a/pkg/apis/deployment/v2alpha1/plan.go +++ b/pkg/apis/deployment/v2alpha1/plan.go @@ -82,9 +82,9 @@ type Action struct { 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 is set when the action is created. CreationTime meta.Time `json:"creationTime"` - // StartTime is set the when the action has been started, but needs to wait to be finished. + // StartTime is set when the action has been started, but needs to wait to be finished. StartTime *meta.Time `json:"startTime,omitempty"` // Reason for this action Reason string `json:"reason,omitempty"` @@ -98,7 +98,7 @@ type Action struct { TaskID types.UID `json:"taskID,omitempty"` // Architecture of the member involved in this action (if any) Architecture ArangoDeploymentArchitectureType `json:"arch,omitempty"` - // Progress describes what is a status of the current action. + // Progress describes the status of the current action. Progress string `json:"progress,omitempty"` } diff --git a/pkg/apis/ml/v1alpha1/extension_spec_deployment.go b/pkg/apis/ml/v1alpha1/extension_spec_deployment.go index a9aff1b27..dbe663499 100644 --- a/pkg/apis/ml/v1alpha1/extension_spec_deployment.go +++ b/pkg/apis/ml/v1alpha1/extension_spec_deployment.go @@ -165,7 +165,10 @@ func (s *ArangoMLExtensionSpecDeployment) Validate() error { continue } - if usedPorts.IndexOf(port) >= 0 { + duplicateCount := usedPorts.Count(func(i int32) bool { + return i == port + }) + if duplicateCount > 0 { errs = append(errs, shared.PrefixResourceErrors(prefix, errors.Newf("port %d already specified for other component", port))) } else { usedPorts.Append(port) diff --git a/pkg/apis/ml/v1alpha1/extension_status.go b/pkg/apis/ml/v1alpha1/extension_status.go index c5243bc20..9a18ab0ce 100644 --- a/pkg/apis/ml/v1alpha1/extension_status.go +++ b/pkg/apis/ml/v1alpha1/extension_status.go @@ -38,4 +38,7 @@ type ArangoMLExtensionStatus struct { // ArangoDB keeps the information about local arangodb reference ArangoDB *ArangoMLExtensionStatusArangoDBRef `json:"arangoDB,omitempty"` + + // Reconciliation keeps the information about reconciliation process. For internal use. + Reconciliation *ArangoMLExtensionStatusReconciliation `json:"reconciliation"` } diff --git a/pkg/apis/ml/v1alpha1/extension_status_reconciliation.go b/pkg/apis/ml/v1alpha1/extension_status_reconciliation.go new file mode 100644 index 000000000..fd07afbc5 --- /dev/null +++ b/pkg/apis/ml/v1alpha1/extension_status_reconciliation.go @@ -0,0 +1,40 @@ +// +// DISCLAIMER +// +// Copyright 2023 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 v1alpha1 + +type ArangoMLExtensionStatusReconciliation struct { + StatefulSetChecksum string `json:"statefulSetChecksum,omitempty"` + ServiceChecksum string `json:"serviceChecksum,omitempty"` +} + +func (r *ArangoMLExtensionStatusReconciliation) GetStatefulSetChecksum() string { + if r == nil { + return "" + } + return r.StatefulSetChecksum +} + +func (r *ArangoMLExtensionStatusReconciliation) GetServiceChecksum() string { + if r == nil { + return "" + } + return r.ServiceChecksum +} diff --git a/pkg/apis/ml/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/ml/v1alpha1/zz_generated.deepcopy.go index a98f9c6a8..91e88d8c2 100644 --- a/pkg/apis/ml/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/ml/v1alpha1/zz_generated.deepcopy.go @@ -540,6 +540,11 @@ func (in *ArangoMLExtensionStatus) DeepCopyInto(out *ArangoMLExtensionStatus) { *out = new(ArangoMLExtensionStatusArangoDBRef) (*in).DeepCopyInto(*out) } + if in.Reconciliation != nil { + in, out := &in.Reconciliation, &out.Reconciliation + *out = new(ArangoMLExtensionStatusReconciliation) + **out = **in + } return } @@ -626,6 +631,22 @@ func (in *ArangoMLExtensionStatusMetadataServiceLocal) DeepCopy() *ArangoMLExten return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ArangoMLExtensionStatusReconciliation) DeepCopyInto(out *ArangoMLExtensionStatusReconciliation) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoMLExtensionStatusReconciliation. +func (in *ArangoMLExtensionStatusReconciliation) DeepCopy() *ArangoMLExtensionStatusReconciliation { + if in == nil { + return nil + } + out := new(ArangoMLExtensionStatusReconciliation) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ArangoMLExtensionTemplate) DeepCopyInto(out *ArangoMLExtensionTemplate) { *out = *in diff --git a/pkg/util/compare/compare.go b/pkg/util/compare/compare.go index ef70e4665..b9b664b81 100644 --- a/pkg/util/compare/compare.go +++ b/pkg/util/compare/compare.go @@ -123,7 +123,7 @@ func P2[T interface{}, P1, P2 interface{}]( } if m, p, err := Evaluate(actionBuilder, evaluatorsFunc...); err != nil { - log.Err(err).Error("Error while getting diff") + log.Err(err).Error("Error while running evaluators") return SkippedRotation, nil, err } else { mode = mode.And(m) diff --git a/pkg/util/compare/interfaces.go b/pkg/util/compare/interfaces.go index 3d8a1f2d4..5b161c7f3 100644 --- a/pkg/util/compare/interfaces.go +++ b/pkg/util/compare/interfaces.go @@ -26,13 +26,7 @@ import ( type Template[T interface{}] interface { GetTemplate() *T - SetTemplate(*T) - - GetTemplateChecksum() string - SetTemplateChecksum(string) - GetChecksum() string - SetChecksum(string) } type Checksum[T interface{}] func(in *T) (string, error) diff --git a/pkg/util/helpers.go b/pkg/util/compare/k8s/service.go similarity index 61% rename from pkg/util/helpers.go rename to pkg/util/compare/k8s/service.go index 0147c15a3..90f8aec95 100644 --- a/pkg/util/helpers.go +++ b/pkg/util/compare/k8s/service.go @@ -18,12 +18,24 @@ // Copyright holder is ArangoDB GmbH, Cologne, Germany // -package util +package k8s -// Ter implements a ternary operation: return cond ? a : b; -func Ter[T any](cond bool, a T, b T) T { - if cond { - return a - } - return b +import ( + core "k8s.io/api/core/v1" + + "github.com/arangodb/kube-arangodb/pkg/util" +) + +func ChecksumService(s *core.Service) (string, error) { + return checksumServiceSpec(&s.Spec) +} + +func checksumServiceSpec(s *core.ServiceSpec) (string, error) { + parts := map[string]interface{}{ + "type": s.Type, + "ports": s.Ports, + "selector": s.Selector, + // add here more fields when needed + } + return util.SHA256FromJSON(parts) } diff --git a/pkg/util/compare/k8s/statefulset.go b/pkg/util/compare/k8s/statefulset.go new file mode 100644 index 000000000..409b94ee2 --- /dev/null +++ b/pkg/util/compare/k8s/statefulset.go @@ -0,0 +1,43 @@ +// +// DISCLAIMER +// +// Copyright 2023 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 k8s + +import ( + apps "k8s.io/api/apps/v1" + + "github.com/arangodb/kube-arangodb/pkg/util" +) + +func ChecksumStatefulSet(s *apps.StatefulSet) (string, error) { + return checksumStatefulSetSpec(&s.Spec) +} + +func checksumStatefulSetSpec(s *apps.StatefulSetSpec) (string, error) { + parts := map[string]interface{}{ + "replicas": s.Replicas, + "serviceName": s.ServiceName, + "minReadySeconds": s.MinReadySeconds, + "selector": s.Selector, + "template": s.Template, + // add here more fields when needed + } + return util.SHA256FromJSON(parts) +} diff --git a/pkg/util/list.go b/pkg/util/list.go index 027ae5ede..208cd8462 100644 --- a/pkg/util/list.go +++ b/pkg/util/list.go @@ -22,7 +22,7 @@ package util import "sort" -type List[T comparable] []T +type List[T any] []T func (l List[T]) Filter(fn func(T) bool) List[T] { if l == nil { @@ -57,15 +57,6 @@ func (l List[T]) Sort(fn func(T, T) bool) List[T] { return clone } -func (l List[T]) IndexOf(item T) int { - for i, element := range l { - if element == item { - return i - } - } - return -1 -} - func MapList[T, V comparable](in List[T], fn func(T) V) List[V] { if in == nil { return nil