Skip to content

Commit 8b13830

Browse files
Merge pull request #444 from bharath-b-rh/cm-1141
CM-1141: Drops cluster FeatureSet gate for TP TrustManager feature
2 parents a31bc8f + f37c874 commit 8b13830

5 files changed

Lines changed: 72 additions & 35 deletions

File tree

pkg/features/features.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,19 +140,21 @@ func readClusterPreviewFeatureGate(ctx context.Context, configClient configv1cli
140140
// (--unsupported-addon-features=IstioCSR=true). FeatureIstioCSR is GA and does not depend on the
141141
// cluster FeatureSet; the featuregate remains available so users can disable it when unused.
142142
func IsIstioCSRFeatureGateEnabled() bool {
143-
return DefaultFeatureGate.Enabled(v1alpha1.FeatureIstioCSR)
143+
if !DefaultFeatureGate.Enabled(v1alpha1.FeatureIstioCSR) {
144+
log.V(1).Info("IstioCSR feature: internal featuregate is not enabled")
145+
return false
146+
}
147+
log.V(1).Info("IstioCSR feature: enabled")
148+
return true
144149
}
145150

146151
// IsTrustManagerFeatureGateEnabled reports whether the TrustManager operand may run.
147-
// When config.openshift.io FeatureGate is served, the cluster must use a preview FeatureSet
148-
// (e.g. TechPreviewNoUpgrade); spec.featureSet Default does not start TrustManager even if
149-
// --unsupported-addon-features=TrustManager=true. The internal operator featuregate must also be on.
150-
// When stateErr is set, TrustManager stays off. When the FeatureGate API is not served, only the
151-
// internal gate applies.
152+
//
153+
// TrustManager remains a TechPreview feature and is gated by the internal operator featuregate
154+
// (--unsupported-addon-features=TrustManager=true). The cluster FeatureSet check
155+
// (featuregates.config.openshift.io/cluster spec.featureSet) was removed: for increased adoption.
156+
// Cluster FeatureSet state is still tracked via FeatureGateState but does not gate TrustManager.
152157
func (f *FeatureGateState) IsTrustManagerFeatureGateEnabled() bool {
153-
if !f.passesClusterPreviewGating(string(v1alpha1.FeatureTrustManager)) {
154-
return false
155-
}
156158
if !DefaultFeatureGate.Enabled(v1alpha1.FeatureTrustManager) {
157159
log.V(1).Info("TrustManager feature: internal featuregate is not enabled")
158160
return false

pkg/features/features_test.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,8 @@ func TestAllowedPreviewClusterFeatureSets(t *testing.T) {
158158
}
159159
}
160160

161-
// TestIsTrustManagerFeatureGateEnabled covers cluster featureset plus TrustManager operator
162-
// featuregate (--unsupported-addon-features).
161+
// TestIsTrustManagerFeatureGateEnabled covers the TrustManager operator featuregate
162+
// (--unsupported-addon-features).
163163
func TestIsTrustManagerFeatureGateEnabled(t *testing.T) {
164164
defer func() {
165165
_ = SetupWithFlagValue("TrustManager=false")
@@ -171,9 +171,10 @@ func TestIsTrustManagerFeatureGateEnabled(t *testing.T) {
171171
assert func(t *testing.T, st *FeatureGateState)
172172
}{
173173
{
174-
name: "returns false when cluster feature gate discovery fails",
174+
name: "returns true when operator featuregate on even if cluster feature gate discovery fails",
175175
prep: func(t *testing.T) *configfake.Clientset {
176176
t.Helper()
177+
require.NoError(t, SetupWithFlagValue("TrustManager=true"))
177178
cs := newFakeConfigClient(t, true, clusterFeatureGateObject(ocpfeaturegate.TechPreviewNoUpgrade))
178179
cs.AddReactor("get", "resource", func(clientgotesting.Action) (bool, runtime.Object, error) {
179180
return true, nil, fmt.Errorf("simulated discovery failure")
@@ -182,13 +183,13 @@ func TestIsTrustManagerFeatureGateEnabled(t *testing.T) {
182183
},
183184
assert: func(t *testing.T, st *FeatureGateState) {
184185
t.Helper()
185-
assert.False(t, st.IsTrustManagerFeatureGateEnabled())
186+
assert.True(t, st.IsTrustManagerFeatureGateEnabled())
186187
require.Error(t, st.Err())
187188
assert.True(t, errors.Is(st.Err(), ErrFeatureGateDiscovery))
188189
},
189190
},
190191
{
191-
name: "returns false when featuregates/cluster cannot be read",
192+
name: "returns true when operator featuregate on even if featuregates/cluster cannot be read",
192193
prep: func(t *testing.T) *configfake.Clientset {
193194
t.Helper()
194195
require.NoError(t, SetupWithFlagValue("TrustManager=true"))
@@ -203,7 +204,7 @@ func TestIsTrustManagerFeatureGateEnabled(t *testing.T) {
203204
},
204205
assert: func(t *testing.T, st *FeatureGateState) {
205206
t.Helper()
206-
assert.False(t, st.IsTrustManagerFeatureGateEnabled())
207+
assert.True(t, st.IsTrustManagerFeatureGateEnabled())
207208
assert.True(t, errors.Is(st.Err(), ErrFeatureGateClusterGet))
208209
},
209210
},
@@ -221,15 +222,15 @@ func TestIsTrustManagerFeatureGateEnabled(t *testing.T) {
221222
},
222223
},
223224
{
224-
name: "returns false when cluster featureset is Default even with operator featuregate set",
225+
name: "returns true when cluster featureset is Default with operator featuregate set",
225226
prep: func(t *testing.T) *configfake.Clientset {
226227
t.Helper()
227228
require.NoError(t, SetupWithFlagValue("TrustManager=true"))
228229
return newFakeConfigClient(t, true, clusterFeatureGateObject(ocpfeaturegate.Default))
229230
},
230231
assert: func(t *testing.T, st *FeatureGateState) {
231232
t.Helper()
232-
assert.False(t, st.IsTrustManagerFeatureGateEnabled())
233+
assert.True(t, st.IsTrustManagerFeatureGateEnabled())
233234
},
234235
},
235236
{

pkg/operator/starter.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,11 @@ func RunOperator(ctx context.Context, cc *controllercmd.ControllerContext) error
167167
}
168168

169169
// setupFeatureGates applies operator feature flags from UnsupportedAddonFeatures
170-
// (--unsupported-addon-features) and builds cluster-side FeatureGateState used for
171-
// feature preview gating. Transient discovery or featuregates/cluster read errors
170+
// (--unsupported-addon-features) and builds cluster-side FeatureGateState by reading
171+
// featuregates/cluster when served. Transient discovery or featuregates/cluster read errors
172172
// are retried up to three times with 30s backoff; persistent failure leaves stateErr set
173-
// so feature stays off without aborting the rest of startup. ErrNilConfigClient
174-
// is not retried. Returns an error only for invalid addon feature syntax or if ctx is
175-
// canceled while waiting between retries.
173+
// on FeatureGateState without aborting the rest of startup. Returns an error only for invalid
174+
// addon feature syntax or if ctx is canceled while waiting between retries.
176175
func setupFeatureGates(ctx context.Context, configClient configv1client.Interface) (*features.FeatureGateState, error) {
177176
const (
178177
// maxFeatureGateAttempts caps how many times we call NewFeatureGateState when discovery or

test/e2e/istio_csr_operand_test.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,9 +261,12 @@ var _ = Describe("Istio-CSR operand coverage [apigroup:operator.openshift.io]",
261261
By("waiting for IstioCSR Ready=False with multiple-instance rejection message")
262262
Expect(waitForIstioCSRConditionMessage(ctx, loader, secondNS.Name, istioCSRResourceName, v1alpha1.Ready, metav1.ConditionFalse, "multiple instances of istiocsr exists", highTimeout, slowPollInterval)).NotTo(HaveOccurred())
263263

264-
obj, err := loader.DynamicClient.Resource(istiocsrSchema).Namespace(secondNS.Name).Get(ctx, istioCSRResourceName, metav1.GetOptions{})
265-
Expect(err).NotTo(HaveOccurred())
266-
Expect(obj.GetAnnotations()).To(HaveKey(istioCSRRejectAnnotation))
264+
By("waiting for reject-multiple-instance annotation (written after status in a separate API update)")
265+
Eventually(func(g Gomega) {
266+
obj, err := loader.DynamicClient.Resource(istiocsrSchema).Namespace(secondNS.Name).Get(ctx, istioCSRResourceName, metav1.GetOptions{})
267+
g.Expect(err).NotTo(HaveOccurred())
268+
g.Expect(obj.GetAnnotations()).To(HaveKey(istioCSRRejectAnnotation))
269+
}, lowTimeout, fastPollInterval).Should(Succeed())
267270

268271
Consistently(func() bool {
269272
_, err := clientset.AppsV1().Deployments(secondNS.Name).Get(ctx, istioCSRGRPCServiceName, metav1.GetOptions{})

test/e2e/trustmanager_test.go

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const (
5757
trustedCABundleKey = "ca-bundle.crt"
5858
)
5959

60-
// Cluster must have an allowed feature set (e.g. TechPreviewNoUpgrade). TrustManager is deployed and reconciled.
60+
// TrustManager is deployed and reconciled when --unsupported-addon-features=TrustManager=true.
6161
var _ = Describe("TrustManager", Ordered, Label("Platform:Generic", "Feature:TrustManager", "TechPreview"), func() {
6262
var (
6363
ctx = context.Background()
@@ -1464,9 +1464,10 @@ var _ = Describe("TrustManager", Ordered, Label("Platform:Generic", "Feature:Tru
14641464
})
14651465
})
14661466

1467-
// Cluster has featuregates.config.openshift.io spec.featureSet at Default. TrustManager controller is not enabled; operand should not be deployed.
1468-
// TechPreview:Inverted labels test scenarios that invert the TechPreview test suite—validating behavior when Tech Preview feature is not enabled.
1469-
var _ = Describe("TrustManager with Default feature set", Ordered, Label("Platform:Generic", "Feature:TrustManager", "TechPreview:Inverted"), func() {
1467+
// TrustManager operator feature gate defaults to disabled (unlike IstioCSR). Creating a TrustManager CR
1468+
// must not deploy the operand when --unsupported-addon-features does not enable TrustManager.
1469+
// TechPreview:Inverted labels test scenarios that invert the main TrustManager suite (feature gate off).
1470+
var _ = Describe("TrustManager with operator feature gate disabled", Ordered, Label("Platform:Generic", "Feature:TrustManager", "TechPreview:Inverted"), func() {
14701471
var (
14711472
ctx = context.Background()
14721473

@@ -1475,16 +1476,16 @@ var _ = Describe("TrustManager with Default feature set", Ordered, Label("Platfo
14751476
originalOperatorLogLevel string
14761477
)
14771478

1478-
BeforeAll(trustManagerBeforeAll(ctx, &clientset, &originalUnsupportedAddonFeatures, &originalOperatorLogLevel))
1479+
BeforeAll(trustManagerFeatureGateDisabledBeforeAll(ctx, &clientset, &originalUnsupportedAddonFeatures, &originalOperatorLogLevel))
14791480

14801481
AfterAll(trustManagerAfterAll(ctx, &originalUnsupportedAddonFeatures, &originalOperatorLogLevel))
14811482

14821483
BeforeEach(trustManagerBeforeEach())
14831484

14841485
AfterEach(trustManagerAfterEach(ctx))
14851486

1486-
It("should not create ServiceAccount or populate status when cluster feature set is Default", func() {
1487-
By("creating TrustManager CR with default settings (same as TechPreview scenario)")
1487+
It("should not create ServiceAccount or populate status when operator feature gate is disabled", func() {
1488+
By("creating TrustManager CR with default settings")
14881489
_, err := trustManagerClient().Create(ctx, &v1alpha1.TrustManager{
14891490
ObjectMeta: metav1.ObjectMeta{Name: "cluster"},
14901491
Spec: v1alpha1.TrustManagerSpec{
@@ -1497,10 +1498,10 @@ var _ = Describe("TrustManager with Default feature set", Ordered, Label("Platfo
14971498
_, err = trustManagerClient().Get(ctx, "cluster", metav1.GetOptions{})
14981499
Expect(err).ShouldNot(HaveOccurred())
14991500

1500-
By("verifying ServiceAccount is not created and TrustManager status stays unset (controller not running for Default feature set)")
1501+
By("verifying ServiceAccount is not created and TrustManager status stays unset (controller not running without feature gate)")
15011502
Consistently(func(g Gomega) {
15021503
_, err := clientset.CoreV1().ServiceAccounts(trustManagerNamespace).Get(ctx, trustManagerServiceAccountName, metav1.GetOptions{})
1503-
g.Expect(apierrors.IsNotFound(err)).To(BeTrue(), "ServiceAccount %s/%s must not exist when cluster feature set is Default", trustManagerNamespace, trustManagerServiceAccountName)
1504+
g.Expect(apierrors.IsNotFound(err)).To(BeTrue(), "ServiceAccount %s/%s must not exist when TrustManager feature gate is disabled", trustManagerNamespace, trustManagerServiceAccountName)
15041505

15051506
tm, err := trustManagerClient().Get(ctx, "cluster", metav1.GetOptions{})
15061507
g.Expect(err).NotTo(HaveOccurred())
@@ -1620,7 +1621,7 @@ func findSecretRule(rules []rbacv1.PolicyRule, verb string) *rbacv1.PolicyRule {
16201621

16211622
// trustManagerRemoveStaleOperandServiceAccount deletes the trust-manager operand ServiceAccount if it
16221623
// exists and waits until it is gone. TrustManager CR deletion does not remove this SA today, so a
1623-
// prior e2e run or TechPreview block can leave it and break the Default-feature-set scenario.
1624+
// prior e2e run can leave it behind and affect later tests.
16241625
//
16251626
// TODO: Remove this when TrustManager is GA and operand teardown removes the ServiceAccount (or
16261627
// equivalent) when the TrustManager CR is deleted.
@@ -1633,6 +1634,37 @@ func trustManagerRemoveStaleOperandServiceAccount(ctx context.Context, cs *kuber
16331634
}, lowTimeout, fastPollInterval).Should(BeTrue())
16341635
}
16351636

1637+
func trustManagerFeatureGateDisabledBeforeAll(ctx context.Context, clientset **kubernetes.Clientset, unsupportedAddonFeatures, operatorLogLevel *string) func() {
1638+
return func() {
1639+
cs, err := kubernetes.NewForConfig(cfg)
1640+
Expect(err).Should(BeNil())
1641+
*clientset = cs
1642+
1643+
trustManagerRemoveStaleOperandServiceAccount(ctx, cs)
1644+
1645+
By("capturing original UNSUPPORTED_ADDON_FEATURES from subscription before patching")
1646+
*unsupportedAddonFeatures, err = getSubscriptionEnvVar(ctx, loader, "UNSUPPORTED_ADDON_FEATURES")
1647+
Expect(err).NotTo(HaveOccurred())
1648+
1649+
By("capturing original OPERATOR_LOG_LEVEL from subscription before patching")
1650+
*operatorLogLevel, err = getSubscriptionEnvVar(ctx, loader, "OPERATOR_LOG_LEVEL")
1651+
Expect(err).NotTo(HaveOccurred())
1652+
1653+
fmt.Fprintf(GinkgoWriter, "TrustManager feature gate disabled BeforeAll: captured UNSUPPORTED_ADDON_FEATURES=%q OPERATOR_LOG_LEVEL=%q\n",
1654+
*unsupportedAddonFeatures, *operatorLogLevel)
1655+
1656+
By("ensuring TrustManager operator feature gate is not enabled via subscription")
1657+
err = patchSubscriptionWithEnvVars(ctx, loader, map[string]string{
1658+
"UNSUPPORTED_ADDON_FEATURES": "",
1659+
})
1660+
Expect(err).NotTo(HaveOccurred())
1661+
1662+
By("waiting for operator deployment to rollout without UNSUPPORTED_ADDON_FEATURES")
1663+
err = waitForDeploymentEnvVarRemovedAndRollout(ctx, operatorNamespace, operatorDeploymentName, "UNSUPPORTED_ADDON_FEATURES", lowTimeout)
1664+
Expect(err).NotTo(HaveOccurred())
1665+
}
1666+
}
1667+
16361668
func trustManagerBeforeAll(ctx context.Context, clientset **kubernetes.Clientset, unsupportedAddonFeatures, operatorLogLevel *string) func() {
16371669
return func() {
16381670
cs, err := kubernetes.NewForConfig(cfg)

0 commit comments

Comments
 (0)