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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// This file imports test packages to ensure they are included in the build.
// These imports are necessary to register Ginkgo tests with the OpenShift Tests Extension framework.
package main

import (
// Import test packages to register Ginkgo tests
_ "github.com/openshift/cluster-kube-apiserver-operator/test/e2e"
)
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/google/go-cmp v0.7.0
github.com/imdario/mergo v0.3.8
github.com/miekg/dns v1.1.61
github.com/onsi/ginkgo/v2 v2.21.0
github.com/openshift-eng/openshift-tests-extension v0.0.0-20250804142706-7b3ab438a292
github.com/openshift/api v0.0.0-20251015095338-264e80a2b6e7
github.com/openshift/build-machinery-go v0.0.0-20250530140348-dc5b2804eeee
Expand Down Expand Up @@ -78,7 +79,6 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/onsi/ginkgo/v2 v2.21.0 // indirect
github.com/onsi/gomega v1.35.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
Expand Down
1 change: 1 addition & 0 deletions test/e2e/bound_sa_token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const (
// with respect to the resources it manages.
//
// Note: this test will roll out a new version - multiple times
// TODO: CNTRLPLANE-2223 - Migrate this test to OTE ginkgo framework
func TestBoundTokenSignerController(t *testing.T) {
kubeConfig, err := testlibrary.NewClientConfigForTest()
require.NoError(t, err)
Expand Down
157 changes: 157 additions & 0 deletions test/e2e/certrotation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package e2e

import (
"context"
"fmt"
"strings"
"testing"
"time"

"github.com/stretchr/testify/require"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"k8s.io/utils/clock"

configv1 "github.com/openshift/api/config/v1"
operatorv1 "github.com/openshift/api/operator/v1"
configclient "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
"github.com/openshift/cluster-kube-apiserver-operator/pkg/operator"
"github.com/openshift/cluster-kube-apiserver-operator/pkg/operator/operatorclient"
testlibrary "github.com/openshift/cluster-kube-apiserver-operator/test/library"
configv1helpers "github.com/openshift/library-go/pkg/config/clusteroperator/v1helpers"
"github.com/openshift/library-go/pkg/operator/genericoperatorclient"
"github.com/openshift/library-go/pkg/operator/v1helpers"

g "github.com/onsi/ginkgo/v2"
)

var _ = g.Describe("[sig-api-machinery] kube-apiserver operator", func() {
g.It("[Operator][Serial] TestCertRotationTimeUpgradeable", func() {
testCertRotationTimeUpgradeable(g.GinkgoTB())
})
g.It("[Operator][Serial] TestCertRotationStompOnBadType", func() {
testCertRotationStompOnBadType(g.GinkgoTB())
})
})

func testCertRotationTimeUpgradeable(t testing.TB) {
kubeConfig, err := testlibrary.NewClientConfigForTest()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

could you adda a new tmp commit with t.Fail() just to see if the expected CI jobs will fail?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

other than that LGTM

require.NoError(t, err)
operatorClient, _, err := genericoperatorclient.NewStaticPodOperatorClient(
clock.RealClock{},
kubeConfig,
operatorv1.GroupVersion.WithResource("kubeapiservers"),
operatorv1.GroupVersion.WithKind("KubeAPIServer"),
operator.ExtractStaticPodOperatorSpec,
operator.ExtractStaticPodOperatorStatus)
require.NoError(t, err)
configClient, err := configclient.NewForConfig(kubeConfig)
require.NoError(t, err)

ctx := context.Background()
_, operatorStatus, _, err := operatorClient.GetStaticPodOperatorStateWithQuorum(ctx)
require.NoError(t, err)
require.True(t, v1helpers.IsOperatorConditionTrue(operatorStatus.Conditions, "CertRotationTimeUpgradeable"))

kubeClient := kubernetes.NewForConfigOrDie(kubeConfig)
t.Logf("Creating unsupported-cert-rotation-config...")
_, err = kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).Create(context.TODO(), &corev1.ConfigMap{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The code creates a ctx variable but doesn't use it consistently. But operation uses context.TODO() while others could use the existing ctx.

ObjectMeta: metav1.ObjectMeta{Namespace: operatorclient.GlobalUserSpecifiedConfigNamespace, Name: "unsupported-cert-rotation-config"},
Data: map[string]string{"base": "2y"},
}, metav1.CreateOptions{})
require.NoError(t, err)
defer func() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Resource Cleanup: Missing Error Handling

kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).Delete(context.TODO(), "unsupported-cert-rotation-config", metav1.DeleteOptions{})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The code creates a ctx variable but doesn't use it consistently. But operation uses context.TODO() while others could use the existing ctx.

}()

Comment on lines +55 to +70
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Make ConfigMap create/delete idempotent to reduce flakes
On Line 62, Create(...) will fail if the ConfigMap already exists (e.g., leftover from a prior failed run). Also the deferred Delete ignores errors. Consider “delete if exists, then create”, and ignore NotFound on cleanup.

@@
 	t.Logf("Creating unsupported-cert-rotation-config...")
-	_, err = kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).Create(context.TODO(), &corev1.ConfigMap{
+	_ = kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).
+		Delete(ctx, "unsupported-cert-rotation-config", metav1.DeleteOptions{})
+	_, err = kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).Create(ctx, &corev1.ConfigMap{
 		ObjectMeta: metav1.ObjectMeta{Namespace: operatorclient.GlobalUserSpecifiedConfigNamespace, Name: "unsupported-cert-rotation-config"},
 		Data:       map[string]string{"base": "2y"},
 	}, metav1.CreateOptions{})
 	require.NoError(t, err)
 	defer func() {
-		kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).Delete(context.TODO(), "unsupported-cert-rotation-config", metav1.DeleteOptions{})
+		if err := kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).
+			Delete(ctx, "unsupported-cert-rotation-config", metav1.DeleteOptions{}); err != nil && !errors.IsNotFound(err) {
+			t.Logf("cleanup: failed to delete configmap unsupported-cert-rotation-config: %v", err)
+		}
 	}()
📝 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
ctx := context.Background()
_, operatorStatus, _, err := operatorClient.GetStaticPodOperatorStateWithQuorum(ctx)
require.NoError(t, err)
require.True(t, v1helpers.IsOperatorConditionTrue(operatorStatus.Conditions, "CertRotationTimeUpgradeable"))
kubeClient := kubernetes.NewForConfigOrDie(kubeConfig)
t.Logf("Creating unsupported-cert-rotation-config...")
_, err = kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).Create(context.TODO(), &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Namespace: operatorclient.GlobalUserSpecifiedConfigNamespace, Name: "unsupported-cert-rotation-config"},
Data: map[string]string{"base": "2y"},
}, metav1.CreateOptions{})
require.NoError(t, err)
defer func() {
kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).Delete(context.TODO(), "unsupported-cert-rotation-config", metav1.DeleteOptions{})
}()
ctx := context.Background()
_, operatorStatus, _, err := operatorClient.GetStaticPodOperatorStateWithQuorum(ctx)
require.NoError(t, err)
require.True(t, v1helpers.IsOperatorConditionTrue(operatorStatus.Conditions, "CertRotationTimeUpgradeable"))
kubeClient := kubernetes.NewForConfigOrDie(kubeConfig)
t.Logf("Creating unsupported-cert-rotation-config...")
_ = kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).
Delete(ctx, "unsupported-cert-rotation-config", metav1.DeleteOptions{})
_, err = kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).Create(ctx, &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Namespace: operatorclient.GlobalUserSpecifiedConfigNamespace, Name: "unsupported-cert-rotation-config"},
Data: map[string]string{"base": "2y"},
}, metav1.CreateOptions{})
require.NoError(t, err)
defer func() {
if err := kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).
Delete(ctx, "unsupported-cert-rotation-config", metav1.DeleteOptions{}); err != nil && !errors.IsNotFound(err) {
t.Logf("cleanup: failed to delete configmap unsupported-cert-rotation-config: %v", err)
}
}()
🤖 Prompt for AI Agents
In test/e2e/certrotation.go around lines 55 to 70, the ConfigMap Create will
fail if the object already exists and the deferred Delete currently ignores all
errors; make the operation idempotent by first attempting to delete the
ConfigMap (ignore NotFound) before calling Create so Create succeeds even if a
prior run left the resource, and update the deferred cleanup to call Delete but
explicitly ignore NotFound errors while surface/logging any other delete error;
use the same context variable (ctx) for both calls and ensure errors from the
pre-create delete are handled/ignored only for NotFound, and require.NoError on
the Create call as before.

err = wait.PollImmediate(1*time.Second, 5*time.Second, func() (bool, error) {
_, operatorStatus, _, err := operatorClient.GetStaticPodOperatorStateWithQuorum(ctx)
if err != nil {
return false, err
}
clusteroperator, err := configClient.ClusterOperators().Get(context.TODO(), "kube-apiserver", metav1.GetOptions{})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The code creates a ctx variable but doesn't use it consistently. But operation uses context.TODO() while others could use the existing ctx.

if err != nil {
return false, err
}

certRotationCondition := v1helpers.FindOperatorCondition(operatorStatus.Conditions, "CertRotationTimeUpgradeable")
upgradeableCondition := configv1helpers.FindStatusCondition(clusteroperator.Status.Conditions, "Upgradeable")
if certRotationCondition == nil || upgradeableCondition == nil {
return false, fmt.Errorf("Couldn't find CertRotationTimeUpgradeable or Upgradeable condition")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Error Message Capitalization

Suggested change
return false, fmt.Errorf("Couldn't find CertRotationTimeUpgradeable or Upgradeable condition")
return false, fmt.Errorf("couldn't find CertRotationTimeUpgradeable or Upgradeable condition")

}
if certRotationCondition.Status == operatorv1.ConditionFalse &&
upgradeableCondition.Status == configv1.ConditionFalse && strings.Contains(upgradeableCondition.Reason, "CertRotationTime") {
return true, nil
}
t.Logf("\nCertRotationTimeUpgradeable: %#v\nUpgradeable: %#v", certRotationCondition, upgradeableCondition)
return false, nil
})
require.NoError(t, err)
Comment on lines +71 to +93
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Short timeout may cause test flakes in slower environments.

The 5-second timeout in both polling loops may be insufficient for end-to-end tests, especially in resource-constrained CI/CD environments where operator reconciliation can be slower. Consider increasing to at least 30-60 seconds to reduce flakiness.

Apply this diff to increase the timeouts:

-	err = wait.PollImmediate(1*time.Second, 5*time.Second, func() (bool, error) {
+	err = wait.PollImmediate(1*time.Second, 30*time.Second, func() (bool, error) {

And similarly for the second polling loop:

-	err = wait.PollImmediate(1*time.Second, 5*time.Second, func() (bool, error) {
+	err = wait.PollImmediate(1*time.Second, 30*time.Second, func() (bool, error) {

Additionally, the polling logic is duplicated between lines 71-92 and 99-119 with only minor differences in the conditions checked. Consider extracting a helper function to reduce duplication and improve maintainability.

Also applies to: 99-120

🤖 Prompt for AI Agents
In test/e2e/certrotation.go around lines 71 to 93, the polling timeout is only 5
seconds which is too short and causes flakes; increase the PollImmediate
timeout/duration to a more robust value (e.g., use PollImmediate(1*time.Second,
30*time.Second) or 60s) for both polling loops and update any matching second
loop (lines ~99-120). Also refactor the duplicated polling logic into a small
helper function that accepts the context, operatorClient, configClient, logger,
and a predicate that checks the two conditions so both places call the same
helper to reduce duplication and improve maintainability.


t.Logf("Removing unsupported-cert-rotation-config...")
err = kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).Delete(context.TODO(), "unsupported-cert-rotation-config", metav1.DeleteOptions{})
require.NoError(t, err)

err = wait.PollImmediate(1*time.Second, 5*time.Second, func() (bool, error) {
_, operatorStatus, _, err := operatorClient.GetStaticPodOperatorStateWithQuorum(ctx)
if err != nil {
return false, err
}
clusteroperator, err := configClient.ClusterOperators().Get(context.TODO(), "kube-apiserver", metav1.GetOptions{})
if err != nil {
return false, err
}
certRotationCondition := v1helpers.FindOperatorCondition(operatorStatus.Conditions, "CertRotationTimeUpgradeable")
upgradeableCondition := configv1helpers.FindStatusCondition(clusteroperator.Status.Conditions, "Upgradeable")
if certRotationCondition == nil || upgradeableCondition == nil {
return false, fmt.Errorf("Couldn't find CertRotationTimeUpgradeable or Upgradeable condition")
}
if certRotationCondition.Status == operatorv1.ConditionTrue &&
(upgradeableCondition.Status == configv1.ConditionTrue || !strings.Contains(upgradeableCondition.Reason, "CertRotationTime")) {
return true, nil
}
t.Logf("\nCertRotationTimeUpgradeable: %#v\nUpgradeable: %#v", certRotationCondition, upgradeableCondition)
return false, nil
})
require.NoError(t, err)
}

func testCertRotationStompOnBadType(t testing.TB) {
kubeConfig, err := testlibrary.NewClientConfigForTest()
require.NoError(t, err)
kubeClient := kubernetes.NewForConfigOrDie(kubeConfig)

// this is inherently racy against a controller
err = wait.PollImmediate(10*time.Millisecond, 5*time.Second, func() (done bool, err error) {
if err := kubeClient.CoreV1().Secrets(operatorclient.OperatorNamespace).Delete(context.TODO(), "aggregator-client-signer", metav1.DeleteOptions{}); err != nil {
return false, nil
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Error swallowed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
return false, nil
return false, fmt.Errorf("failed to delete secret: %w", err)

}
if _, err := kubeClient.CoreV1().Secrets(operatorclient.OperatorNamespace).Create(context.TODO(), &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Namespace: operatorclient.OperatorNamespace, Name: "aggregator-client-signer"},
Type: "SecretTypeTLS",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Use the proper Secret type constant.

The string literal "SecretTypeTLS" should be replaced with the proper Kubernetes constant corev1.SecretTypeTLS for type safety and correctness. The current string literal is invalid and will not match the expected type.

Apply this diff:

-			Type:       "SecretTypeTLS",
+			Type:       corev1.SecretTypeTLS,
📝 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
Type: "SecretTypeTLS",
Type: corev1.SecretTypeTLS,
🤖 Prompt for AI Agents
In test/e2e/certrotation.go around line 135, the Secret Type field is set to the
string literal "SecretTypeTLS"; replace this with the proper Kubernetes constant
corev1.SecretTypeTLS (ensure the corev1 package is imported/aliased
appropriately) so the Secret uses the correct typed constant instead of an
invalid string literal.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it's should be Type: "kubernetes.io/tls", I remember that we have one bug to migrate type of this.
see: openshift/library-go#1681

}, metav1.CreateOptions{}); err != nil {
return false, nil
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Error swallowed

}
return true, nil
})
require.NoError(t, err)

err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (done bool, err error) {
curr, err := kubeClient.CoreV1().Secrets(operatorclient.OperatorNamespace).Get(context.TODO(), "aggregator-client-signer", metav1.GetOptions{})
if errors.IsNotFound(err) {
return false, nil
}
if err != nil {
return false, err
}
if curr.Type == corev1.SecretTypeTLS {
return true, nil
}
return false, nil
})
require.NoError(t, err)
}
151 changes: 13 additions & 138 deletions test/e2e/certrotation_test.go
Original file line number Diff line number Diff line change
@@ -1,146 +1,21 @@
package e2e

import (
"context"
"fmt"
"strings"
"testing"
"time"

"github.com/stretchr/testify/require"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"k8s.io/utils/clock"

configv1 "github.com/openshift/api/config/v1"
operatorv1 "github.com/openshift/api/operator/v1"
configclient "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
"github.com/openshift/cluster-kube-apiserver-operator/pkg/operator"
"github.com/openshift/cluster-kube-apiserver-operator/pkg/operator/operatorclient"
test "github.com/openshift/cluster-kube-apiserver-operator/test/library"
configv1helpers "github.com/openshift/library-go/pkg/config/clusteroperator/v1helpers"
"github.com/openshift/library-go/pkg/operator/genericoperatorclient"
"github.com/openshift/library-go/pkg/operator/v1helpers"
)
import "testing"

// This test calls the shared function which
// can be called from both standard Go tests and Ginkgo
//
// This situation is temporary until we test the new e2e-gcp-operator-serial-ote job.
// Eventually all tests will be run only as part of the OTE framework.
func TestCertRotationTimeUpgradeable(t *testing.T) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

let's add a comment:

// This test calls the shared function which
// can be called from both standard Go tests and Ginkgo 
//
// This situation is temporary until we test the new e2e-gcp-operator-serial-ote job.
// Eventually all tests will be run only as part of the OTE framework.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated

kubeConfig, err := test.NewClientConfigForTest()
require.NoError(t, err)
operatorClient, _, err := genericoperatorclient.NewStaticPodOperatorClient(
clock.RealClock{},
kubeConfig,
operatorv1.GroupVersion.WithResource("kubeapiservers"),
operatorv1.GroupVersion.WithKind("KubeAPIServer"),
operator.ExtractStaticPodOperatorSpec,
operator.ExtractStaticPodOperatorStatus)
require.NoError(t, err)
configClient, err := configclient.NewForConfig(kubeConfig)
require.NoError(t, err)

ctx := context.Background()
_, operatorStatus, _, err := operatorClient.GetStaticPodOperatorStateWithQuorum(ctx)
require.NoError(t, err)
require.True(t, v1helpers.IsOperatorConditionTrue(operatorStatus.Conditions, "CertRotationTimeUpgradeable"))

kubeClient := kubernetes.NewForConfigOrDie(kubeConfig)
t.Logf("Creating unsupported-cert-rotation-config...")
_, err = kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).Create(context.TODO(), &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Namespace: operatorclient.GlobalUserSpecifiedConfigNamespace, Name: "unsupported-cert-rotation-config"},
Data: map[string]string{"base": "2y"},
}, metav1.CreateOptions{})
require.NoError(t, err)
defer func() {
kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).Delete(context.TODO(), "unsupported-cert-rotation-config", metav1.DeleteOptions{})
}()

err = wait.PollImmediate(1*time.Second, 5*time.Second, func() (bool, error) {
_, operatorStatus, _, err := operatorClient.GetStaticPodOperatorStateWithQuorum(ctx)
if err != nil {
return false, err
}
clusteroperator, err := configClient.ClusterOperators().Get(context.TODO(), "kube-apiserver", metav1.GetOptions{})
if err != nil {
return false, err
}

certRotationCondition := v1helpers.FindOperatorCondition(operatorStatus.Conditions, "CertRotationTimeUpgradeable")
upgradeableCondition := configv1helpers.FindStatusCondition(clusteroperator.Status.Conditions, "Upgradeable")
if certRotationCondition == nil || upgradeableCondition == nil {
return false, fmt.Errorf("Couldn't find CertRotationTimeUpgradeable or Upgradeable condition")
}
if certRotationCondition.Status == operatorv1.ConditionFalse &&
upgradeableCondition.Status == configv1.ConditionFalse && strings.Contains(upgradeableCondition.Reason, "CertRotationTime") {
return true, nil
}
t.Logf("\nCertRotationTimeUpgradeable: %#v\nUpgradeable: %#v", certRotationCondition, upgradeableCondition)
return false, nil
})
require.NoError(t, err)

t.Logf("Removing unsupported-cert-rotation-config...")
err = kubeClient.CoreV1().ConfigMaps(operatorclient.GlobalUserSpecifiedConfigNamespace).Delete(context.TODO(), "unsupported-cert-rotation-config", metav1.DeleteOptions{})
require.NoError(t, err)

err = wait.PollImmediate(1*time.Second, 5*time.Second, func() (bool, error) {
_, operatorStatus, _, err := operatorClient.GetStaticPodOperatorStateWithQuorum(ctx)
if err != nil {
return false, err
}
clusteroperator, err := configClient.ClusterOperators().Get(context.TODO(), "kube-apiserver", metav1.GetOptions{})
if err != nil {
return false, err
}
certRotationCondition := v1helpers.FindOperatorCondition(operatorStatus.Conditions, "CertRotationTimeUpgradeable")
upgradeableCondition := configv1helpers.FindStatusCondition(clusteroperator.Status.Conditions, "Upgradeable")
if certRotationCondition == nil || upgradeableCondition == nil {
return false, fmt.Errorf("Couldn't find CertRotationTimeUpgradeable or Upgradeable condition")
}
if certRotationCondition.Status == operatorv1.ConditionTrue &&
(upgradeableCondition.Status == configv1.ConditionTrue || !strings.Contains(upgradeableCondition.Reason, "CertRotationTime")) {
return true, nil
}
t.Logf("\nCertRotationTimeUpgradeable: %#v\nUpgradeable: %#v", certRotationCondition, upgradeableCondition)
return false, nil
})
require.NoError(t, err)
testCertRotationTimeUpgradeable(t)
}

// This test calls the shared function which
// can be called from both standard Go tests and Ginkgo
//
// This situation is temporary until we test the new e2e-gcp-operator-serial-ote job.
// Eventually all tests will be run only as part of the OTE framework.
func TestCertRotationStompOnBadType(t *testing.T) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

and here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated

kubeConfig, err := test.NewClientConfigForTest()
require.NoError(t, err)
kubeClient := kubernetes.NewForConfigOrDie(kubeConfig)

// this is inherently racy against a controller
err = wait.PollImmediate(10*time.Millisecond, 5*time.Second, func() (done bool, err error) {
if err := kubeClient.CoreV1().Secrets(operatorclient.OperatorNamespace).Delete(context.TODO(), "aggregator-client-signer", metav1.DeleteOptions{}); err != nil {
return false, nil
}
if _, err := kubeClient.CoreV1().Secrets(operatorclient.OperatorNamespace).Create(context.TODO(), &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Namespace: operatorclient.OperatorNamespace, Name: "aggregator-client-signer"},
Type: "SecretTypeTLS",
}, metav1.CreateOptions{}); err != nil {
return false, nil
}
return true, nil
})
require.NoError(t, err)

err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (done bool, err error) {
curr, err := kubeClient.CoreV1().Secrets(operatorclient.OperatorNamespace).Get(context.TODO(), "aggregator-client-signer", metav1.GetOptions{})
if errors.IsNotFound(err) {
return false, nil
}
if err != nil {
return false, err
}
if curr.Type == corev1.SecretTypeTLS {
return true, nil
}
return false, nil
})
require.NoError(t, err)
testCertRotationStompOnBadType(t)
}