Skip to content

Commit f763687

Browse files
Merge pull request #1310 from hongkailiu/accept-risks-e2e-serial
OTA-1546: Add a serial e2e for accept-risks
2 parents 0797b28 + dae0f49 commit f763687

10 files changed

Lines changed: 227 additions & 17 deletions

File tree

.openshift-tests-extension/openshift_payload_cluster-version-operator.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
11
[
2+
{
3+
"name": "[Jira:\"Cluster Version Operator\"] cluster-version-operator should work with accept risks",
4+
"labels": {
5+
"Serial": {}
6+
},
7+
"resources": {
8+
"isolation": {}
9+
},
10+
"source": "openshift:payload:cluster-version-operator",
11+
"lifecycle": "blocking",
12+
"environmentSelector": {}
13+
},
214
{
315
"name": "[Jira:\"Cluster Version Operator\"] cluster-version-operator-tests should support passing tests",
416
"labels": {},

cmd/cluster-version-operator-tests/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func main() {
2424
Name: "openshift/cluster-version-operator/conformance/parallel",
2525
Parents: []string{"openshift/conformance/parallel"},
2626
Qualifiers: []string{
27-
`!(name.contains("[Serial]") || name.contains("[Slow]"))`,
27+
`!(name.contains("[Serial]") || "Serial" in labels || name.contains("[Slow]"))`,
2828
},
2929
})
3030

@@ -33,7 +33,7 @@ func main() {
3333
Name: "openshift/cluster-version-operator/conformance/serial",
3434
Parents: []string{"openshift/conformance/serial"},
3535
Qualifiers: []string{
36-
`name.contains("[Serial]")`,
36+
`name.contains("[Serial]") || "Serial" in labels`,
3737
},
3838
})
3939

lib/resourcebuilder/apps.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1414
"k8s.io/klog/v2"
1515

16+
"github.com/openshift/cluster-version-operator/pkg/external"
1617
"github.com/openshift/cluster-version-operator/pkg/payload"
1718
)
1819

@@ -39,7 +40,7 @@ func (b *builder) modifyDeployment(ctx context.Context, deployment *appsv1.Deplo
3940
// if we detect the CVO deployment we need to replace the KUBERNETES_SERVICE_HOST env var with the internal load
4041
// balancer to be resilient to kube-apiserver rollouts that cause the localhost server to become non-responsive for
4142
// multiple minutes.
42-
if deployment.Namespace == "openshift-cluster-version" && deployment.Name == "cluster-version-operator" {
43+
if deployment.Namespace == external.DefaultCVONamespace && deployment.Name == external.DefaultDeploymentName {
4344
infrastructureConfig, err := b.configClientv1.Infrastructures().Get(ctx, "cluster", metav1.GetOptions{})
4445
// not found just means that we don't have infrastructure configuration yet, so we should tolerate not found and avoid substitution
4546
if err != nil && !errors.IsNotFound(err) {
@@ -63,7 +64,7 @@ func (b *builder) modifyDeployment(ctx context.Context, deployment *appsv1.Deplo
6364
}
6465
err = updatePodSpecWithInternalLoadBalancerKubeService(
6566
&deployment.Spec.Template.Spec,
66-
[]string{"cluster-version-operator"},
67+
[]string{external.DefaultContainerName},
6768
lbHost,
6869
lbPort,
6970
)

pkg/cvo/sync_worker.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
configv1 "github.com/openshift/api/config/v1"
2727

2828
"github.com/openshift/cluster-version-operator/lib/capability"
29+
"github.com/openshift/cluster-version-operator/pkg/internal"
2930
"github.com/openshift/cluster-version-operator/pkg/payload"
3031
"github.com/openshift/cluster-version-operator/pkg/payload/precondition"
3132
)
@@ -329,7 +330,7 @@ func (w *SyncWorker) syncPayload(ctx context.Context, work *SyncWork) ([]configv
329330
// The remainder of this logic is for loading a new payload.
330331
// Any filtering as to not needing to reload the payload should be done in the switch before this point.
331332

332-
cvoObjectRef := &corev1.ObjectReference{APIVersion: "config.openshift.io/v1", Kind: "ClusterVersion", Name: "version", Namespace: "openshift-cluster-version"}
333+
cvoObjectRef := &corev1.ObjectReference{APIVersion: "config.openshift.io/v1", Kind: "ClusterVersion", Name: internal.DefaultClusterVersionName, Namespace: internal.DefaultCVONamespace}
333334
msg := fmt.Sprintf("Retrieving and verifying payload version=%q image=%q", desired.Version, desired.Image)
334335
w.eventRecorder.Eventf(cvoObjectRef, corev1.EventTypeNormal, "RetrievePayload", msg)
335336
reporter.ReportPayload(LoadPayloadStatus{

pkg/external/constants.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package external
2+
3+
import "github.com/openshift/cluster-version-operator/pkg/internal"
4+
5+
// The constants defined here are used by the components, e.g., e2e tests that have no access
6+
// to github.com/openshift/cluster-version-operator/pkg/internal
7+
// See https://pkg.go.dev/cmd/go#hdr-Internal_Directories
8+
const (
9+
// DefaultCVONamespace is the default namespace for the Cluster Version Operator
10+
DefaultCVONamespace = internal.DefaultCVONamespace
11+
// DefaultClusterVersionName is the default name for the Cluster Version resource managed by the Cluster Version Operator
12+
DefaultClusterVersionName = internal.DefaultClusterVersionName
13+
// DefaultDeploymentName is the default name of the deployment for the Cluster Version Operator
14+
DefaultDeploymentName = internal.DefaultDeploymentName
15+
// DefaultContainerName is the default container name in the deployment for the Cluster Version Operator
16+
DefaultContainerName = internal.DefaultContainerName
17+
18+
// ConditionalUpdateConditionTypeRecommended is a type of the condition present on a conditional update
19+
// that indicates whether the conditional update is recommended or not
20+
ConditionalUpdateConditionTypeRecommended = internal.ConditionalUpdateConditionTypeRecommended
21+
)

pkg/internal/constants.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ import (
77
)
88

99
const (
10+
// DefaultCVONamespace is the default namespace for the Cluster Version Operator
11+
DefaultCVONamespace = "openshift-cluster-version"
12+
// DefaultClusterVersionName is the default name for the Cluster Version resource managed by the Cluster Version Operator
13+
DefaultClusterVersionName = "version"
14+
// DefaultDeploymentName is the default name of the deployment for the Cluster Version Operator
15+
DefaultDeploymentName = "cluster-version-operator"
16+
// DefaultContainerName is the default container name in the deployment for the Cluster Version Operator
17+
DefaultContainerName = DefaultDeploymentName
18+
1019
ConfigNamespace = "openshift-config"
1120
ConfigManagedNamespace = "openshift-config-managed"
1221
AdminGatesConfigMap = "admin-gates"

pkg/start/start.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,6 @@ import (
4949
)
5050

5151
const (
52-
defaultComponentName = "version"
53-
defaultComponentNamespace = "openshift-cluster-version"
54-
5552
minResyncPeriod = 2 * time.Minute
5653
)
5754

@@ -122,8 +119,8 @@ func NewOptions() *Options {
122119
PromQLTarget: defaultPromQLTarget,
123120

124121
// exposed only for testing
125-
Namespace: defaultEnv("CVO_NAMESPACE", defaultComponentNamespace),
126-
Name: defaultEnv("CVO_NAME", defaultComponentName),
122+
Namespace: defaultEnv("CVO_NAMESPACE", internal.DefaultCVONamespace),
123+
Name: defaultEnv("CVO_NAME", internal.DefaultClusterVersionName),
127124
PayloadOverride: os.Getenv("PAYLOAD_OVERRIDE"),
128125
ResyncInterval: minResyncPeriod,
129126
Exclude: os.Getenv("EXCLUDE_MANIFESTS"),

test/cvo/accept_risks.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package cvo
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
g "github.com/onsi/ginkgo/v2"
8+
o "github.com/onsi/gomega"
9+
10+
configv1 "github.com/openshift/api/config/v1"
11+
configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
12+
"k8s.io/apimachinery/pkg/api/meta"
13+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
"k8s.io/apimachinery/pkg/util/sets"
15+
"k8s.io/apimachinery/pkg/util/wait"
16+
"k8s.io/client-go/rest"
17+
18+
"github.com/openshift/cluster-version-operator/pkg/external"
19+
"github.com/openshift/cluster-version-operator/test/util"
20+
)
21+
22+
var _ = g.Describe(`[Jira:"Cluster Version Operator"] cluster-version-operator`, func() {
23+
24+
var (
25+
c *rest.Config
26+
configClient *configv1client.ConfigV1Client
27+
err error
28+
29+
ctx = context.TODO()
30+
backup configv1.ClusterVersionSpec
31+
)
32+
33+
g.BeforeEach(func() {
34+
c, err = util.GetRestConfig()
35+
o.Expect(err).To(o.BeNil())
36+
configClient, err = configv1client.NewForConfig(c)
37+
o.Expect(err).To(o.BeNil())
38+
39+
util.SkipIfNotTechPreviewNoUpgrade(ctx, c)
40+
o.Expect(util.SkipIfHypershift(ctx, c)).To(o.BeNil())
41+
o.Expect(util.SkipIfMicroshift(ctx, c)).To(o.BeNil())
42+
43+
cv, err := configClient.ClusterVersions().Get(ctx, external.DefaultClusterVersionName, metav1.GetOptions{})
44+
o.Expect(err).NotTo(o.HaveOccurred())
45+
if du := cv.Spec.DesiredUpdate; du != nil {
46+
logger.WithValues("AcceptRisks", du.AcceptRisks).Info("Accept risks before testing")
47+
o.Expect(du.AcceptRisks).To(o.BeEmpty(), "found accept risks")
48+
}
49+
backup = *cv.Spec.DeepCopy()
50+
})
51+
52+
g.AfterEach(func() {
53+
cv, err := configClient.ClusterVersions().Get(ctx, external.DefaultClusterVersionName, metav1.GetOptions{})
54+
o.Expect(err).NotTo(o.HaveOccurred())
55+
cv.Spec = backup
56+
_, err = configClient.ClusterVersions().Update(ctx, cv, metav1.UpdateOptions{})
57+
o.Expect(err).NotTo(o.HaveOccurred())
58+
})
59+
60+
g.It("should work with accept risks", g.Label("Serial"), func() {
61+
cv, err := configClient.ClusterVersions().Get(ctx, external.DefaultClusterVersionName, metav1.GetOptions{})
62+
o.Expect(err).NotTo(o.HaveOccurred())
63+
64+
g.By("Using fauxinnati as the upstream and its risks-always channel")
65+
cv.Spec.Upstream = util.FauxinnatiAPIURL
66+
cv.Spec.Channel = "risks-always"
67+
68+
_, err = configClient.ClusterVersions().Update(ctx, cv, metav1.UpdateOptions{})
69+
o.Expect(err).NotTo(o.HaveOccurred())
70+
71+
g.By("Checking that conditional updates shows up in status")
72+
// waiting for the conditional updates to show up
73+
o.Expect(wait.PollUntilContextTimeout(ctx, 30*time.Second, 5*time.Minute, true, func(ctx context.Context) (done bool, err error) {
74+
cv, err = configClient.ClusterVersions().Get(ctx, external.DefaultClusterVersionName, metav1.GetOptions{})
75+
o.Expect(err).NotTo(o.HaveOccurred())
76+
if len(cv.Status.ConditionalUpdates) == 0 {
77+
return false, nil
78+
}
79+
return true, nil
80+
})).NotTo(o.HaveOccurred(), "no conditional updates found in status")
81+
82+
g.By("Checking that no conditional updates are recommended")
83+
conditionalUpdatesLength := len(cv.Status.ConditionalUpdates)
84+
var acceptRisks []configv1.AcceptRisk
85+
var releases []configv1.Release
86+
names := sets.New[string]()
87+
for _, cu := range cv.Status.ConditionalUpdates {
88+
releases = append(releases, cu.Release)
89+
o.Expect(cu.RiskNames).NotTo(o.BeEmpty())
90+
for _, name := range cu.RiskNames {
91+
if names.Has(name) {
92+
continue
93+
}
94+
names.Insert(name)
95+
acceptRisks = append(acceptRisks, configv1.AcceptRisk{Name: name})
96+
}
97+
o.Expect(cu.Risks).NotTo(o.BeEmpty())
98+
recommendedCondition := meta.FindStatusCondition(cu.Conditions, external.ConditionalUpdateConditionTypeRecommended)
99+
o.Expect(recommendedCondition).NotTo(o.BeNil())
100+
o.Expect(recommendedCondition.Status).To(o.Equal(metav1.ConditionFalse))
101+
}
102+
103+
g.By("Accepting all risks")
104+
o.Expect(acceptRisks).NotTo(o.BeEmpty())
105+
if cv.Spec.DesiredUpdate == nil {
106+
cv.Spec.DesiredUpdate = &configv1.Update{
107+
Image: cv.Status.Desired.Image,
108+
Version: cv.Status.Desired.Version,
109+
Architecture: cv.Status.Desired.Architecture,
110+
}
111+
}
112+
cv.Spec.DesiredUpdate.AcceptRisks = acceptRisks
113+
114+
_, err = configClient.ClusterVersions().Update(ctx, cv, metav1.UpdateOptions{})
115+
o.Expect(err).NotTo(o.HaveOccurred())
116+
117+
g.By("Checking that all conditional updates are recommended")
118+
var releasesNow []configv1.Release
119+
// waiting for the conditional updates to be refreshed
120+
o.Expect(wait.PollUntilContextTimeout(ctx, 30*time.Second, 5*time.Minute, true, func(ctx context.Context) (done bool, err error) {
121+
cv, err = configClient.ClusterVersions().Get(ctx, external.DefaultClusterVersionName, metav1.GetOptions{})
122+
o.Expect(err).NotTo(o.HaveOccurred())
123+
o.Expect(cv.Status.ConditionalUpdates).NotTo(o.BeEmpty())
124+
releasesNow = nil
125+
for _, cu := range cv.Status.ConditionalUpdates {
126+
releasesNow = append(releasesNow, cu.Release)
127+
recommendedCondition := meta.FindStatusCondition(cu.Conditions, external.ConditionalUpdateConditionTypeRecommended)
128+
o.Expect(recommendedCondition).NotTo(o.BeNil())
129+
if recommendedCondition.Status != metav1.ConditionTrue {
130+
return false, nil
131+
}
132+
}
133+
return true, nil
134+
})).NotTo(o.HaveOccurred(), "no conditional updates are recommended in status after accepting risks")
135+
136+
o.Expect(cv.Spec.DesiredUpdate.AcceptRisks).To(o.Equal(acceptRisks))
137+
o.Expect(cv.Status.ConditionalUpdates).To(o.HaveLen(conditionalUpdatesLength))
138+
o.Expect(releasesNow).To(o.Equal(releases))
139+
})
140+
})

test/cvo/cvo.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Package cvo contains end-to-end tests for the Cluster Version Operator.
2+
// The accept_risks test validates the feature about accept risks for conditional updates.
13
package cvo
24

35
import (
@@ -10,15 +12,14 @@ import (
1012
g "github.com/onsi/ginkgo/v2"
1113
o "github.com/onsi/gomega"
1214

15+
"github.com/openshift/cluster-version-operator/pkg/external"
1316
"github.com/openshift/cluster-version-operator/test/oc"
1417
ocapi "github.com/openshift/cluster-version-operator/test/oc/api"
1518
"github.com/openshift/cluster-version-operator/test/util"
1619
)
1720

1821
var logger = g.GinkgoLogr.WithName("cluster-version-operator-tests")
1922

20-
const cvoNamespace = "openshift-cluster-version"
21-
2223
var _ = g.Describe(`[Jira:"Cluster Version Operator"] cluster-version-operator-tests`, func() {
2324
g.It("should support passing tests", func() {
2425
o.Expect(true).To(o.BeTrue())
@@ -66,14 +67,14 @@ var _ = g.Describe(`[Jira:"Cluster Version Operator"] cluster-version-operator`,
6667
o.Expect(err).NotTo(o.HaveOccurred(), "Failed to determine if cluster is MicroShift")
6768

6869
g.By("Checking that the 'openshift.io/run-level' label exists on the namespace and has the empty value")
69-
ns, err := kubeClient.CoreV1().Namespaces().Get(ctx, cvoNamespace, metav1.GetOptions{})
70-
o.Expect(err).NotTo(o.HaveOccurred(), "Failed to get namespace %s", cvoNamespace)
70+
ns, err := kubeClient.CoreV1().Namespaces().Get(ctx, external.DefaultCVONamespace, metav1.GetOptions{})
71+
o.Expect(err).NotTo(o.HaveOccurred(), "Failed to get namespace %s", external.DefaultCVONamespace)
7172
runLevel, exists := ns.Labels["openshift.io/run-level"]
72-
o.Expect(exists).To(o.BeTrue(), "The 'openshift.io/run-level' label on namespace %s does not exist", cvoNamespace)
73-
o.Expect(runLevel).To(o.BeEmpty(), "Expected the 'openshift.io/run-level' label value on namespace %s has the empty value, but got %s", cvoNamespace, runLevel)
73+
o.Expect(exists).To(o.BeTrue(), "The 'openshift.io/run-level' label on namespace %s does not exist", external.DefaultCVONamespace)
74+
o.Expect(runLevel).To(o.BeEmpty(), "Expected the 'openshift.io/run-level' label value on namespace %s has the empty value, but got %s", external.DefaultCVONamespace, runLevel)
7475

7576
g.By("Checking that the annotation 'openshift.io/scc annotation' on the CVO pod has the value hostaccess")
76-
podList, err := kubeClient.CoreV1().Pods(cvoNamespace).List(ctx, metav1.ListOptions{
77+
podList, err := kubeClient.CoreV1().Pods(external.DefaultCVONamespace).List(ctx, metav1.ListOptions{
7778
LabelSelector: "k8s-app=cluster-version-operator",
7879
FieldSelector: "status.phase=Running",
7980
})

test/util/util.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"time"
66

77
g "github.com/onsi/ginkgo/v2"
8+
o "github.com/onsi/gomega"
9+
810
configv1 "github.com/openshift/api/config/v1"
911
clientconfigv1 "github.com/openshift/client-go/config/clientset/versioned"
1012
corev1 "k8s.io/api/core/v1"
@@ -106,3 +108,29 @@ func GetKubeClient(restConfig *rest.Config) (kubernetes.Interface, error) {
106108
func GetConfigClient(restConfig *rest.Config) (clientconfigv1.Interface, error) {
107109
return clientconfigv1.NewForConfig(restConfig)
108110
}
111+
112+
// IsTechPreviewNoUpgrade checks if a cluster is a TechPreviewNoUpgrade cluster
113+
func IsTechPreviewNoUpgrade(ctx context.Context, restConfig *rest.Config) bool {
114+
configClient, err := GetConfigClient(restConfig)
115+
o.Expect(err).NotTo(o.HaveOccurred())
116+
featureGate, err := configClient.ConfigV1().FeatureGates().Get(ctx, "cluster", metav1.GetOptions{})
117+
if err != nil {
118+
if apierrors.IsNotFound(err) {
119+
return false
120+
}
121+
o.Expect(err).NotTo(o.HaveOccurred(), "could not retrieve feature-gate: %v", err)
122+
}
123+
return featureGate.Spec.FeatureSet == configv1.TechPreviewNoUpgrade
124+
}
125+
126+
// SkipIfNotTechPreviewNoUpgrade skips the test if a cluster is not a TechPreviewNoUpgrade cluster
127+
func SkipIfNotTechPreviewNoUpgrade(ctx context.Context, restConfig *rest.Config) {
128+
if !IsTechPreviewNoUpgrade(ctx, restConfig) {
129+
g.Skip("This test is skipped because the Tech Preview NoUpgrade is not enabled")
130+
}
131+
}
132+
133+
const (
134+
// fauxinnati mocks Cincinnati Update Graph Server for OpenShift
135+
FauxinnatiAPIURL = "https://fauxinnati-fauxinnati.apps.ota-stage.q2z4.p1.openshiftapps.com/api/upgrades_info/graph"
136+
)

0 commit comments

Comments
 (0)