From 157e7895ac3e99cf9ee5732c25111fc09344f8d6 Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Wed, 2 Mar 2022 23:20:45 +0000 Subject: [PATCH] [Feature] [ACS] Initial --- .gitignore | 1 + CHANGELOG.md | 1 + .../templates/deployment-operator/role.yaml | 2 +- go.mod | 7 +- .../v1/cluster_synchronization_spec.go | 8 ++ .../v1/cluster_synchronization_status.go | 10 ++ .../deployment/v1/zz_generated.deepcopy.go | 53 ++++++- .../v2alpha1/cluster_synchronization_spec.go | 7 + .../cluster_synchronization_status.go | 12 ++ .../v2alpha1/zz_generated.deepcopy.go | 79 ++++++++++- pkg/deployment/acs/acs.community.go | 34 +++++ pkg/deployment/acs/sutil/conditions.go | 28 ++++ pkg/deployment/deployment_inspector.go | 9 +- pkg/deployment/resources/inspector/acs.go | 30 +++- .../resources/inspector/inspector.go | 1 + .../arangoclustersynchronization/node.go | 1 + pkg/util/kclient/client_factory.go | 15 +- pkg/util/kclient/fake.go | 101 ++++++++++++++ pkg/util/kclient/helpers/secret.go | 51 +++++++ pkg/util/kclient/helpers/secret_test.go | 131 ++++++++++++++++++ pkg/util/tests/gen.go | 57 ++++++++ pkg/util/tests/inspector.go | 39 ++++++ 22 files changed, 653 insertions(+), 24 deletions(-) create mode 100644 pkg/deployment/acs/acs.community.go create mode 100644 pkg/deployment/acs/sutil/conditions.go create mode 100644 pkg/util/kclient/fake.go create mode 100644 pkg/util/kclient/helpers/secret.go create mode 100644 pkg/util/kclient/helpers/secret_test.go create mode 100644 pkg/util/tests/gen.go create mode 100644 pkg/util/tests/inspector.go diff --git a/.gitignore b/.gitignore index d2a0b5929..7306e3369 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ vendor/ deps/ .vscode/ **/*.enterprise.go +**/*.enterprise.go **/enterprise/** enterprise.mk local/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a5222432e..293d966ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - (Feature) Add CRD Installer - (Bugfix) Assign imagePullSecrets to LocalStorage - (Update) Bump K8S API to 1.21.10 +- (Feature) (ACS) Add ACS handler ## [1.2.8](https://github.com/arangodb/kube-arangodb/tree/1.2.8) (2022-02-24) - Do not check License V2 on Community images diff --git a/chart/kube-arangodb/templates/deployment-operator/role.yaml b/chart/kube-arangodb/templates/deployment-operator/role.yaml index 5070bff67..217ded84a 100644 --- a/chart/kube-arangodb/templates/deployment-operator/role.yaml +++ b/chart/kube-arangodb/templates/deployment-operator/role.yaml @@ -14,7 +14,7 @@ metadata: release: {{ .Release.Name }} rules: - apiGroups: ["database.arangodb.com"] - resources: ["arangodeployments", "arangodeployments/status","arangomembers", "arangomembers/status"] + resources: ["arangodeployments", "arangodeployments/status","arangomembers", "arangomembers/status", "arangoclustersynchronizations", "arangoclustersynchronizations/status"] verbs: ["*"] - apiGroups: [""] resources: ["pods", "services", "endpoints", "persistentvolumeclaims", "events", "secrets", "serviceaccounts"] diff --git a/go.mod b/go.mod index 7fed31252..e50443547 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,6 @@ require ( github.com/arangodb/go-driver v1.2.1 github.com/arangodb/go-driver/v2 v2.0.0-20211021031401-d92dcd5a4c83 github.com/arangodb/go-upgrade-rules v0.0.0-20180809110947-031b4774ff21 - //github.com/arangodb/rebalancer v0.1.1 github.com/cenkalti/backoff v2.2.1+incompatible github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9 github.com/ghodss/yaml v1.0.0 @@ -55,11 +54,6 @@ require ( k8s.io/klog v1.0.0 ) -require ( - github.com/arangodb/rebalancer v0.1.1 - golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba -) - require ( github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -102,6 +96,7 @@ require ( golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 // indirect golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect golang.org/x/text v0.3.6 // indirect + golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.27.1 // indirect diff --git a/pkg/apis/deployment/v1/cluster_synchronization_spec.go b/pkg/apis/deployment/v1/cluster_synchronization_spec.go index b7d40295a..8599b313f 100644 --- a/pkg/apis/deployment/v1/cluster_synchronization_spec.go +++ b/pkg/apis/deployment/v1/cluster_synchronization_spec.go @@ -21,4 +21,12 @@ package v1 type ArangoClusterSynchronizationSpec struct { + DeploymentName string `json:"deploymentName,omitempty"` + KubeConfig *ArangoClusterSynchronizationKubeConfigSpec `json:"kubeconfig,omitempty"` +} + +type ArangoClusterSynchronizationKubeConfigSpec struct { + SecretName string `json:"secretName"` + SecretKey string `json:"secretKey"` + Namespace string `json:"namespace"` } diff --git a/pkg/apis/deployment/v1/cluster_synchronization_status.go b/pkg/apis/deployment/v1/cluster_synchronization_status.go index 3ba2d83f3..aaf61e666 100644 --- a/pkg/apis/deployment/v1/cluster_synchronization_status.go +++ b/pkg/apis/deployment/v1/cluster_synchronization_status.go @@ -20,5 +20,15 @@ package v1 +import "k8s.io/apimachinery/pkg/types" + type ArangoClusterSynchronizationStatus struct { + Deployment *ArangoClusterSynchronizationDeploymentStatus `json:"deployment,omitempty"` + Conditions ConditionList `json:"conditions,omitempty"` +} + +type ArangoClusterSynchronizationDeploymentStatus struct { + Name string `json:"name"` + Namespace string `json:"namespace"` + UID types.UID `json:"uid"` } diff --git a/pkg/apis/deployment/v1/zz_generated.deepcopy.go b/pkg/apis/deployment/v1/zz_generated.deepcopy.go index 145aa5c4f..8350c2dd8 100644 --- a/pkg/apis/deployment/v1/zz_generated.deepcopy.go +++ b/pkg/apis/deployment/v1/zz_generated.deepcopy.go @@ -67,8 +67,8 @@ func (in *ArangoClusterSynchronization) DeepCopyInto(out *ArangoClusterSynchroni *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) return } @@ -90,6 +90,38 @@ func (in *ArangoClusterSynchronization) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ArangoClusterSynchronizationDeploymentStatus) DeepCopyInto(out *ArangoClusterSynchronizationDeploymentStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoClusterSynchronizationDeploymentStatus. +func (in *ArangoClusterSynchronizationDeploymentStatus) DeepCopy() *ArangoClusterSynchronizationDeploymentStatus { + if in == nil { + return nil + } + out := new(ArangoClusterSynchronizationDeploymentStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ArangoClusterSynchronizationKubeConfigSpec) DeepCopyInto(out *ArangoClusterSynchronizationKubeConfigSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoClusterSynchronizationKubeConfigSpec. +func (in *ArangoClusterSynchronizationKubeConfigSpec) DeepCopy() *ArangoClusterSynchronizationKubeConfigSpec { + if in == nil { + return nil + } + out := new(ArangoClusterSynchronizationKubeConfigSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ArangoClusterSynchronizationList) DeepCopyInto(out *ArangoClusterSynchronizationList) { *out = *in @@ -126,6 +158,11 @@ func (in *ArangoClusterSynchronizationList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ArangoClusterSynchronizationSpec) DeepCopyInto(out *ArangoClusterSynchronizationSpec) { *out = *in + if in.KubeConfig != nil { + in, out := &in.KubeConfig, &out.KubeConfig + *out = new(ArangoClusterSynchronizationKubeConfigSpec) + **out = **in + } return } @@ -142,6 +179,18 @@ func (in *ArangoClusterSynchronizationSpec) DeepCopy() *ArangoClusterSynchroniza // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ArangoClusterSynchronizationStatus) DeepCopyInto(out *ArangoClusterSynchronizationStatus) { *out = *in + if in.Deployment != nil { + in, out := &in.Deployment, &out.Deployment + *out = new(ArangoClusterSynchronizationDeploymentStatus) + **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]) + } + } return } diff --git a/pkg/apis/deployment/v2alpha1/cluster_synchronization_spec.go b/pkg/apis/deployment/v2alpha1/cluster_synchronization_spec.go index d5b66f38b..d16f32609 100644 --- a/pkg/apis/deployment/v2alpha1/cluster_synchronization_spec.go +++ b/pkg/apis/deployment/v2alpha1/cluster_synchronization_spec.go @@ -21,4 +21,11 @@ package v2alpha1 type ArangoClusterSynchronizationSpec struct { + DeploymentName *string `json:"deploymentName,omitempty"` + KubeConfig *ArangoClusterSynchronizationKubeConfigSpec `json:"kubeconfig,omitempty"` +} + +type ArangoClusterSynchronizationKubeConfigSpec struct { + SecretName string `json:"secretName"` + Namespace string `json:"namespace"` } diff --git a/pkg/apis/deployment/v2alpha1/cluster_synchronization_status.go b/pkg/apis/deployment/v2alpha1/cluster_synchronization_status.go index a57e69756..ad84a5be1 100644 --- a/pkg/apis/deployment/v2alpha1/cluster_synchronization_status.go +++ b/pkg/apis/deployment/v2alpha1/cluster_synchronization_status.go @@ -20,5 +20,17 @@ package v2alpha1 +import "k8s.io/apimachinery/pkg/types" + type ArangoClusterSynchronizationStatus struct { + Deployment *ArangoClusterSynchronizationDeploymentStatus `json:"deployment,omitempty"` + KubeConfig *ArangoClusterSynchronizationKubeConfigStatus `json:"kubeconfig,omitempty"` +} + +type ArangoClusterSynchronizationDeploymentStatus struct { + UID types.UID `json:"UID"` +} + +type ArangoClusterSynchronizationKubeConfigStatus struct { + Conditions ConditionList `json:"conditions,omitempty"` } diff --git a/pkg/apis/deployment/v2alpha1/zz_generated.deepcopy.go b/pkg/apis/deployment/v2alpha1/zz_generated.deepcopy.go index 6ad7606f0..998d8fcb8 100644 --- a/pkg/apis/deployment/v2alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/deployment/v2alpha1/zz_generated.deepcopy.go @@ -67,8 +67,8 @@ func (in *ArangoClusterSynchronization) DeepCopyInto(out *ArangoClusterSynchroni *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) return } @@ -90,6 +90,61 @@ func (in *ArangoClusterSynchronization) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ArangoClusterSynchronizationDeploymentStatus) DeepCopyInto(out *ArangoClusterSynchronizationDeploymentStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoClusterSynchronizationDeploymentStatus. +func (in *ArangoClusterSynchronizationDeploymentStatus) DeepCopy() *ArangoClusterSynchronizationDeploymentStatus { + if in == nil { + return nil + } + out := new(ArangoClusterSynchronizationDeploymentStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ArangoClusterSynchronizationKubeConfigSpec) DeepCopyInto(out *ArangoClusterSynchronizationKubeConfigSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoClusterSynchronizationKubeConfigSpec. +func (in *ArangoClusterSynchronizationKubeConfigSpec) DeepCopy() *ArangoClusterSynchronizationKubeConfigSpec { + if in == nil { + return nil + } + out := new(ArangoClusterSynchronizationKubeConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ArangoClusterSynchronizationKubeConfigStatus) DeepCopyInto(out *ArangoClusterSynchronizationKubeConfigStatus) { + *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]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoClusterSynchronizationKubeConfigStatus. +func (in *ArangoClusterSynchronizationKubeConfigStatus) DeepCopy() *ArangoClusterSynchronizationKubeConfigStatus { + if in == nil { + return nil + } + out := new(ArangoClusterSynchronizationKubeConfigStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ArangoClusterSynchronizationList) DeepCopyInto(out *ArangoClusterSynchronizationList) { *out = *in @@ -126,6 +181,16 @@ func (in *ArangoClusterSynchronizationList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ArangoClusterSynchronizationSpec) DeepCopyInto(out *ArangoClusterSynchronizationSpec) { *out = *in + if in.DeploymentName != nil { + in, out := &in.DeploymentName, &out.DeploymentName + *out = new(string) + **out = **in + } + if in.KubeConfig != nil { + in, out := &in.KubeConfig, &out.KubeConfig + *out = new(ArangoClusterSynchronizationKubeConfigSpec) + **out = **in + } return } @@ -142,6 +207,16 @@ func (in *ArangoClusterSynchronizationSpec) DeepCopy() *ArangoClusterSynchroniza // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ArangoClusterSynchronizationStatus) DeepCopyInto(out *ArangoClusterSynchronizationStatus) { *out = *in + if in.Deployment != nil { + in, out := &in.Deployment, &out.Deployment + *out = new(ArangoClusterSynchronizationDeploymentStatus) + **out = **in + } + if in.KubeConfig != nil { + in, out := &in.KubeConfig, &out.KubeConfig + *out = new(ArangoClusterSynchronizationKubeConfigStatus) + (*in).DeepCopyInto(*out) + } return } diff --git a/pkg/deployment/acs/acs.community.go b/pkg/deployment/acs/acs.community.go new file mode 100644 index 000000000..a9f493927 --- /dev/null +++ b/pkg/deployment/acs/acs.community.go @@ -0,0 +1,34 @@ +// +// DISCLAIMER +// +// Copyright 2016-2022 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 +//go:build !enterprise +// +build !enterprise + +package acs + +import ( + "context" + + api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" + inspectorInterface "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector" + "github.com/arangodb/kube-arangodb/pkg/util/kclient" +) + +func Inspect(ctx context.Context, deployment *api.ArangoDeployment, client kclient.Client, cachedStatus inspectorInterface.Inspector) error { + return nil +} diff --git a/pkg/deployment/acs/sutil/conditions.go b/pkg/deployment/acs/sutil/conditions.go new file mode 100644 index 000000000..d557cc177 --- /dev/null +++ b/pkg/deployment/acs/sutil/conditions.go @@ -0,0 +1,28 @@ +// +// DISCLAIMER +// +// Copyright 2016-2022 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 sutil + +import api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" + +const ( + DeploymentReadyCondition api.ConditionType = "DeploymentReady" + KubernetesConnectedCondition api.ConditionType = "KubernetesConnected" +) diff --git a/pkg/deployment/deployment_inspector.go b/pkg/deployment/deployment_inspector.go index cafb9a603..97c8ed0ac 100644 --- a/pkg/deployment/deployment_inspector.go +++ b/pkg/deployment/deployment_inspector.go @@ -41,10 +41,11 @@ import ( "github.com/arangodb/kube-arangodb/pkg/apis/deployment" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" + "github.com/arangodb/kube-arangodb/pkg/deployment/acs" "github.com/arangodb/kube-arangodb/pkg/metrics" "github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + meta "k8s.io/apimachinery/pkg/apis/meta/v1" ) var ( @@ -84,7 +85,7 @@ func (d *Deployment) inspectDeployment(lastInterval util.Interval) util.Interval var updated *api.ArangoDeployment err = globals.GetGlobalTimeouts().Kubernetes().RunWithTimeout(ctxReconciliation, func(ctxChild context.Context) error { var err error - updated, err = d.deps.Client.Arango().DatabaseV1().ArangoDeployments(d.GetNamespace()).Get(ctxChild, deploymentName, metav1.GetOptions{}) + updated, err = d.deps.Client.Arango().DatabaseV1().ArangoDeployments(d.GetNamespace()).Get(ctxChild, deploymentName, meta.GetOptions{}) return err }) if k8sutil.IsNotFound(err) { @@ -174,6 +175,10 @@ func (d *Deployment) inspectDeploymentWithError(ctx context.Context, lastInterva } } + if err := acs.Inspect(ctx, d.apiObject, d.deps.Client, cachedStatus); err != nil { + d.deps.Log.Warn().Err(err).Msgf("Unable to handle ACS objects") + } + // Cleanup terminated pods on the beginning of loop if x, err := d.resources.CleanupTerminatedPods(ctx, cachedStatus); err != nil { return minInspectionInterval, errors.Wrapf(err, "Pod cleanup failed") diff --git a/pkg/deployment/resources/inspector/acs.go b/pkg/deployment/resources/inspector/acs.go index 700ec26bf..09b25885f 100644 --- a/pkg/deployment/resources/inspector/acs.go +++ b/pkg/deployment/resources/inspector/acs.go @@ -38,11 +38,11 @@ func (i *inspector) GetArangoClusterSynchronizations() (arangoclustersynchroniza i.lock.Lock() defer i.lock.Unlock() - if i.nodes == nil { + if i.acs == nil { return nil, false } - return i.acs, i.nodes.accessible + return i.acs, i.acs.accessible } type arangoClusterSynchronizationLoader struct { @@ -51,6 +51,28 @@ type arangoClusterSynchronizationLoader struct { acs map[string]*api.ArangoClusterSynchronization } +func (a *arangoClusterSynchronizationLoader) FilterArangoClusterSynchronizations(filters ...arangoclustersynchronization.Filter) []*api.ArangoClusterSynchronization { + q := make([]*api.ArangoClusterSynchronization, 0, len(a.acs)) + + for _, obj := range a.acs { + if a.filterArangoClusterSynchronizations(obj, filters...) { + q = append(q, obj) + } + } + + return q +} + +func (a *arangoClusterSynchronizationLoader) filterArangoClusterSynchronizations(obj *api.ArangoClusterSynchronization, filters ...arangoclustersynchronization.Filter) bool { + for _, f := range filters { + if !f(obj) { + return false + } + } + + return true +} + func (a *arangoClusterSynchronizationLoader) ArangoClusterSynchronizations() []*api.ArangoClusterSynchronization { var r []*api.ArangoClusterSynchronization for _, acs := range a.acs { @@ -157,12 +179,12 @@ func getArangoClusterSynchronizations(ctx context.Context, k versioned.Interface } if acss.Continue != "" { - nextNodeLayer, err := getArangoClusterSynchronizations(ctx, k, namespace, acss.Continue) + newACSLoader, err := getArangoClusterSynchronizations(ctx, k, namespace, acss.Continue) if err != nil { return nil, err } - return append(acss.Items, nextNodeLayer...), nil + return append(acss.Items, newACSLoader...), nil } return acss.Items, nil diff --git a/pkg/deployment/resources/inspector/inspector.go b/pkg/deployment/resources/inspector/inspector.go index 9f3c516fe..79731d31f 100644 --- a/pkg/deployment/resources/inspector/inspector.go +++ b/pkg/deployment/resources/inspector/inspector.go @@ -179,6 +179,7 @@ func (i *inspector) Refresh(ctx context.Context) error { i.serviceMonitors = new.serviceMonitors i.arangoMembers = new.arangoMembers i.nodes = new.nodes + i.acs = new.acs i.versionInfo = new.versionInfo return nil diff --git a/pkg/util/k8sutil/inspector/arangoclustersynchronization/node.go b/pkg/util/k8sutil/inspector/arangoclustersynchronization/node.go index 81731fd9f..754bab793 100644 --- a/pkg/util/k8sutil/inspector/arangoclustersynchronization/node.go +++ b/pkg/util/k8sutil/inspector/arangoclustersynchronization/node.go @@ -31,6 +31,7 @@ type Loader interface { type Inspector interface { ArangoClusterSynchronizations() []*api.ArangoClusterSynchronization ArangoClusterSynchronization(name string) (*api.ArangoClusterSynchronization, bool) + FilterArangoClusterSynchronizations(filters ...Filter) []*api.ArangoClusterSynchronization IterateArangoClusterSynchronizations(action Action, filters ...Filter) error ArangoClusterSynchronizationReadInterface() ReadInterface } diff --git a/pkg/util/kclient/client_factory.go b/pkg/util/kclient/client_factory.go index 0f3c79bb9..f61180f38 100644 --- a/pkg/util/kclient/client_factory.go +++ b/pkg/util/kclient/client_factory.go @@ -24,15 +24,11 @@ import ( "sync" "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned" - versionedFake "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/fake" "github.com/dchest/uniuri" "github.com/pkg/errors" monitoring "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned" - monitoringFake "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/fake" apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - apiextensionsclientFake "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake" "k8s.io/client-go/kubernetes" - kubernetesFake "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/rest" ) @@ -168,10 +164,8 @@ type Client interface { KubernetesExtensions() apiextensionsclient.Interface Arango() versioned.Interface Monitoring() monitoring.Interface -} -func NewFakeClient() Client { - return NewStaticClient(kubernetesFake.NewSimpleClientset(), apiextensionsclientFake.NewSimpleClientset(), versionedFake.NewSimpleClientset(), monitoringFake.NewSimpleClientset()) + Config() *rest.Config } func NewStaticClient(kubernetes kubernetes.Interface, kubernetesExtensions apiextensionsclient.Interface, arango versioned.Interface, monitoring monitoring.Interface) Client { @@ -186,6 +180,8 @@ func NewStaticClient(kubernetes kubernetes.Interface, kubernetesExtensions apiex func newClient(cfg *rest.Config) (*client, error) { var c client + c.config = cfg + if q, err := kubernetes.NewForConfig(cfg); err != nil { return nil, err } else { @@ -218,6 +214,11 @@ type client struct { kubernetesExtensions apiextensionsclient.Interface arango versioned.Interface monitoring monitoring.Interface + config *rest.Config +} + +func (c *client) Config() *rest.Config { + return c.config } func (c *client) Kubernetes() kubernetes.Interface { diff --git a/pkg/util/kclient/fake.go b/pkg/util/kclient/fake.go new file mode 100644 index 000000000..b73efd715 --- /dev/null +++ b/pkg/util/kclient/fake.go @@ -0,0 +1,101 @@ +// +// DISCLAIMER +// +// Copyright 2016-2022 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 kclient + +import ( + "sync" + + versionedFake "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/fake" + monitoringFake "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/fake" + apiextensionsclientFake "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake" + "k8s.io/apimachinery/pkg/runtime" + kubernetesFake "k8s.io/client-go/kubernetes/fake" +) + +func NewFakeClient() Client { + return NewStaticClient(kubernetesFake.NewSimpleClientset(), apiextensionsclientFake.NewSimpleClientset(), versionedFake.NewSimpleClientset(), monitoringFake.NewSimpleClientset()) +} + +type FakeClientBuilder interface { + Kubernetes(objects ...runtime.Object) FakeClientBuilder + KubernetesExtensions(objects ...runtime.Object) FakeClientBuilder + Arango(objects ...runtime.Object) FakeClientBuilder + Monitoring(objects ...runtime.Object) FakeClientBuilder + + Client() Client +} + +func NewFakeClientBuilder() FakeClientBuilder { + return &fakeClientBuilder{} +} + +type fakeClientBuilder struct { + lock sync.Mutex + + kubernetes []runtime.Object + kubernetesExtensions []runtime.Object + arango []runtime.Object + monitoring []runtime.Object +} + +func (f *fakeClientBuilder) Kubernetes(objects ...runtime.Object) FakeClientBuilder { + f.lock.Lock() + defer f.lock.Unlock() + + f.kubernetes = append(f.kubernetes, objects...) + + return f +} + +func (f *fakeClientBuilder) KubernetesExtensions(objects ...runtime.Object) FakeClientBuilder { + f.lock.Lock() + defer f.lock.Unlock() + + f.kubernetesExtensions = append(f.kubernetesExtensions, objects...) + + return f +} + +func (f *fakeClientBuilder) Arango(objects ...runtime.Object) FakeClientBuilder { + f.lock.Lock() + defer f.lock.Unlock() + + f.arango = append(f.arango, objects...) + + return f +} + +func (f *fakeClientBuilder) Monitoring(objects ...runtime.Object) FakeClientBuilder { + f.lock.Lock() + defer f.lock.Unlock() + + f.monitoring = append(f.monitoring, objects...) + + return f +} + +func (f *fakeClientBuilder) Client() Client { + return NewStaticClient( + kubernetesFake.NewSimpleClientset(f.kubernetes...), + apiextensionsclientFake.NewSimpleClientset(f.kubernetesExtensions...), + versionedFake.NewSimpleClientset(f.arango...), + monitoringFake.NewSimpleClientset(f.monitoring...)) +} diff --git a/pkg/util/kclient/helpers/secret.go b/pkg/util/kclient/helpers/secret.go new file mode 100644 index 000000000..7f2c78753 --- /dev/null +++ b/pkg/util/kclient/helpers/secret.go @@ -0,0 +1,51 @@ +// +// DISCLAIMER +// +// Copyright 2016-2022 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 helpers + +import ( + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/secret" + "github.com/arangodb/kube-arangodb/pkg/util/kclient" + "github.com/pkg/errors" + rest "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +func SecretConfigGetter(s secret.Inspector, name, key string) kclient.ConfigGetter { + return func() (*rest.Config, string, error) { + secret, ok := s.Secret(name) + if !ok { + return nil, "", errors.Errorf("Secret %s not found", name) + } + + v, ok := secret.Data[key] + if !ok { + return nil, "", errors.Errorf("Key %s/%s not found", name, key) + } + + cfg, err := clientcmd.RESTConfigFromKubeConfig(v) + if err != nil { + return nil, "", err + } + + return cfg, util.SHA256(v), nil + } +} diff --git a/pkg/util/kclient/helpers/secret_test.go b/pkg/util/kclient/helpers/secret_test.go new file mode 100644 index 000000000..7bcec2fca --- /dev/null +++ b/pkg/util/kclient/helpers/secret_test.go @@ -0,0 +1,131 @@ +// +// DISCLAIMER +// +// Copyright 2016-2022 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 helpers + +import ( + "context" + "testing" + + "github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector" + "github.com/arangodb/kube-arangodb/pkg/util/kclient" + "github.com/stretchr/testify/require" + core "k8s.io/api/core/v1" + meta "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func Test_SecretConfigGetter(t *testing.T) { + t.Run("Missing secret", func(t *testing.T) { + c := kclient.NewFakeClient() + + i, err := inspector.NewInspector(context.Background(), c, "default") + require.NoError(t, err) + + _, _, err = SecretConfigGetter(i, "secret", "key")() + require.EqualError(t, err, "Secret secret not found") + }) + + t.Run("Missing key", func(t *testing.T) { + c := kclient.NewFakeClient() + + s := core.Secret{ + ObjectMeta: meta.ObjectMeta{ + Name: "secret", + Namespace: "default", + }, + } + + _, err := c.Kubernetes().CoreV1().Secrets("default").Create(context.Background(), &s, meta.CreateOptions{}) + require.NoError(t, err) + + i, err := inspector.NewInspector(context.Background(), c, "default") + require.NoError(t, err) + + _, _, err = SecretConfigGetter(i, "secret", "key")() + require.EqualError(t, err, "Key secret/key not found") + }) + + t.Run("Invalid data", func(t *testing.T) { + c := kclient.NewFakeClient() + + s := core.Secret{ + ObjectMeta: meta.ObjectMeta{ + Name: "secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "key": []byte(` +random data +`), + }, + } + + _, err := c.Kubernetes().CoreV1().Secrets("default").Create(context.Background(), &s, meta.CreateOptions{}) + require.NoError(t, err) + + i, err := inspector.NewInspector(context.Background(), c, "default") + require.NoError(t, err) + + _, _, err = SecretConfigGetter(i, "secret", "key")() + require.Error(t, err, "Key secret/key not found") + }) + + t.Run("Valid data", func(t *testing.T) { + c := kclient.NewFakeClient() + + s := core.Secret{ + ObjectMeta: meta.ObjectMeta{ + Name: "secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "key": []byte(` +apiVersion: v1 +clusters: +- cluster: + server: https://localhost + name: test +contexts: +- context: + cluster: test + user: test + namespace: test + name: test +current-context: test +kind: Config +preferences: {} +users: +- name: test + user: + token: x +`), + }, + } + + _, err := c.Kubernetes().CoreV1().Secrets("default").Create(context.Background(), &s, meta.CreateOptions{}) + require.NoError(t, err) + + i, err := inspector.NewInspector(context.Background(), c, "default") + require.NoError(t, err) + + _, _, err = SecretConfigGetter(i, "secret", "key")() + require.NoError(t, err) + }) +} diff --git a/pkg/util/tests/gen.go b/pkg/util/tests/gen.go new file mode 100644 index 000000000..b024562d5 --- /dev/null +++ b/pkg/util/tests/gen.go @@ -0,0 +1,57 @@ +// +// DISCLAIMER +// +// Copyright 2016-2022 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 tests + +import ( + api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" + meta "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/uuid" + "github.com/arangodb/kube-arangodb/pkg/util/kclient" + "testing" + "context" + "github.com/stretchr/testify/require" +) + +func NewArangoDeployment(name string) *api.ArangoDeployment { + return &api.ArangoDeployment{ + ObjectMeta: meta.ObjectMeta{ + Name: name, + Namespace: FakeNamespace, + UID: uuid.NewUUID(), + }, + } +} + +func NewArangoClusterSynchronization(name string) *api.ArangoClusterSynchronization { + return &api.ArangoClusterSynchronization{ + ObjectMeta: meta.ObjectMeta{ + Name: name, + Namespace: FakeNamespace, + UID: uuid.NewUUID(), + }, + } +} + +func RefreshArangoClusterSynchronization(t *testing.T, client kclient.Client, acs *api.ArangoClusterSynchronization) *api.ArangoClusterSynchronization { + nacs, err := client.Arango().DatabaseV1().ArangoClusterSynchronizations(acs.GetNamespace()).Get(context.Background(), acs.GetName(), meta.GetOptions{}) + require.NoError(t, err) + return nacs +} diff --git a/pkg/util/tests/inspector.go b/pkg/util/tests/inspector.go new file mode 100644 index 000000000..ae24b1440 --- /dev/null +++ b/pkg/util/tests/inspector.go @@ -0,0 +1,39 @@ +// +// DISCLAIMER +// +// Copyright 2016-2022 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 tests + +import ( + "testing" + "github.com/arangodb/kube-arangodb/pkg/util/kclient" + "github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector" + "context" + "github.com/stretchr/testify/require" + inspectorInterface "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector" +) + +const FakeNamespace = "fake" + +func NewInspector(t *testing.T, c kclient.Client) inspectorInterface.Inspector { + i, err := inspector.NewInspector(context.Background(), c, FakeNamespace) + require.NoError(t, err) + + return i +}