From 367e59fe5b41afcc35d23cfe33b5437fced64010 Mon Sep 17 00:00:00 2001 From: Adam Janikowski <12255597+ajanikow@users.noreply.github.com> Date: Mon, 23 Sep 2024 12:27:59 +0200 Subject: [PATCH] [Feature] [Scheduler] Create Integration Profile (#1727) --- CHANGELOG.md | 1 + pkg/deployment/resources/arango_profiles.go | 129 ++++++++++++++++++ .../resources/pod_creator_gateway_pod.go | 27 +++- pkg/deployment/resources/resources.go | 3 +- .../inspector/arangoprofile/v1beta1/reader.go | 6 +- pkg/util/k8sutil/interfaces/pod_creator.go | 2 + 6 files changed, 157 insertions(+), 11 deletions(-) create mode 100644 pkg/deployment/resources/arango_profiles.go diff --git a/CHANGELOG.md b/CHANGELOG.md index df03d648f..8afd9abe6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ - (Feature) (Scheduler) Merge Strategy - (Feature) (Networking) Endpoints Destination - (Improvement) Improve Metrics Handling +- (Feature) (Scheduler) Create Integration Profile ## [1.2.42](https://github.com/arangodb/kube-arangodb/tree/1.2.42) (2024-07-23) - (Maintenance) Go 1.22.4 & Kubernetes 1.29.6 libraries diff --git a/pkg/deployment/resources/arango_profiles.go b/pkg/deployment/resources/arango_profiles.go new file mode 100644 index 000000000..99bab20e8 --- /dev/null +++ b/pkg/deployment/resources/arango_profiles.go @@ -0,0 +1,129 @@ +// +// DISCLAIMER +// +// Copyright 2024 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// + +package resources + +import ( + "context" + "fmt" + "time" + + meta "k8s.io/apimachinery/pkg/apis/meta/v1" + + schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1" + schedulerContainerResourcesApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/container/resources" + "github.com/arangodb/kube-arangodb/pkg/deployment/patch" + "github.com/arangodb/kube-arangodb/pkg/integrations/sidecar" + "github.com/arangodb/kube-arangodb/pkg/metrics" + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" + inspectorInterface "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/patcher" +) + +var ( + inspectedArangoProfilesCounters = metrics.MustRegisterCounterVec(metricsComponent, "inspected_arango_profiles", "Number of ArangoProfiles inspections per deployment", metrics.DeploymentName) + inspectArangoProfilesDurationGauges = metrics.MustRegisterGaugeVec(metricsComponent, "inspect_arango_profiles_duration", "Amount of time taken by a single inspection of all ArangoProfiles for a deployment (in sec)", metrics.DeploymentName) +) + +// EnsureArangoProfiles creates all ArangoProfiles needed to run the given deployment +func (r *Resources) EnsureArangoProfiles(ctx context.Context, cachedStatus inspectorInterface.Inspector) error { + start := time.Now() + spec := r.context.GetSpec() + arangoProfiles := cachedStatus.ArangoProfileModInterface().V1Beta1() + apiObject := r.context.GetAPIObject() + deploymentName := apiObject.GetName() + + defer metrics.SetDuration(inspectArangoProfilesDurationGauges.WithLabelValues(deploymentName), start) + counterMetric := inspectedArangoProfilesCounters.WithLabelValues(deploymentName) + + reconcileRequired := k8sutil.NewReconcile(cachedStatus) + + intName := fmt.Sprintf("%s-int", deploymentName) + + integration, err := sidecar.NewIntegration(&schedulerContainerResourcesApi.Image{ + Image: util.NewType(r.context.GetOperatorImage()), + }, spec.Integration.GetSidecar()) + if err != nil { + return err + } + + integrationChecksum, err := integration.Checksum() + if err != nil { + return err + } + + if c, err := cachedStatus.ArangoProfile().V1Beta1(); err == nil { + counterMetric.Inc() + if s, ok := c.GetSimple(intName); !ok { + s = &schedulerApi.ArangoProfile{ + ObjectMeta: meta.ObjectMeta{ + Name: intName, + Namespace: apiObject.GetNamespace(), + OwnerReferences: []meta.OwnerReference{ + apiObject.AsOwner(), + }, + }, + Spec: schedulerApi.ProfileSpec{ + Template: integration, + }, + } + + if _, err := cachedStatus.ArangoProfileModInterface().V1Beta1().Create(ctx, s, meta.CreateOptions{}); err != nil { + return err + } + + reconcileRequired.Required() + } else { + currChecksum, err := s.Spec.Template.Checksum() + if err != nil { + return err + } + + if s.Spec.Selectors != nil { + if _, changed, err := patcher.Patcher[*schedulerApi.ArangoProfile](ctx, arangoProfiles, s, meta.PatchOptions{}, + func(in *schedulerApi.ArangoProfile) []patch.Item { + return []patch.Item{ + patch.ItemRemove(patch.NewPath("spec", "selectors")), + } + }); err != nil { + return err + } else if changed { + reconcileRequired.Required() + } + } + + if currChecksum != integrationChecksum { + if _, changed, err := patcher.Patcher[*schedulerApi.ArangoProfile](ctx, arangoProfiles, s, meta.PatchOptions{}, + func(in *schedulerApi.ArangoProfile) []patch.Item { + return []patch.Item{ + patch.ItemReplace(patch.NewPath("spec", "template"), integration), + } + }); err != nil { + return err + } else if changed { + reconcileRequired.Required() + } + } + } + } + + return reconcileRequired.Reconcile(ctx) +} diff --git a/pkg/deployment/resources/pod_creator_gateway_pod.go b/pkg/deployment/resources/pod_creator_gateway_pod.go index 620d3d2c8..0bbf5f83b 100644 --- a/pkg/deployment/resources/pod_creator_gateway_pod.go +++ b/pkg/deployment/resources/pod_creator_gateway_pod.go @@ -29,13 +29,12 @@ import ( api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1" - schedulerContainerResourcesApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/container/resources" shared "github.com/arangodb/kube-arangodb/pkg/apis/shared" "github.com/arangodb/kube-arangodb/pkg/deployment/features" "github.com/arangodb/kube-arangodb/pkg/deployment/pod" "github.com/arangodb/kube-arangodb/pkg/integrations/sidecar" - "github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util/collection" + "github.com/arangodb/kube-arangodb/pkg/util/errors" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/interfaces" kresources "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/resources" @@ -203,6 +202,14 @@ func (m *MemberGatewayPod) Validate(cachedStatus interfaces.Inspector) error { return err } + if c, err := cachedStatus.ArangoProfile().V1Beta1(); err != nil { + return err + } else { + if _, ok := c.GetSimple(fmt.Sprintf("%s-int", m.context.GetName())); !ok { + return errors.Errorf("Unable to find deployment integration") + } + } + return nil } @@ -236,14 +243,20 @@ func (m *MemberGatewayPod) Labels() map[string]string { } func (m *MemberGatewayPod) Profiles() (schedulerApi.ProfileTemplates, error) { - integration, err := sidecar.NewIntegration(&schedulerContainerResourcesApi.Image{ - Image: util.NewType(m.resources.context.GetOperatorImage()), - }, m.spec.Integration.GetSidecar()) - + c, err := m.cachedStatus.ArangoProfile().V1Beta1() if err != nil { return nil, err } + integration, ok := c.GetSimple(fmt.Sprintf("%s-int", m.context.GetName())) + if !ok { + return nil, errors.Errorf("Unable to find deployment integration") + } + + if integration.Status.Accepted == nil { + return nil, errors.Errorf("Unable to find accepted integration integration") + } + integrations, err := sidecar.NewIntegrationEnablement( sidecar.IntegrationEnvoyV3{ Spec: m.spec, @@ -258,5 +271,5 @@ func (m *MemberGatewayPod) Profiles() (schedulerApi.ProfileTemplates, error) { shutdownAnnotation := sidecar.NewShutdownAnnotations([]string{shared.ServerContainerName}) - return []*schedulerApi.ProfileTemplate{integration, integrations, shutdownAnnotation}, nil + return []*schedulerApi.ProfileTemplate{integration.Status.Accepted.Template, integrations, shutdownAnnotation}, nil } diff --git a/pkg/deployment/resources/resources.go b/pkg/deployment/resources/resources.go index e4407c5bb..b233778b9 100644 --- a/pkg/deployment/resources/resources.go +++ b/pkg/deployment/resources/resources.go @@ -57,7 +57,8 @@ func (r *Resources) EnsureCoreResources(ctx context.Context, cachedStatus inspec errors.Section(r.EnsureArangoMembers(ctx, cachedStatus), "EnsureArangoMembers"), errors.Section(r.EnsureServices(ctx, cachedStatus), "EnsureServices"), errors.Section(r.EnsureConfigMaps(ctx, cachedStatus), "EnsureConfigMaps"), - errors.Section(r.EnsureSecrets(ctx, cachedStatus), "EnsureSecrets")) + errors.Section(r.EnsureSecrets(ctx, cachedStatus), "EnsureSecrets"), + errors.Section(r.EnsureArangoProfiles(ctx, cachedStatus), "EnsureArangoProfiles")) } func (r *Resources) EnsureResources(ctx context.Context, serviceMonitorEnabled bool, cachedStatus inspectorInterface.Inspector) error { diff --git a/pkg/util/k8sutil/inspector/arangoprofile/v1beta1/reader.go b/pkg/util/k8sutil/inspector/arangoprofile/v1beta1/reader.go index 1cc3a44cf..d0328fe5b 100644 --- a/pkg/util/k8sutil/inspector/arangoprofile/v1beta1/reader.go +++ b/pkg/util/k8sutil/inspector/arangoprofile/v1beta1/reader.go @@ -31,9 +31,9 @@ import ( // ModInterface has methods to work with ArangoTask resources only for creation type ModInterface interface { - Create(ctx context.Context, arangotask *schedulerApi.ArangoProfile, opts meta.CreateOptions) (*schedulerApi.ArangoProfile, error) - Update(ctx context.Context, arangotask *schedulerApi.ArangoProfile, opts meta.UpdateOptions) (*schedulerApi.ArangoProfile, error) - UpdateStatus(ctx context.Context, arangotask *schedulerApi.ArangoProfile, opts meta.UpdateOptions) (*schedulerApi.ArangoProfile, error) + Create(ctx context.Context, arangoProfile *schedulerApi.ArangoProfile, opts meta.CreateOptions) (*schedulerApi.ArangoProfile, error) + Update(ctx context.Context, arangoProfile *schedulerApi.ArangoProfile, opts meta.UpdateOptions) (*schedulerApi.ArangoProfile, error) + UpdateStatus(ctx context.Context, arangoProfile *schedulerApi.ArangoProfile, opts meta.UpdateOptions) (*schedulerApi.ArangoProfile, error) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts meta.PatchOptions, subresources ...string) (result *schedulerApi.ArangoProfile, err error) Delete(ctx context.Context, name string, opts meta.DeleteOptions) error } diff --git a/pkg/util/k8sutil/interfaces/pod_creator.go b/pkg/util/k8sutil/interfaces/pod_creator.go index 577302ede..171acd051 100644 --- a/pkg/util/k8sutil/interfaces/pod_creator.go +++ b/pkg/util/k8sutil/interfaces/pod_creator.go @@ -26,6 +26,7 @@ import ( core "k8s.io/api/core/v1" schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/arangoprofile" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/configmap" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/secret" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/service" @@ -35,6 +36,7 @@ type Inspector interface { secret.Inspector service.Inspector configmap.Inspector + arangoprofile.Inspector } type PodModifier interface {