Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ metadata:
capabilities: Deep Insights
console.openshift.io/plugins: '["gitops-plugin"]'
containerImage: quay.io/redhat-developer/gitops-operator
createdAt: "2026-07-31T05:20:33Z"
createdAt: "2026-08-14T10:12:23Z"
description: Enables teams to adopt GitOps principles for managing cluster configurations
and application delivery across hybrid multi-cluster Kubernetes environments.
features.operators.openshift.io/disconnected: "true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sequential

import (
"context"
"fmt"
"strings"

. "github.com/onsi/ginkgo/v2"
Expand Down Expand Up @@ -152,6 +153,36 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() {
}
verifyResourceConstraints(k8sClient, "gitops-plugin", expectedReq, expectedLim)
verifyResourceConstraints(k8sClient, "cluster", expectedReq, expectedLim)
//below code needs to be verified only on and above 4.22 cluster, because apiserver CR will not having tls parameters below 4.22 OCP version
var major, minor int
_, err := fmt.Sscanf(ocVersion, "%d.%d", &major, &minor)
Expect(err).NotTo(HaveOccurred())

if major > 4 || (major == 4 && minor >= 22) {
depl = &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster",
Namespace: "openshift-gitops",
},
}

Expect(depl).To(k8sFixture.ExistByName())
Expect(depl.Spec.Template.Spec.Containers).NotTo(BeEmpty())

container := depl.Spec.Template.Spec.Containers[0]
env := container.Env

Expect(env).To(ContainElement(corev1.EnvVar{
Name: "TLS_MIN_VERSION",
Value: "1.2",
}))

Expect(env).To(ContainElement(corev1.EnvVar{
Name: "TLS_CIPHER_SUITES",
Value: "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305",
}))
}

})

It("validates that GitOpsService can update resource constraints", func() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
/*
Copyright 2025.

Licensed under the Apache License, Version 2.0 (the "License");
*/

package sequential

import (
"context"
"fmt"
"os"
"time"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"

argov1beta1api "github.com/argoproj-labs/argocd-operator/api/v1beta1"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"

"sigs.k8s.io/controller-runtime/pkg/client"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"github.com/argoproj-labs/argocd-operator/tests/ginkgo/fixture"
osFixture "github.com/argoproj-labs/argocd-operator/tests/ginkgo/fixture/os"
"github.com/argoproj-labs/argocd-operator/tests/ginkgo/fixture/utils"
)

var _ = Describe("Validate Deployment Env Args For TLS Configuration", func() {
const (
argocdNamespace = "test-tls-argocd"
argocdInstanceName = "example-argocd"
)
var (
c client.Client
ctx context.Context
)
BeforeEach(func() {
fixture.EnsureSequentialCleanSlate()
c, _ = utils.GetE2ETestKubeClient()
ctx = context.Background()
})
BeforeEach(func() {
if fixture.EnvLocalRun() {
Skip("This test is known not to work when running gitops operator locally")
}
})
// --- Helper: Extract TLS values from args ---
getTLSValues := func(args []string) (min string, hasMin bool, hasCiphers bool, ciphers string) {
for i := 0; i < len(args); i++ {
arg := args[i]
// handle --tlsminversion <value>
if arg == "--tlsminversion" {
hasMin = true
if i+1 < len(args) {
min = args[i+1]
}
}
if arg == "--tlsciphers" {
hasCiphers = true
if i+1 < len(args) {
ciphers = args[i+1]
}
}
// handle --tlsminversion=value
if len(arg) > len("--tlsminversion=") && arg[:len("--tlsminversion=")] == "--tlsminversion=" {
hasMin = true
min = arg[len("--tlsminversion="):]
}
if len(arg) > len("--tlsciphers=") && arg[:len("--tlsciphers=")] == "--tlsciphers=" {
hasCiphers = true
ciphers = arg[len("--tlsciphers="):]
}
}
return
}

Context("When the ArgoCD instance is created with default TLS settings", func() {
It("should validate default TLS values and updates on RepoServer, Server and Redis Deployments", func() {
ocVersion := getOCPVersion()
Expect(ocVersion).ToNot(BeEmpty())

var major, minor int
_, err := fmt.Sscanf(ocVersion, "%d.%d", &major, &minor)
Expect(err).NotTo(HaveOccurred())

if major < 4 || (major == 4 && minor < 22) {
Skip(fmt.Sprintf("skipping this test as OCP version is %s, requires OCP >= 4.22", ocVersion))
return
}
By("creating namespace")
ns := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: argocdNamespace,
},
}
Expect(c.Create(ctx, ns)).To(Succeed())

By("generating a test certificate to use with redis, using openssl")
redis_crt_File, err := os.CreateTemp("", "redis.crt")
Expect(err).ToNot(HaveOccurred())

redis_key_File, err := os.CreateTemp("", "redis.key")
Expect(err).ToNot(HaveOccurred())

openssl_test_File, err := os.CreateTemp("", "openssl_test.cnf")
Expect(err).ToNot(HaveOccurred())

opensslTestCNFContents := "\n[SAN]\nsubjectAltName=DNS:argocd-redis." + argocdNamespace + ".svc.cluster.local\n[req]\ndistinguished_name=req"

err = os.WriteFile(openssl_test_File.Name(), ([]byte)(opensslTestCNFContents), 0666)
Expect(err).ToNot(HaveOccurred())
Comment on lines +104 to +116

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Close the temp files and restrict the config file mode.

os.CreateTemp returns open file handles that are never closed, and mode 0666 makes the OpenSSL config world-writable. The private key file is also written by openssl with the default umask.

🔒 Proposed fix
 			redis_crt_File, err := os.CreateTemp("", "redis.crt")
 			Expect(err).ToNot(HaveOccurred())
+			Expect(redis_crt_File.Close()).To(Succeed())
 
 			redis_key_File, err := os.CreateTemp("", "redis.key")
 			Expect(err).ToNot(HaveOccurred())
+			Expect(redis_key_File.Close()).To(Succeed())
 
 			openssl_test_File, err := os.CreateTemp("", "openssl_test.cnf")
 			Expect(err).ToNot(HaveOccurred())
+			Expect(openssl_test_File.Close()).To(Succeed())
 
 			opensslTestCNFContents := "\n[SAN]\nsubjectAltName=DNS:argocd-redis." + argocdNamespace + ".svc.cluster.local\n[req]\ndistinguished_name=req"
 
-			err = os.WriteFile(openssl_test_File.Name(), ([]byte)(opensslTestCNFContents), 0666)
+			err = os.WriteFile(openssl_test_File.Name(), ([]byte)(opensslTestCNFContents), 0600)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
redis_crt_File, err := os.CreateTemp("", "redis.crt")
Expect(err).ToNot(HaveOccurred())
redis_key_File, err := os.CreateTemp("", "redis.key")
Expect(err).ToNot(HaveOccurred())
openssl_test_File, err := os.CreateTemp("", "openssl_test.cnf")
Expect(err).ToNot(HaveOccurred())
opensslTestCNFContents := "\n[SAN]\nsubjectAltName=DNS:argocd-redis." + argocdNamespace + ".svc.cluster.local\n[req]\ndistinguished_name=req"
err = os.WriteFile(openssl_test_File.Name(), ([]byte)(opensslTestCNFContents), 0666)
Expect(err).ToNot(HaveOccurred())
redis_crt_File, err := os.CreateTemp("", "redis.crt")
Expect(err).ToNot(HaveOccurred())
Expect(redis_crt_File.Close()).To(Succeed())
redis_key_File, err := os.CreateTemp("", "redis.key")
Expect(err).ToNot(HaveOccurred())
Expect(redis_key_File.Close()).To(Succeed())
openssl_test_File, err := os.CreateTemp("", "openssl_test.cnf")
Expect(err).ToNot(HaveOccurred())
Expect(openssl_test_File.Close()).To(Succeed())
opensslTestCNFContents := "\n[SAN]\nsubjectAltName=DNS:argocd-redis." + argocdNamespace + ".svc.cluster.local\n[req]\ndistinguished_name=req"
err = os.WriteFile(openssl_test_File.Name(), ([]byte)(opensslTestCNFContents), 0600)
Expect(err).ToNot(HaveOccurred())
🧰 Tools
🪛 ast-grep (0.45.1)

[warning] 114-114: File mode grants world-writable permission; restrict the mode so other users cannot modify the file (e.g. 0o644 / 0o600).
Context: os.WriteFile(openssl_test_File.Name(), ([]byte)(opensslTestCNFContents), 0666)
Note: [CWE-276] Incorrect Default Permissions.

(world-writable-chmod-go)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@test/openshift/e2e/ginkgo/sequential/1-143_validate_deployment_Env_Args_For_Tls_Configuration_test.go`
around lines 104 - 116, Close the file handles returned by os.CreateTemp for
redis_crt_File, redis_key_File, and openssl_test_File after creation, ensuring
cleanup occurs on all paths. Update the os.WriteFile call for
opensslTestCNFContents to use a restrictive non-world-writable mode, and
explicitly set restrictive permissions on the generated private key file after
openssl creates it.

Source: Linters/SAST tools


_, err = osFixture.ExecCommandWithOutputParam(false, true, "openssl", "req", "-new", "-x509", "-sha256",
"-subj", "/C=XX/ST=XX/O=Testing/CN=redis",
"-reqexts", "SAN",
"-extensions", "SAN",
"-config", openssl_test_File.Name(),
"-keyout", redis_key_File.Name(),
"-out", redis_crt_File.Name(),
"-newkey", "rsa:4096",
"-nodes",
"-days", "10",
)
Expect(err).ToNot(HaveOccurred())

By("creating argocd-operator-redis-tls secret from that cert")
_, err = osFixture.ExecCommand("kubectl", "create", "secret", "tls", "argocd-operator-redis-tls", "--key="+redis_key_File.Name(), "--cert="+redis_crt_File.Name(), "-n", argocdNamespace)
Expect(err).ToNot(HaveOccurred())

By("adding argo cd label to argocd-operator-redis-tls secret")
_, err = osFixture.ExecCommand("kubectl", "annotate", "secret", "argocd-operator-redis-tls", "argocds.argoproj.io/name=argocd", "-n", argocdNamespace)
Expect(err).ToNot(HaveOccurred())
Comment on lines +135 to +137

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find how the redis TLS secret annotation/label is consumed in existing e2e tests and operator code.
rg -n -C5 'argocds\.argoproj\.io/name' 
rg -n -C5 'argocd-operator-redis-tls'

Repository: redhat-developer/gitops-operator

Length of output: 170


🏁 Script executed:

#!/bin/bash
set -eu
file="$(fd -t f '1-143_validate_deployment_Env_Args_For_Tls_Configuration_test.go' . | head -n1)"
printf '%s\n' "FILE=$file"
cat -n "$file" | sed -n '120,180p'
printf '%s\n' '--- related instance and secret references ---'
rg -n -C4 'example-argocd|argocd-operator-redis-tls|argocds\.argoproj\.io/name|argoproj\.io/name|redis.*tls' test controllers config deploy 2>/dev/null || true
printf '%s\n' '--- all references to the test helpers and resource names ---'
rg -n -C3 'Create\(.*Argo|New.*Argo|image-updater|image_updater|redis-tls' test/openshift/e2e/ginkgo/sequential 2>/dev/null || true

Repository: redhat-developer/gitops-operator

Length of output: 50389


🏁 Script executed on selected repositories:

#!/bin/bash
set -eu
printf '%s\n' '--- GitOps test setup and annotation usage ---'
rg -n -C6 'argocdInstanceName|argocds\.argoproj\.io/name|kubectl.*annotate.*redis-tls' \
  test/openshift/e2e/ginkgo/sequential/1-143_validate_deployment_Env_Args_For_Tls_Configuration_test.go \
  test/openshift/e2e/ginkgo/parallel/1-067_validate_redis_secure_comm_no_autotls_ha_test.go 2>/dev/null || true
printf '%s\n' '--- Argo CD Operator consumers of the annotation ---'
rg -n -C8 'argocds\.argoproj\.io/name|redis.*tls.*secret|RedisTLS|redisTLS' \
  controllers config api deploy 2>/dev/null | head -n 300 || true
printf '%s\n' '--- Argo CD Operator tests and fixtures using the annotation ---'
rg -n -C6 'argocds\.argoproj\.io/name|redis-tls' \
  controllers tests 2>/dev/null | head -n 300 || true

Repositories: redhat-developer/gitops-operator, argoproj-labs/argocd-operator

Length of output: 62056


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- annotation mapping implementation ---'
rg -n -C12 'func .*Map|AnnotationName|argocds\.argoproj\.io/namespace|Map with owner annotation|redis-operator-redis-tls|Secret.*Watch|Owns\(.*Secret' \
  controllers/argocd api config 2>/dev/null | head -n 400
printf '%s\n' '--- secret reconciliation and watch registration ---'
rg -n -C10 'reconcileRedisTLSSecret|reconcileSecrets|RedisServerTLSSecretName|Watches|Owns' \
  controllers/argocd 2>/dev/null | head -n 400

Repository: argoproj-labs/argocd-operator

Length of output: 50385


🏁 Script executed:

#!/bin/bash
set -eu
cat -n controllers/argocd/custommapper.go | sed -n '1,180p'
printf '%s\n' '--- Redis TLS decision logic ---'
cat -n controllers/argocd/util.go | sed -n '475,530p'
printf '%s\n' '--- controller watch registration ---'
rg -n -C8 'setResourceWatches|tlsSecretMapper|tlsSecret' controllers/argocd/argocd_controller.go controllers/argocd/*.go | head -n 220

Repository: argoproj-labs/argocd-operator

Length of output: 26401


Use example-argocd in the annotation.

The TLS secret mapper uses argocds.argoproj.io/name to enqueue the ArgoCD instance when the secret changes. The current value, argocd, does not match example-argocd, so later secret updates do not reconcile this instance. Change the value to example-argocd and update the By text from “label” to “annotation”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@test/openshift/e2e/ginkgo/sequential/1-143_validate_deployment_Env_Args_For_Tls_Configuration_test.go`
around lines 135 - 137, Update the annotation command in the TLS secret setup to
set argocds.argoproj.io/name to example-argocd, and change the adjacent By
description from “label” to “annotation”; leave the existing secret and
namespace targets unchanged.


By("creating ArgoCD instance")
argo := &argov1beta1api.ArgoCD{
ObjectMeta: metav1.ObjectMeta{
Name: argocdInstanceName,
Namespace: argocdNamespace,
},
Spec: argov1beta1api.ArgoCDSpec{},
}
argo.Spec.ImageUpdater.Enabled = true
Expect(c.Create(ctx, argo)).To(Succeed())
By("waiting for ArgoCD to be available")
Eventually(func() error {
return c.Get(ctx, types.NamespacedName{Name: argocdInstanceName, Namespace: argocdNamespace}, &argov1beta1api.ArgoCD{})
}, 2*time.Minute, 5*time.Second).Should(Succeed())
defer func() {
By("cleaning up resources")
_ = c.Delete(ctx, argo)
_ = c.Delete(ctx, ns)
os.Remove(redis_crt_File.Name())
os.Remove(redis_key_File.Name())
os.Remove(openssl_test_File.Name())
}()
Comment on lines +95 to +160

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Register cleanup before creating cluster resources.

The defer at Line 153 runs only if all preceding statements succeed. If openssl, the kubectl create secret call, or c.Create(ctx, argo) fails, the assertion aborts the test and the namespace test-tls-argocd and the temp files are never removed. A leaked namespace breaks later sequential tests that call EnsureSequentialCleanSlate.

Move the cleanup registration to just after each resource is created.

♻️ Proposed restructuring
 			Expect(c.Create(ctx, ns)).To(Succeed())
+			defer func() {
+				By("cleaning up namespace")
+				_ = c.Delete(ctx, ns)
+			}()
 
 			By("generating a test certificate to use with redis, using openssl")
 			redis_crt_File, err := os.CreateTemp("", "redis.crt")
 			Expect(err).ToNot(HaveOccurred())
+			defer os.Remove(redis_crt_File.Name())

and reduce the later defer to the ArgoCD instance only:

 			defer func() {
 				By("cleaning up resources")
 				_ = c.Delete(ctx, argo)
-				_ = c.Delete(ctx, ns)
-				os.Remove(redis_crt_File.Name())
-				os.Remove(redis_key_File.Name())
-				os.Remove(openssl_test_File.Name())
 			}()
🧰 Tools
🪛 ast-grep (0.45.1)

[warning] 114-114: File mode grants world-writable permission; restrict the mode so other users cannot modify the file (e.g. 0o644 / 0o600).
Context: os.WriteFile(openssl_test_File.Name(), ([]byte)(opensslTestCNFContents), 0666)
Note: [CWE-276] Incorrect Default Permissions.

(world-writable-chmod-go)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@test/openshift/e2e/ginkgo/sequential/1-143_validate_deployment_Env_Args_For_Tls_Configuration_test.go`
around lines 95 - 160, Register cleanup immediately after creating each resource
in the setup flow, rather than deferring all cleanup until after ArgoCD
availability succeeds. Ensure the namespace cleanup is established after
c.Create(ctx, ns), each temporary file is removed after its os.CreateTemp
succeeds, and the ArgoCD cleanup is registered immediately after c.Create(ctx,
argo); remove the later combined defer while preserving cleanup ordering and
behavior.

coreDeployments := []string{
"example-argocd-server",
"example-argocd-repo-server",
"example-argocd-argocd-image-updater-controller",
}
time.Sleep(5 * time.Second)
// --- Validate updated TLS values ---
By("validating updated TLS args For RepoServer and Server")
Eventually(func() bool {
for _, deploymentName := range coreDeployments {
deployment := &appsv1.Deployment{}
if err := c.Get(ctx, types.NamespacedName{Name: deploymentName, Namespace: argocdNamespace}, deployment); err != nil {
return false
}
valid := false
for _, container := range deployment.Spec.Template.Spec.Containers {
min, hasMin, hasCiphers, ciphers := getTLSValues(container.Args)
if !hasMin {
continue
}
if min != "1.2" {
GinkgoWriter.Printf("%s: expected tlsminversion=1.2, got %s\n", deploymentName, min)
return false
}
if !hasCiphers || ciphers == "" {
GinkgoWriter.Printf("%s: expected --tlsciphers to be present and non-empty, got %q\n", deploymentName, ciphers)
return false
}
GinkgoWriter.Printf("%s updated TLS OK: min=%s\n", deploymentName, min)
valid = true
}
if !valid {
return false
}
}
return true
}, 60*time.Second, 2*time.Second).Should(BeTrue(), "all deployments should have updated TLS configuration")
By("Validating Updated TLS args in Redis deployment")
Eventually(func() bool {
deployment := &appsv1.Deployment{}
if err := c.Get(ctx, types.NamespacedName{Name: "example-argocd-redis", Namespace: argocdNamespace}, deployment); err != nil {
return false
}
if len(deployment.Spec.Template.Spec.Containers) == 0 {
return false
}
args := deployment.Spec.Template.Spec.Containers[0].Args
var tlsProtocols string
var tlsCiphersTLS12 string
var tlsCiphersTLS13 string
hasProtocols := false
hasCiphersTLS12 := false
hasCiphersTLS13 := false
for i := 0; i < len(args); i++ {
arg := args[i]
// --- Handle "--tls-protocols <value>"
if arg == "--tls-protocols" {
hasProtocols = true
if i+1 < len(args) {
tlsProtocols = args[i+1]
}
}
if arg == "--tls-ciphersuites" {
hasCiphersTLS13 = true
if i+1 < len(args) {
tlsCiphersTLS13 = args[i+1]
}
}

if arg == "--tls-ciphers" {
hasCiphersTLS12 = true
if i+1 < len(args) {
tlsCiphersTLS12 = args[i+1]
}
}
}

// --- Print results (always helpful in debugging)
if !hasCiphersTLS13 || tlsCiphersTLS13 == "" {
GinkgoWriter.Printf(" --tls-ciphersuites should not be empty, got %q\n", tlsCiphersTLS13)
return false
}
if !hasCiphersTLS12 || tlsCiphersTLS12 == "" {
GinkgoWriter.Printf(" --tls-ciphers should not be empty, got %q\n", tlsCiphersTLS12)
return false
}

if !hasProtocols || tlsProtocols != "TLSv1.2" {
GinkgoWriter.Printf("%s: expected --tls-protocols=TLSv1.2, got %s\n", deployment.Name, tlsProtocols)
return false
}
GinkgoWriter.Printf("%s TLS args protocol value: %s\n", deployment.Name, tlsProtocols)
GinkgoWriter.Printf("%s TLS args ciphersuites value: %s\n", deployment.Name, tlsCiphersTLS13)
GinkgoWriter.Printf("%s TLS args ciphers value: %s\n", deployment.Name, tlsCiphersTLS12)
return true
}, 60*time.Second, 2*time.Second).Should(BeTrue())
})
})
})
Loading