From 3c6368b82aaa0b8e8b11decb5ef5432ab53b4c70 Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Fri, 31 Oct 2025 08:53:02 +0000 Subject: [PATCH 1/2] [Feature] Simplify Operator ID --- CHANGELOG.md | 1 + README.md | 1 - cmd/cmd.go | 77 ++++++---- cmd/cmd_pod_test.go | 132 ++++++++++-------- docs/cli/arangodb_operator.md | 1 - integrations/scheduler/v2/suite_test.go | 3 +- pkg/deployment/context_impl.go | 2 +- pkg/deployment/deployment.go | 2 +- pkg/deployment/deployment_core_test.go | 8 +- pkg/deployment/deployment_encryption_test.go | 4 +- pkg/deployment/deployment_pod_sync_test.go | 22 +-- pkg/deployment/deployment_run_test.go | 4 +- pkg/deployment/resources/context.go | 3 +- pkg/handlers/job/job_suite_test.go | 3 +- pkg/handlers/networking/route/suite_test.go | 3 +- pkg/handlers/platform/chart/suite_test.go | 5 +- pkg/handlers/platform/service/suite_test.go | 3 +- pkg/handlers/platform/storage/suite_test.go | 5 +- pkg/handlers/scheduler/batchjob/suite_test.go | 5 +- pkg/handlers/scheduler/cronjob/suite_test.go | 5 +- .../scheduler/deployment/suite_test.go | 5 +- pkg/handlers/scheduler/pod/suite_test.go | 5 +- pkg/handlers/scheduler/profile/suite_test.go | 5 +- pkg/operator/operator.go | 4 +- pkg/operator/operator_deployment.go | 2 +- pkg/operatorV2/operator.go | 9 +- pkg/operatorV2/operator_suite_test.go | 15 +- pkg/operatorV2/operator_test.go | 23 ++- pkg/operatorV2/operator_worker_test.go | 19 ++- pkg/platform/package_test.go | 2 +- pkg/util/image.go | 34 +++++ pkg/util/tests/equal.go | 38 +++++ pkg/util/tests/operator.go | 3 +- 33 files changed, 292 insertions(+), 161 deletions(-) create mode 100644 pkg/util/image.go create mode 100644 pkg/util/tests/equal.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 59294ac01..2e796f494 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - (Maintenance) Bump Dependencies - (Feature) (Platform) EventsV1 Integration - (Feature) (Platform) Allows to opt out in the Inventory Telemetry +- (Feature) Simplify Operator ID Process ## [1.3.1](https://github.com/arangodb/kube-arangodb/tree/1.3.1) (2025-10-07) - (Documentation) Add ArangoPlatformStorage Docs & Examples diff --git a/README.md b/README.md index 71ac0cfdd..c7c3d6abc 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,6 @@ Flags: --http1.transport.keep-alive-timeout-short duration Interval between keep-alive probes for an active network connection (default 100ms) --http1.transport.max-idle-conns int Maximum number of idle (keep-alive) connections across all hosts. Zero means no limit (default 100) --http1.transport.tls-handshake-timeout duration Maximum amount of time to wait for a TLS handshake. Zero means no timeout (default 10s) - --image.discovery.status Discover Operator Image from Pod Status by default. When disabled Pod Spec is used. (default true) --image.discovery.timeout duration Timeout for image discovery process (default 1m0s) --internal.scaling-integration Enable Scaling Integration --kubernetes.burst int Burst for the k8s API (default 256) diff --git a/cmd/cmd.go b/cmd/cmd.go index 7c795f23d..4039d8eec 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -162,7 +162,8 @@ var ( podSchedulingGracePeriod time.Duration } operatorImageDiscovery struct { - timeout time.Duration + timeout time.Duration + //deprecated: Do not use this flag, as discovery method changed defaultStatusDiscovery bool } operatorReconciliationRetry struct { @@ -191,6 +192,12 @@ var ( ) func init() { + if err := initE(); err != nil { + panic(err.Error()) + } +} + +func initE() error { var deprecatedStr string f := cmdMain.Flags() @@ -217,20 +224,14 @@ func init() { f.BoolVar(&operatorOptions.enablePlatform, "operator.platform", false, "Enable to run the Platform operator") f.BoolVar(&operatorOptions.enableScheduler, "operator.scheduler", false, "Enable to run the Scheduler operator") f.BoolVar(&operatorOptions.enableK2KClusterSync, "operator.k2k-cluster-sync", false, "Enable to run the ListSimple operator") - f.MarkDeprecated("operator.k2k-cluster-sync", "Enabled within deployment operator") f.BoolVar(&operatorOptions.versionOnly, "operator.version", false, "Enable only version endpoint in Operator") f.StringVar(&deprecatedStr, "operator.alpine-image", "alpine:3.7", "Docker image used for alpine containers") - f.MarkDeprecated("operator.alpine-image", "Value is not used anymore") f.StringVar(&deprecatedStr, "operator.metrics-exporter-image", "arangodb/arangodb-exporter:0.1.6", "Docker image used for metrics containers by default") - f.MarkDeprecated("operator.metrics-exporter-image", "Value is not used anymore") f.StringVar(&deprecatedStr, "operator.arango-image", "arangodb/arangodb:latest", "Docker image used for arango by default") - f.MarkDeprecated("operator.arango-image", "Value is not used anymore") f.BoolVar(&chaosOptions.allowed, "chaos.allowed", false, "Set to allow chaos in deployments. Only activated when allowed and enabled in deployment") f.BoolVar(&operatorOptions.skipLeaderLabel, "leader.label.skip", false, "Skips Leader Label for the Pod") f.BoolVar(&operatorOptions.singleMode, "mode.single", false, "Enable single mode in Operator. WARNING: There should be only one replica of Operator, otherwise Operator can take unexpected actions") f.String("scope", "", "Define scope on which Operator works. Legacy - pre 1.1.0 scope with limited cluster access") - f.MarkDeprecated("scope", "Value is not used anymore") - f.MarkHidden("scope") f.DurationVar(&operatorTimeouts.k8s, "timeout.k8s", globals.DefaultKubernetesTimeout, "The request timeout to the kubernetes") f.DurationVar(&operatorTimeouts.arangoD, "timeout.arangod", globals.DefaultArangoDTimeout, "The request timeout to the ArangoDB") f.DurationVar(&operatorTimeouts.arangoDCheck, "timeout.arangod-check", globals.DefaultArangoDCheckTimeout, "The version check request timeout to the ArangoDB") @@ -260,21 +261,35 @@ func init() { f.BoolVar(&operatorImageDiscovery.defaultStatusDiscovery, "image.discovery.status", true, "Discover Operator Image from Pod Status by default. When disabled Pod Spec is used.") f.DurationVar(&operatorImageDiscovery.timeout, "image.discovery.timeout", time.Minute, "Timeout for image discovery process") f.IntVar(&threads, "threads", 16, "Number of the worker threads") - if err := logging.Init(&cmdMain); err != nil { - panic(err.Error()) - } - if err := features.Init(&cmdMain); err != nil { - panic(err.Error()) - } - if err := agencyConfig.Init(&cmdMain); err != nil { - panic(err.Error()) + + if err := errors.Errors( + f.MarkDeprecated("operator.k2k-cluster-sync", "Enabled within deployment operator"), + f.MarkDeprecated("operator.alpine-image", "Value is not used anymore"), + f.MarkDeprecated("operator.metrics-exporter-image", "Value is not used anymore"), + f.MarkDeprecated("operator.arango-image", "Value is not used anymore"), + f.MarkDeprecated("scope", "Value is not used anymore"), + f.MarkDeprecated("image.discovery.status", "Value fetched from the Operator Spec"), + ); err != nil { + return errors.Wrap(err, "Unable to mark flags as deprecated") } - if err := reconcile.ActionsConfigGlobal.Init(&cmdMain); err != nil { - panic(err.Error()) + + if err := errors.Errors( + f.MarkHidden("scope"), + ); err != nil { + return errors.Wrap(err, "Unable to mark flags as hidden") } - if err := operatorHTTP.InitConfiguration(&cmdMain); err != nil { - panic(err.Error()) + + if err := errors.Errors( + logging.Init(&cmdMain), + features.Init(&cmdMain), + agencyConfig.Init(&cmdMain), + reconcile.ActionsConfigGlobal.Init(&cmdMain), + operatorHTTP.InitConfiguration(&cmdMain), + ); err != nil { + return errors.Wrap(err, "Unable to register secondary commands") } + + return nil } func Command() *cobra.Command { @@ -590,7 +605,7 @@ func newOperatorConfigAndDeps(id, namespace, name string) (operator.Config, oper Namespace: namespace, PodName: name, ServiceAccount: serviceAccount, - OperatorImage: image, + Image: image, SkipLeaderLabel: operatorOptions.skipLeaderLabel, EnableDeployment: operatorOptions.enableDeployment, EnableDeploymentReplication: operatorOptions.enableDeploymentReplication, @@ -633,18 +648,24 @@ func newOperatorConfigAndDeps(id, namespace, name string) (operator.Config, oper // getMyPodInfo looks up the image & service account of the pod with given name in given namespace // Returns image, serviceAccount, error. -func getMyPodInfo(kubecli kubernetes.Interface, namespace, name string) (string, string, error) { - if image, sa, ok := getMyPodInfoWrap(kubecli, namespace, name, getMyImageInfoFunc(operatorImageDiscovery.defaultStatusDiscovery)); ok { - return image, sa, nil - } +func getMyPodInfo(kubecli kubernetes.Interface, namespace, name string) (util.Image, string, error) { + var ret util.Image + var serviceAccount string - logger.Warn("Unable to discover image, fallback to second method") + if image, sa, ok := getMyPodInfoWrap(kubecli, namespace, name, getMyImageInfoFunc(false)); !ok { + return util.Image{}, "", errors.Errorf("Unable to discover Operator image from Spec") + } else { + ret.Image = image + serviceAccount = sa + } - if image, sa, ok := getMyPodInfoWrap(kubecli, namespace, name, getMyImageInfoFunc(!operatorImageDiscovery.defaultStatusDiscovery)); ok { - return image, sa, nil + if image, _, ok := getMyPodInfoWrap(kubecli, namespace, name, getMyImageInfoFunc(true)); ok { + ret.StatusImage = util.NewType(image) + } else { + logger.Warn("Unable to discover image from status") } - return "", "", errors.Errorf("Unable to discover image") + return ret, serviceAccount, nil } func getMyPodInfoWrap(kubecli kubernetes.Interface, namespace, name string, imageFunc func(in *core.Pod) (string, bool)) (string, string, bool) { diff --git a/cmd/cmd_pod_test.go b/cmd/cmd_pod_test.go index b6ed9a9f9..214ed710c 100644 --- a/cmd/cmd_pod_test.go +++ b/cmd/cmd_pod_test.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2024 ArangoDB GmbH, Cologne, Germany +// Copyright 2025 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. @@ -41,49 +41,52 @@ func Test_PodDiscovery(t *testing.T) { Pod core.Pod - Image, ServiceAccount string + Image util.Image - Valid bool + ServiceAccount string - DefaultStatusDiscovery *bool + Valid bool } var testCases = []testCase{ + //{ + // Name: "Empty pod", + // Valid: false, + //}, + //{ + // Name: "Not allowed containers", + // Valid: false, + // Pod: core.Pod{ + // ObjectMeta: meta.ObjectMeta{ + // Name: "operator", + // Namespace: tests.FakeNamespace, + // }, + // Spec: core.PodSpec{ + // Containers: []core.Container{ + // { + // Name: "unknown", + // Image: "image1", + // }, + // }, + // }, + // Status: core.PodStatus{ + // ContainerStatuses: []core.ContainerStatus{ + // { + // Name: "unknown", + // Image: "image1", + // ImageID: "image1", + // }, + // }, + // }, + // }, + //}, { - Name: "Empty pod", - Valid: false, - }, - { - Name: "Not allowed containers", - Valid: false, - Pod: core.Pod{ - ObjectMeta: meta.ObjectMeta{ - Name: "operator", - Namespace: tests.FakeNamespace, - }, - Spec: core.PodSpec{ - Containers: []core.Container{ - { - Name: "unknown", - Image: "image1", - }, - }, - }, - Status: core.PodStatus{ - ContainerStatuses: []core.ContainerStatus{ - { - Name: "unknown", - Image: "image1", - ImageID: "image1", - }, - }, - }, + Name: "Allowed Status & Spec", + Valid: true, + Image: util.Image{ + Image: "image1", + StatusImage: util.NewType("image1"), }, - }, - { - Name: "Allowed Status & Spec", - Valid: true, - Image: "image1", ServiceAccount: "sa", Pod: core.Pod{ ObjectMeta: meta.ObjectMeta{ @@ -111,9 +114,12 @@ func Test_PodDiscovery(t *testing.T) { }, }, { - Name: "Allowed Status & Spec", - Valid: true, - Image: "imageStatusID1", + Name: "Allowed Status & Spec", + Valid: true, + Image: util.Image{ + Image: "imageSpec1", + StatusImage: util.NewType("imageStatusID1"), + }, ServiceAccount: "sa", Pod: core.Pod{ ObjectMeta: meta.ObjectMeta{ @@ -141,11 +147,13 @@ func Test_PodDiscovery(t *testing.T) { }, }, { - Name: "Allowed Status & Spec - From Spec", - Valid: true, - Image: "imageSpec1", - ServiceAccount: "sa", - DefaultStatusDiscovery: util.NewType(false), + Name: "Allowed Status & Spec - From Spec", + Valid: true, + Image: util.Image{ + Image: "imageSpec1", + StatusImage: util.NewType("imageStatusID1"), + }, + ServiceAccount: "sa", Pod: core.Pod{ ObjectMeta: meta.ObjectMeta{ Name: "operator", @@ -172,9 +180,11 @@ func Test_PodDiscovery(t *testing.T) { }, }, { - Name: "Allowed Spec", - Valid: true, - Image: "imageSpec1", + Name: "Allowed Spec", + Valid: true, + Image: util.Image{ + Image: "imageSpec1", + }, ServiceAccount: "sa", Pod: core.Pod{ ObjectMeta: meta.ObjectMeta{ @@ -193,9 +203,12 @@ func Test_PodDiscovery(t *testing.T) { }, }, { - Name: "Allowed Status & Spec - From Second Pod", - Valid: true, - Image: "imageStatusID2", + Name: "Allowed Status & Spec - From Second Pod", + Valid: true, + Image: util.Image{ + Image: "imageSpec2", + StatusImage: util.NewType("imageStatusID2"), + }, ServiceAccount: "sa", Pod: core.Pod{ ObjectMeta: meta.ObjectMeta{ @@ -232,11 +245,13 @@ func Test_PodDiscovery(t *testing.T) { }, }, { - Name: "Allowed Status & Spec - From Second Pod Spec", - Valid: true, - Image: "imageSpec2", - ServiceAccount: "sa", - DefaultStatusDiscovery: util.NewType(false), + Name: "Allowed Status & Spec - From Second Pod Spec", + Valid: true, + Image: util.Image{ + Image: "imageSpec2", + StatusImage: util.NewType("imageStatusID2"), + }, + ServiceAccount: "sa", Pod: core.Pod{ ObjectMeta: meta.ObjectMeta{ Name: "operator", @@ -275,8 +290,6 @@ func Test_PodDiscovery(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.Name, func(t *testing.T) { - operatorImageDiscovery.defaultStatusDiscovery = util.TypeOrDefault(testCase.DefaultStatusDiscovery, true) - c := kclient.NewFakeClientBuilder().Add(&testCase.Pod).Client() image, sa, err := getMyPodInfo(c.Kubernetes(), tests.FakeNamespace, "operator") @@ -286,7 +299,8 @@ func Test_PodDiscovery(t *testing.T) { require.Empty(t, sa) } else { require.NoError(t, err) - require.Equal(t, testCase.Image, image) + require.Equal(t, testCase.Image.Image, image.Image) + tests.EqualPointers(t, testCase.Image.StatusImage, image.StatusImage) require.Equal(t, testCase.ServiceAccount, sa) } }) diff --git a/docs/cli/arangodb_operator.md b/docs/cli/arangodb_operator.md index 7e3a7a169..07d6ff4cf 100644 --- a/docs/cli/arangodb_operator.md +++ b/docs/cli/arangodb_operator.md @@ -78,7 +78,6 @@ Flags: --http1.transport.keep-alive-timeout-short duration Interval between keep-alive probes for an active network connection (default 100ms) --http1.transport.max-idle-conns int Maximum number of idle (keep-alive) connections across all hosts. Zero means no limit (default 100) --http1.transport.tls-handshake-timeout duration Maximum amount of time to wait for a TLS handshake. Zero means no timeout (default 10s) - --image.discovery.status Discover Operator Image from Pod Status by default. When disabled Pod Spec is used. (default true) --image.discovery.timeout duration Timeout for image discovery process (default 1m0s) --internal.scaling-integration Enable Scaling Integration --kubernetes.burst int Burst for the k8s API (default 256) diff --git a/integrations/scheduler/v2/suite_test.go b/integrations/scheduler/v2/suite_test.go index daaa9ac80..5b5a342e2 100644 --- a/integrations/scheduler/v2/suite_test.go +++ b/integrations/scheduler/v2/suite_test.go @@ -32,6 +32,7 @@ import ( "github.com/arangodb/kube-arangodb/pkg/logging" operator "github.com/arangodb/kube-arangodb/pkg/operatorV2" "github.com/arangodb/kube-arangodb/pkg/operatorV2/event" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/helm" "github.com/arangodb/kube-arangodb/pkg/util/kclient" "github.com/arangodb/kube-arangodb/pkg/util/kclient/external" @@ -102,7 +103,7 @@ func MockClient(t *testing.T, ctx context.Context, mods ...Mod) (pbSchedulerV2.S } func chartHandler(client kclient.Client, ns string) operator.Handler { - op := operator.NewOperator("mock", ns, "mock") + op := operator.NewOperator("mock", ns, util.Image{Image: "mock"}) recorder := event.NewEventRecorder("mock", client.Kubernetes()) return chart.Handler(op, recorder, client) diff --git a/pkg/deployment/context_impl.go b/pkg/deployment/context_impl.go index b4d404722..107ef1a3d 100644 --- a/pkg/deployment/context_impl.go +++ b/pkg/deployment/context_impl.go @@ -84,7 +84,7 @@ func (d *Deployment) GetServerGroupIterator() reconciler.ServerGroupIterator { } func (d *Deployment) GetOperatorImage() string { - return d.config.OperatorImage + return d.config.Image.Get(d.GetSpec().ImageDiscoveryMode.Get() == api.DeploymentImageDiscoveryKubeletMode) } // GetNamespace returns the kubernetes namespace that contains diff --git a/pkg/deployment/deployment.go b/pkg/deployment/deployment.go index de03ec1e8..61d50910d 100644 --- a/pkg/deployment/deployment.go +++ b/pkg/deployment/deployment.go @@ -71,7 +71,7 @@ type Config struct { ServiceAccount string AllowChaos bool ScalingIntegrationEnabled bool - OperatorImage string + Image util.Image ReconciliationDelay time.Duration } diff --git a/pkg/deployment/deployment_core_test.go b/pkg/deployment/deployment_core_test.go index 0da57bb2b..48d8a8b14 100644 --- a/pkg/deployment/deployment_core_test.go +++ b/pkg/deployment/deployment_core_test.go @@ -459,7 +459,7 @@ func TestEnsurePod_ArangoDB_Core(t *testing.T) { }, }, config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, Helper: func(t *testing.T, deployment *Deployment, testCase *testCaseStruct) { deployment.currentObjectStatus = &api.DeploymentStatus{ @@ -690,7 +690,7 @@ func TestEnsurePod_ArangoDB_Core(t *testing.T) { }, }, config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, Helper: func(t *testing.T, deployment *Deployment, testCase *testCaseStruct) { deployment.currentObjectStatus = &api.DeploymentStatus{ @@ -1237,7 +1237,7 @@ func TestEnsurePod_ArangoDB_Core(t *testing.T) { testCase.ExpectedPod.Spec.Containers[0].LivenessProbe = createTestLivenessProbe(httpProbe, false, authorization, shared.ServerPortName) }, config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, ExpectedEvent: "member dbserver is created", ExpectedPod: core.Pod{ @@ -1307,7 +1307,7 @@ func TestEnsurePod_ArangoDB_Core(t *testing.T) { testCase.ExpectedPod.Spec.Containers[0].LivenessProbe = createTestLivenessProbe(httpProbe, false, authorization, shared.ServerPortName) }, config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, ExpectedEvent: "member dbserver is created", ExpectedPod: core.Pod{ diff --git a/pkg/deployment/deployment_encryption_test.go b/pkg/deployment/deployment_encryption_test.go index a68eaa2ef..45015ae62 100644 --- a/pkg/deployment/deployment_encryption_test.go +++ b/pkg/deployment/deployment_encryption_test.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2025 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. @@ -132,7 +132,7 @@ func TestEnsurePod_ArangoDB_Encryption(t *testing.T) { testCase.ExpectedPod.Spec.Containers[0].LivenessProbe = createTestLivenessProbe(httpProbe, true, authorization, shared.ServerPortName) }, config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, ExpectedEvent: "member dbserver is created", ExpectedPod: core.Pod{ diff --git a/pkg/deployment/deployment_pod_sync_test.go b/pkg/deployment/deployment_pod_sync_test.go index b93496cd3..b5cb1a0cd 100644 --- a/pkg/deployment/deployment_pod_sync_test.go +++ b/pkg/deployment/deployment_pod_sync_test.go @@ -289,7 +289,7 @@ func TestEnsurePod_Sync_Master(t *testing.T) { DropInit: true, Name: "Sync Master Pod with lifecycle, license, monitoring without authentication and alpine", config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, ArangoDeployment: &api.ArangoDeployment{ Spec: api.DeploymentSpec{ @@ -377,7 +377,7 @@ func TestEnsurePod_Sync_Master(t *testing.T) { DropInit: true, Name: "Sync Master Pod alias - existing service, ClusterIP and valid name", config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, ArangoDeployment: &api.ArangoDeployment{ Spec: api.DeploymentSpec{ @@ -489,7 +489,7 @@ func TestEnsurePod_Sync_Master(t *testing.T) { DropInit: true, Name: "Sync Master Pod alias - missing service and valid name", config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, ArangoDeployment: &api.ArangoDeployment{ Spec: api.DeploymentSpec{ @@ -582,7 +582,7 @@ func TestEnsurePod_Sync_Master(t *testing.T) { DropInit: true, Name: "Sync Master Pod alias - existing service, missing ClusterIP and valid name", config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, ArangoDeployment: &api.ArangoDeployment{ Spec: api.DeploymentSpec{ @@ -683,7 +683,7 @@ func TestEnsurePod_Sync_Master(t *testing.T) { DropInit: true, Name: "Sync Master Pod alias - existing service, ClusterIP and invalid name", config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, ArangoDeployment: &api.ArangoDeployment{ Spec: api.DeploymentSpec{ @@ -787,7 +787,7 @@ func TestEnsurePod_Sync_Master(t *testing.T) { DropInit: true, Name: "Sync Master Pod alias - existing service, ClusterIP and missing name", config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, ArangoDeployment: &api.ArangoDeployment{ Spec: api.DeploymentSpec{ @@ -886,7 +886,7 @@ func TestEnsurePod_Sync_Master(t *testing.T) { DropInit: true, Name: "Sync Master Pod alias - existing service, ClusterIP and valid names", config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, ArangoDeployment: &api.ArangoDeployment{ Spec: api.DeploymentSpec{ @@ -1002,7 +1002,7 @@ func TestEnsurePod_Sync_Master(t *testing.T) { DropInit: true, Name: "Sync Master Pod alias - existing service, ClusterIP and valid names with different ports", config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, ArangoDeployment: &api.ArangoDeployment{ Spec: api.DeploymentSpec{ @@ -1116,7 +1116,7 @@ func TestEnsurePod_Sync_Master(t *testing.T) { DropInit: true, Name: "Sync Master Pod alias - existing service, ClusterIP and mixed names", config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, ArangoDeployment: &api.ArangoDeployment{ Spec: api.DeploymentSpec{ @@ -1239,7 +1239,7 @@ func TestEnsurePod_Sync_Worker(t *testing.T) { Name: "Sync Worker Pod with monitoring, service account, node selector, lifecycle, license " + "liveness probe, priority class name, resource requirements without alpine", config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, ArangoDeployment: &api.ArangoDeployment{ Spec: api.DeploymentSpec{ @@ -1334,7 +1334,7 @@ func TestEnsurePod_Sync_Worker(t *testing.T) { InitContainersCopyResources: util.NewType(false), }, config: Config{ - OperatorImage: testImageOperator, + Image: util.Image{Image: testImageOperator}, }, ArangoDeployment: &api.ArangoDeployment{ Spec: api.DeploymentSpec{ diff --git a/pkg/deployment/deployment_run_test.go b/pkg/deployment/deployment_run_test.go index cf5d09ef2..2a224a88f 100644 --- a/pkg/deployment/deployment_run_test.go +++ b/pkg/deployment/deployment_run_test.go @@ -54,8 +54,8 @@ func runTestCases(t *testing.T, testCases ...testCaseStruct) { func runTestCase(t *testing.T, testCase testCaseStruct) { t.Run(testCase.Name, func(t *testing.T) { // Arrange - if testCase.config.OperatorImage == "" { - testCase.config.OperatorImage = testImageOperator + if testCase.config.Image.Image == "" { + testCase.config.Image.Image = testImageOperator } d, eventRecorder := createTestDeployment(t, testCase.config, testCase.ArangoDeployment) diff --git a/pkg/deployment/resources/context.go b/pkg/deployment/resources/context.go index 02400cae4..e2ff8aa60 100644 --- a/pkg/deployment/resources/context.go +++ b/pkg/deployment/resources/context.go @@ -49,7 +49,8 @@ type Context interface { // GetServerGroupIterator returns the deployment as ServerGroupIterator. GetServerGroupIterator() reconciler.ServerGroupIterator - // GetOperatorImage returns the image name of operator image + // GetOperatorImage returns the full image name (including tag or hash) of the operator image used by the deployment. + // This may include the image hash if specified, or the image tag otherwise. GetOperatorImage() string // CreateEvent creates a given event. // On error, the error is logged. diff --git a/pkg/handlers/job/job_suite_test.go b/pkg/handlers/job/job_suite_test.go index 707fe80c4..e9d36ba07 100644 --- a/pkg/handlers/job/job_suite_test.go +++ b/pkg/handlers/job/job_suite_test.go @@ -39,6 +39,7 @@ import ( operator "github.com/arangodb/kube-arangodb/pkg/operatorV2" "github.com/arangodb/kube-arangodb/pkg/operatorV2/event" "github.com/arangodb/kube-arangodb/pkg/operatorV2/operation" + "github.com/arangodb/kube-arangodb/pkg/util" ) func newFakeHandler() *handler { @@ -49,7 +50,7 @@ func newFakeHandler() *handler { client: f, kubeClient: k, eventRecorder: newEventInstance(event.NewEventRecorder("mock", k)), - operator: operator.NewOperator("mock", "mock", "mock"), + operator: operator.NewOperator("mock", "mock", util.Image{Image: "mock"}), } return h diff --git a/pkg/handlers/networking/route/suite_test.go b/pkg/handlers/networking/route/suite_test.go index fb4ade712..d18fa5407 100644 --- a/pkg/handlers/networking/route/suite_test.go +++ b/pkg/handlers/networking/route/suite_test.go @@ -29,6 +29,7 @@ import ( operator "github.com/arangodb/kube-arangodb/pkg/operatorV2" "github.com/arangodb/kube-arangodb/pkg/operatorV2/event" "github.com/arangodb/kube-arangodb/pkg/operatorV2/operation" + "github.com/arangodb/kube-arangodb/pkg/util" ) func newFakeHandler() *handler { @@ -39,7 +40,7 @@ func newFakeHandler() *handler { client: f, kubeClient: k, eventRecorder: event.NewEventRecorder("mock", k).NewInstance(Group(), Version(), Kind()), - operator: operator.NewOperator("mock", "mock", "mock"), + operator: operator.NewOperator("mock", "mock", util.Image{Image: "mock"}), } return h diff --git a/pkg/handlers/platform/chart/suite_test.go b/pkg/handlers/platform/chart/suite_test.go index 45f1fe57f..118f4d844 100644 --- a/pkg/handlers/platform/chart/suite_test.go +++ b/pkg/handlers/platform/chart/suite_test.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2024 ArangoDB GmbH, Cologne, Germany +// Copyright 2024-2025 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. @@ -31,6 +31,7 @@ import ( operator "github.com/arangodb/kube-arangodb/pkg/operatorV2" "github.com/arangodb/kube-arangodb/pkg/operatorV2/event" "github.com/arangodb/kube-arangodb/pkg/operatorV2/operation" + "github.com/arangodb/kube-arangodb/pkg/util" ) func newFakeHandler() *handler { @@ -41,7 +42,7 @@ func newFakeHandler() *handler { client: f, kubeClient: k, eventRecorder: event.NewEventRecorder("mock", k).NewInstance(Group(), Version(), Kind()), - operator: operator.NewOperator("mock", "mock", "mock"), + operator: operator.NewOperator("mock", "mock", util.Image{Image: "mock"}), } return h diff --git a/pkg/handlers/platform/service/suite_test.go b/pkg/handlers/platform/service/suite_test.go index 8709371f2..3a847333a 100644 --- a/pkg/handlers/platform/service/suite_test.go +++ b/pkg/handlers/platform/service/suite_test.go @@ -32,6 +32,7 @@ import ( operator "github.com/arangodb/kube-arangodb/pkg/operatorV2" "github.com/arangodb/kube-arangodb/pkg/operatorV2/event" "github.com/arangodb/kube-arangodb/pkg/operatorV2/operation" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/helm" "github.com/arangodb/kube-arangodb/pkg/util/kclient/external" ) @@ -39,7 +40,7 @@ import ( func newFakeHandler(t *testing.T) (*handler, string, operator.Handler) { client, ns := external.ExternalClient(t) - op := operator.NewOperator("mock", ns, "mock") + op := operator.NewOperator("mock", ns, util.Image{Image: "mock"}) recorder := event.NewEventRecorder("mock", client.Kubernetes()) h, err := helm.NewClient(helm.Configuration{ Namespace: ns, diff --git a/pkg/handlers/platform/storage/suite_test.go b/pkg/handlers/platform/storage/suite_test.go index 9b36fcc87..9a3e652ef 100644 --- a/pkg/handlers/platform/storage/suite_test.go +++ b/pkg/handlers/platform/storage/suite_test.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2024 ArangoDB GmbH, Cologne, Germany +// Copyright 2024-2025 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. @@ -29,6 +29,7 @@ import ( operator "github.com/arangodb/kube-arangodb/pkg/operatorV2" "github.com/arangodb/kube-arangodb/pkg/operatorV2/event" "github.com/arangodb/kube-arangodb/pkg/operatorV2/operation" + "github.com/arangodb/kube-arangodb/pkg/util" ) func newFakeHandler() *handler { @@ -39,7 +40,7 @@ func newFakeHandler() *handler { client: f, kubeClient: k, eventRecorder: event.NewEventRecorder("mock", k).NewInstance(Group(), Version(), Kind()), - operator: operator.NewOperator("mock", "mock", "mock"), + operator: operator.NewOperator("mock", "mock", util.Image{Image: "mock"}), } return h diff --git a/pkg/handlers/scheduler/batchjob/suite_test.go b/pkg/handlers/scheduler/batchjob/suite_test.go index 749aa5009..7dd5279e4 100644 --- a/pkg/handlers/scheduler/batchjob/suite_test.go +++ b/pkg/handlers/scheduler/batchjob/suite_test.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2024 ArangoDB GmbH, Cologne, Germany +// Copyright 2024-2025 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. @@ -29,6 +29,7 @@ import ( operator "github.com/arangodb/kube-arangodb/pkg/operatorV2" "github.com/arangodb/kube-arangodb/pkg/operatorV2/event" "github.com/arangodb/kube-arangodb/pkg/operatorV2/operation" + "github.com/arangodb/kube-arangodb/pkg/util" ) func newFakeHandler() *handler { @@ -39,7 +40,7 @@ func newFakeHandler() *handler { client: f, kubeClient: k, eventRecorder: event.NewEventRecorder("mock", k).NewInstance(Group(), Version(), Kind()), - operator: operator.NewOperator("mock", "mock", "mock"), + operator: operator.NewOperator("mock", "mock", util.Image{Image: "mock"}), } h.init() diff --git a/pkg/handlers/scheduler/cronjob/suite_test.go b/pkg/handlers/scheduler/cronjob/suite_test.go index 5be3356c0..2bf56f755 100644 --- a/pkg/handlers/scheduler/cronjob/suite_test.go +++ b/pkg/handlers/scheduler/cronjob/suite_test.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2024 ArangoDB GmbH, Cologne, Germany +// Copyright 2024-2025 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. @@ -29,6 +29,7 @@ import ( operator "github.com/arangodb/kube-arangodb/pkg/operatorV2" "github.com/arangodb/kube-arangodb/pkg/operatorV2/event" "github.com/arangodb/kube-arangodb/pkg/operatorV2/operation" + "github.com/arangodb/kube-arangodb/pkg/util" ) func newFakeHandler() *handler { @@ -39,7 +40,7 @@ func newFakeHandler() *handler { client: f, kubeClient: k, eventRecorder: event.NewEventRecorder("mock", k).NewInstance(Group(), Version(), Kind()), - operator: operator.NewOperator("mock", "mock", "mock"), + operator: operator.NewOperator("mock", "mock", util.Image{Image: "mock"}), } h.init() diff --git a/pkg/handlers/scheduler/deployment/suite_test.go b/pkg/handlers/scheduler/deployment/suite_test.go index f0b5b69ca..6e269b60b 100644 --- a/pkg/handlers/scheduler/deployment/suite_test.go +++ b/pkg/handlers/scheduler/deployment/suite_test.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2024 ArangoDB GmbH, Cologne, Germany +// Copyright 2024-2025 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. @@ -29,6 +29,7 @@ import ( operator "github.com/arangodb/kube-arangodb/pkg/operatorV2" "github.com/arangodb/kube-arangodb/pkg/operatorV2/event" "github.com/arangodb/kube-arangodb/pkg/operatorV2/operation" + "github.com/arangodb/kube-arangodb/pkg/util" ) func newFakeHandler() *handler { @@ -39,7 +40,7 @@ func newFakeHandler() *handler { client: f, kubeClient: k, eventRecorder: event.NewEventRecorder("mock", k).NewInstance(Group(), Version(), Kind()), - operator: operator.NewOperator("mock", "mock", "mock"), + operator: operator.NewOperator("mock", "mock", util.Image{Image: "mock"}), } h.init() diff --git a/pkg/handlers/scheduler/pod/suite_test.go b/pkg/handlers/scheduler/pod/suite_test.go index bdbb1150e..b8a1032bd 100644 --- a/pkg/handlers/scheduler/pod/suite_test.go +++ b/pkg/handlers/scheduler/pod/suite_test.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2024 ArangoDB GmbH, Cologne, Germany +// Copyright 2024-2025 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. @@ -29,6 +29,7 @@ import ( operator "github.com/arangodb/kube-arangodb/pkg/operatorV2" "github.com/arangodb/kube-arangodb/pkg/operatorV2/event" "github.com/arangodb/kube-arangodb/pkg/operatorV2/operation" + "github.com/arangodb/kube-arangodb/pkg/util" ) func newFakeHandler() *handler { @@ -39,7 +40,7 @@ func newFakeHandler() *handler { client: f, kubeClient: k, eventRecorder: event.NewEventRecorder("mock", k).NewInstance(Group(), Version(), Kind()), - operator: operator.NewOperator("mock", "mock", "mock"), + operator: operator.NewOperator("mock", "mock", util.Image{Image: "mock"}), } h.init() diff --git a/pkg/handlers/scheduler/profile/suite_test.go b/pkg/handlers/scheduler/profile/suite_test.go index 6e5eeb32b..6e0768374 100644 --- a/pkg/handlers/scheduler/profile/suite_test.go +++ b/pkg/handlers/scheduler/profile/suite_test.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2024 ArangoDB GmbH, Cologne, Germany +// Copyright 2024-2025 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. @@ -29,6 +29,7 @@ import ( operator "github.com/arangodb/kube-arangodb/pkg/operatorV2" "github.com/arangodb/kube-arangodb/pkg/operatorV2/event" "github.com/arangodb/kube-arangodb/pkg/operatorV2/operation" + "github.com/arangodb/kube-arangodb/pkg/util" ) func newFakeHandler() *handler { @@ -39,7 +40,7 @@ func newFakeHandler() *handler { client: f, kubeClient: k, eventRecorder: event.NewEventRecorder("mock", k).NewInstance(Group(), Version(), Kind()), - operator: operator.NewOperator("mock", "mock", "mock"), + operator: operator.NewOperator("mock", "mock", util.Image{Image: "mock"}), } h.init() diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go index 49e83c59c..42ef63d74 100644 --- a/pkg/operator/operator.go +++ b/pkg/operator/operator.go @@ -109,7 +109,7 @@ type Config struct { Namespace string PodName string ServiceAccount string - OperatorImage string + Image util.Image SkipLeaderLabel bool EnableDeployment bool EnableDeploymentReplication bool @@ -321,7 +321,7 @@ func (o *Operator) onStartScheduler(stop <-chan struct{}) { // onStartOperatorV2 run the operatorV2 type func (o *Operator) onStartOperatorV2(operatorType operatorV2type, stop <-chan struct{}) { operatorName := fmt.Sprintf("arangodb-%s-operator", operatorType) - operator := operatorV2.NewOperator(operatorName, o.Namespace, o.OperatorImage) + operator := operatorV2.NewOperator(operatorName, o.Namespace, o.Image) util.Rand().Seed(time.Now().Unix()) diff --git a/pkg/operator/operator_deployment.go b/pkg/operator/operator_deployment.go index d35f7826e..1aecfc842 100644 --- a/pkg/operator/operator_deployment.go +++ b/pkg/operator/operator_deployment.go @@ -197,7 +197,7 @@ func (o *Operator) handleDeploymentEvent(event *Event) error { func (o *Operator) makeDeploymentConfigAndDeps() (deployment.Config, deployment.Dependencies) { cfg := deployment.Config{ ServiceAccount: o.Config.ServiceAccount, - OperatorImage: o.Config.OperatorImage, + Image: o.Config.Image, AllowChaos: o.Config.AllowChaos, ScalingIntegrationEnabled: o.Config.ScalingIntegrationEnabled, ReconciliationDelay: o.Config.ReconciliationDelay, diff --git a/pkg/operatorV2/operator.go b/pkg/operatorV2/operator.go index 6d3ce483c..b7bb6fb9e 100644 --- a/pkg/operatorV2/operator.go +++ b/pkg/operatorV2/operator.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2025 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. @@ -30,6 +30,7 @@ import ( "k8s.io/client-go/util/workqueue" "github.com/arangodb/kube-arangodb/pkg/operatorV2/operation" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util/errors" ) @@ -58,7 +59,7 @@ type Operator interface { } // NewOperator creates new operator -func NewOperator(name, namespace, image string) Operator { +func NewOperator(name, namespace string, image util.Image) Operator { o := &operator{ name: name, namespace: namespace, @@ -81,7 +82,7 @@ type operator struct { name string namespace string - image string + image util.Image informers []cache.SharedInformer starters []Starter @@ -102,7 +103,7 @@ func (o *operator) Name() string { } func (o *operator) Image() string { - return o.image + return o.image.Get(true) } func (o *operator) ProcessItem(item operation.Item) error { diff --git a/pkg/operatorV2/operator_suite_test.go b/pkg/operatorV2/operator_suite_test.go index 3994fb967..30683941c 100644 --- a/pkg/operatorV2/operator_suite_test.go +++ b/pkg/operatorV2/operator_suite_test.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2025 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. @@ -26,6 +26,7 @@ import ( "time" "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/util/uuid" "github.com/arangodb/kube-arangodb/pkg/operatorV2/operation" "github.com/arangodb/kube-arangodb/pkg/util" @@ -117,3 +118,15 @@ func waitForItems(t *testing.T, i <-chan operation.Item, expectedSize int) []ope } } } + +func newImage(hash bool) (string, util.Image) { + var i util.Image + + i.Image = string(uuid.NewUUID()) + + if hash { + i.StatusImage = util.NewType(string(uuid.NewUUID())) + } + + return string(uuid.NewUUID()), i +} diff --git a/pkg/operatorV2/operator_test.go b/pkg/operatorV2/operator_test.go index de6ed98ac..91c125e36 100644 --- a/pkg/operatorV2/operator_test.go +++ b/pkg/operatorV2/operator_test.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2025 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. @@ -29,7 +29,6 @@ import ( "github.com/stretchr/testify/require" core "k8s.io/api/core/v1" meta "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" @@ -38,8 +37,8 @@ import ( func Test_Operator_InformerProcessing(t *testing.T) { // Arrange - name := string(uuid.NewUUID()) - o := NewOperator(name, name, name) + name, image := newImage(false) + o := NewOperator(name, name, image) size := 64 objects := make([]string, size) @@ -87,8 +86,8 @@ func Test_Operator_InformerProcessing(t *testing.T) { func Test_Operator_MultipleInformers(t *testing.T) { // Arrange - name := string(uuid.NewUUID()) - o := NewOperator(name, name, name) + name, image := newImage(false) + o := NewOperator(name, name, image) size := 16 objects := make([]string, size) @@ -148,8 +147,8 @@ func Test_Operator_MultipleInformers(t *testing.T) { func Test_Operator_MultipleInformers_IgnoredTypes(t *testing.T) { // Arrange - name := string(uuid.NewUUID()) - o := NewOperator(name, name, name) + name, image := newImage(false) + o := NewOperator(name, name, image) size := 16 objects := make([]string, size) @@ -208,8 +207,8 @@ func Test_Operator_MultipleInformers_IgnoredTypes(t *testing.T) { func Test_Operator_MultipleInformers_MultipleHandlers(t *testing.T) { // Arrange - name := string(uuid.NewUUID()) - o := NewOperator(name, name, name) + name, image := newImage(true) + o := NewOperator(name, name, image) size := 16 objects := make([]string, size) @@ -316,8 +315,8 @@ func Test_Operator_MultipleInformers_MultipleHandlers(t *testing.T) { func Test_Operator_InformerProcessing_Namespaced(t *testing.T) { // Arrange - name := string(uuid.NewUUID()) - o := NewOperator(name, name, name) + name, image := newImage(false) + o := NewOperator(name, name, image) size := 16 objects := make([]string, size) diff --git a/pkg/operatorV2/operator_worker_test.go b/pkg/operatorV2/operator_worker_test.go index 6f970ed8c..0a2698c58 100644 --- a/pkg/operatorV2/operator_worker_test.go +++ b/pkg/operatorV2/operator_worker_test.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2025 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. @@ -25,13 +25,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "k8s.io/apimachinery/pkg/util/uuid" ) func Test_Worker_Empty(t *testing.T) { // Arrange - name := string(uuid.NewUUID()) - o := NewOperator(name, name, name) + name, image := newImage(false) + o := NewOperator(name, name, image) stopCh := make(chan struct{}) @@ -50,8 +49,8 @@ func Test_Worker_Empty(t *testing.T) { func Test_Worker_CatchAll(t *testing.T) { // Arrange - name := string(uuid.NewUUID()) - o := NewOperator(name, name, name) + name, image := newImage(false) + o := NewOperator(name, name, image) stopCh := make(chan struct{}) @@ -79,8 +78,8 @@ func Test_Worker_CatchAll(t *testing.T) { func Test_Worker_EnsureFirstProcessStopLoop(t *testing.T) { // Arrange - name := string(uuid.NewUUID()) - o := NewOperator(name, name, name) + name, image := newImage(false) + o := NewOperator(name, name, image) stopCh := make(chan struct{}) @@ -113,8 +112,8 @@ func Test_Worker_EnsureFirstProcessStopLoop(t *testing.T) { func Test_Worker_EnsureObjectIsProcessedBySecondHandler(t *testing.T) { // Arrange - name := string(uuid.NewUUID()) - o := NewOperator(name, name, name) + name, image := newImage(false) + o := NewOperator(name, name, image) stopCh := make(chan struct{}) diff --git a/pkg/platform/package_test.go b/pkg/platform/package_test.go index b4ac59efd..3408c8e57 100644 --- a/pkg/platform/package_test.go +++ b/pkg/platform/package_test.go @@ -223,7 +223,7 @@ func EnsureRegistry(t *testing.T, client kclient.Client, ns string) string { func Test_Package(t *testing.T) { client, ns := external.ExternalClient(t) - defer operator.NewTestingOperator(shutdown.Context(), t, ns, "operator:latest", client, platformChart.RegisterInformer, platformService.RegisterInformer)() + defer operator.NewTestingOperator(shutdown.Context(), t, ns, util.Image{Image: "operator:latest"}, client, platformChart.RegisterInformer, platformService.RegisterInformer)() deployment := tests.NewMetaObject[*api.ArangoDeployment](t, ns, "example", func(t *testing.T, obj *api.ArangoDeployment) {}) diff --git a/pkg/util/image.go b/pkg/util/image.go new file mode 100644 index 000000000..76038ceae --- /dev/null +++ b/pkg/util/image.go @@ -0,0 +1,34 @@ +// +// DISCLAIMER +// +// Copyright 2025 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 util + +type Image struct { + Image string + StatusImage *string +} + +func (i Image) Get(hash bool) string { + if i.StatusImage == nil || !hash { + return i.Image + } + + return *i.StatusImage +} diff --git a/pkg/util/tests/equal.go b/pkg/util/tests/equal.go new file mode 100644 index 000000000..d0d9ac0d5 --- /dev/null +++ b/pkg/util/tests/equal.go @@ -0,0 +1,38 @@ +// +// DISCLAIMER +// +// Copyright 2025 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 testing + +package tests + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func EqualPointers[A any](t *testing.T, a, b *A) { + if a == nil { + require.Nil(t, b, "Both objects expected to be nil") + } else { + require.NotNil(t, b, "Both objects expected not to be nil") + require.EqualValues(t, *a, *b) + } +} diff --git a/pkg/util/tests/operator.go b/pkg/util/tests/operator.go index 708d39990..a6868e976 100644 --- a/pkg/util/tests/operator.go +++ b/pkg/util/tests/operator.go @@ -33,12 +33,13 @@ import ( arangoInformer "github.com/arangodb/kube-arangodb/pkg/generated/informers/externalversions" operator "github.com/arangodb/kube-arangodb/pkg/operatorV2" "github.com/arangodb/kube-arangodb/pkg/operatorV2/event" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util/kclient" ) type TestingOperatorRegisterer func(operator operator.Operator, recorder event.Recorder, client kclient.Client, informer arangoInformer.SharedInformerFactory, kubeInformer informers.SharedInformerFactory) error -func NewTestingOperator(ctx context.Context, t *testing.T, ns, image string, client kclient.Client, registers ...TestingOperatorRegisterer) context.CancelFunc { +func NewTestingOperator(ctx context.Context, t *testing.T, ns string, image util.Image, client kclient.Client, registers ...TestingOperatorRegisterer) context.CancelFunc { nctx, c := context.WithCancel(ctx) operator := operator.NewOperator("test", ns, image) From c07c1e4b7c7ca7684525b1c83c12c0f71b9b6306 Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Fri, 31 Oct 2025 13:32:19 +0000 Subject: [PATCH 2/2] Iter --- cmd/cmd.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index 4039d8eec..00b759794 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -163,7 +163,7 @@ var ( } operatorImageDiscovery struct { timeout time.Duration - //deprecated: Do not use this flag, as discovery method changed + // Deprecated: Do not use this flag, as discovery method changed defaultStatusDiscovery bool } operatorReconciliationRetry struct { @@ -258,7 +258,7 @@ func initE() error { f.IntVar(&operatorBackup.concurrentUploads, "backup-concurrent-uploads", globals.DefaultBackupConcurrentUploads, "Number of concurrent uploads per deployment") f.Uint64Var(&memoryLimit.hardLimit, "memory-limit", 0, "Define memory limit for hard shutdown and the dump of goroutines. Used for testing") f.StringArrayVar(&metricsOptions.excludedMetricPrefixes, "metrics.excluded-prefixes", nil, "List of the excluded metrics prefixes") - f.BoolVar(&operatorImageDiscovery.defaultStatusDiscovery, "image.discovery.status", true, "Discover Operator Image from Pod Status by default. When disabled Pod Spec is used.") + f.BoolVar(&operatorImageDiscovery.defaultStatusDiscovery, "image.discovery.status", true, "Image discovery method is now determined by the deployment's ImageDiscoveryMode specification") f.DurationVar(&operatorImageDiscovery.timeout, "image.discovery.timeout", time.Minute, "Timeout for image discovery process") f.IntVar(&threads, "threads", 16, "Number of the worker threads") @@ -275,6 +275,7 @@ func initE() error { if err := errors.Errors( f.MarkHidden("scope"), + f.MarkHidden("image.discovery.status"), ); err != nil { return errors.Wrap(err, "Unable to mark flags as hidden") }