Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
80 changes: 51 additions & 29 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -191,6 +192,12 @@ var (
)

func init() {
if err := initE(); err != nil {
panic(err.Error())
}
}

func initE() error {
var deprecatedStr string

f := cmdMain.Flags()
Expand All @@ -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")
Expand All @@ -257,24 +258,39 @@ func init() {
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")
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"),
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deprecation message 'Value fetched from the Operator Spec' is unclear. It doesn't explain what users should do or why the flag is deprecated. Consider a more descriptive message like 'Image discovery method is now determined by the deployment's ImageDiscoveryMode specification' to help users understand the change.

Suggested change
f.MarkDeprecated("image.discovery.status", "Value fetched from the Operator Spec"),
f.MarkDeprecated("image.discovery.status", "Image discovery method is now determined by the deployment's ImageDiscoveryMode specification"),

Copilot uses AI. Check for mistakes.
); 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"),
f.MarkHidden("image.discovery.status"),
); 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 {
Expand Down Expand Up @@ -590,7 +606,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,
Expand Down Expand Up @@ -633,18 +649,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) {
Expand Down
132 changes: 73 additions & 59 deletions cmd/cmd_pod_test.go
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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",
Expand All @@ -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{
Expand All @@ -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{
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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")

Expand All @@ -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)
}
})
Expand Down
1 change: 0 additions & 1 deletion docs/cli/arangodb_operator.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion integrations/scheduler/v2/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion pkg/deployment/context_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pkg/deployment/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ type Config struct {
ServiceAccount string
AllowChaos bool
ScalingIntegrationEnabled bool
OperatorImage string
Image util.Image
ReconciliationDelay time.Duration
}

Expand Down
Loading