Skip to content

Commit 0cad08f

Browse files
committed
Adding common methods for inject upstream
CreateGraphTemplate() will help create a upstream graph template. RunUpdateService() will add upstream graph to a test pod. CreateService() will create a service. StopUpdateService() will delete deployment. CleanupService() will delete a service.
1 parent f7e9723 commit 0cad08f

4 files changed

Lines changed: 566 additions & 0 deletions

File tree

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,20 @@
7272
"source": "openshift:payload:cluster-version-operator",
7373
"lifecycle": "blocking",
7474
"environmentSelector": {}
75+
},
76+
{
77+
"name": "[Jira:\"Cluster Version Operator\"] cluster-version-operator cvo drops invalid conditional edges",
78+
"labels": {
79+
"46422": {},
80+
"ConnectedOnly": {},
81+
"Low": {},
82+
"Serial": {}
83+
},
84+
"resources": {
85+
"isolation": {}
86+
},
87+
"source": "openshift:payload:cluster-version-operator",
88+
"lifecycle": "blocking",
89+
"environmentSelector": {}
7590
}
7691
]

pkg/payload/precondition/clusterversion/upgradeable.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,17 @@ func GetCurrentVersion(history []configv1.UpdateHistory) string {
180180
}
181181
return ""
182182
}
183+
184+
func GetCurrentVersionAndImage(history []configv1.UpdateHistory) (string, string) {
185+
for _, h := range history {
186+
if h.State == configv1.CompletedUpdate {
187+
klog.V(2).Infof("Cluster current version=%s", h.Version)
188+
return h.Version, h.Image
189+
}
190+
}
191+
// Empty history should only occur if method is called early in startup before history is populated.
192+
if len(history) != 0 {
193+
return history[len(history)-1].Version, history[len(history)-1].Image
194+
}
195+
return "", ""
196+
}

test/cvo/cvo.go

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,24 @@ package cvo
44

55
import (
66
"context"
7+
"fmt"
8+
"strings"
9+
"time"
710

11+
"github.com/blang/semver/v4"
812
g "github.com/onsi/ginkgo/v2"
913
o "github.com/onsi/gomega"
1014

1115
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
16+
"k8s.io/apimachinery/pkg/util/wait"
1217
"k8s.io/client-go/kubernetes"
1318
"k8s.io/client-go/rest"
1419

20+
configv1 "github.com/openshift/api/config/v1"
21+
clientconfigv1 "github.com/openshift/client-go/config/clientset/versioned"
22+
1523
"github.com/openshift/cluster-version-operator/pkg/external"
24+
"github.com/openshift/cluster-version-operator/pkg/payload/precondition/clusterversion"
1625
"github.com/openshift/cluster-version-operator/test/oc"
1726
ocapi "github.com/openshift/cluster-version-operator/test/oc/api"
1827
"github.com/openshift/cluster-version-operator/test/util"
@@ -99,4 +108,202 @@ var _ = g.Describe(`[Jira:"Cluster Version Operator"] cluster-version-operator`,
99108
sccAnnotation := cvoPod.Annotations["openshift.io/scc"]
100109
o.Expect(sccAnnotation).To(o.Equal("hostaccess"), "Expected the annotation 'openshift.io/scc annotation' on pod %s to have the value 'hostaccess', but got %s", cvoPod.Name, sccAnnotation)
101110
})
111+
112+
g.It("cvo drops invalid conditional edges", g.Label("46422", "Low", "ConnectedOnly", "Serial"), func() {
113+
ctx := context.Background()
114+
err := util.SkipIfNetworkRestricted(ctx, restCfg, util.FauxinnatiAPIURL)
115+
o.Expect(err).NotTo(o.HaveOccurred(), "Failed to check network connectivity")
116+
117+
clientconfigv1, err := clientconfigv1.NewForConfig(restCfg)
118+
o.Expect(err).NotTo(o.HaveOccurred())
119+
cv, err := clientconfigv1.ConfigV1().ClusterVersions().Get(ctx, "version", metav1.GetOptions{})
120+
o.Expect(err).NotTo(o.HaveOccurred())
121+
o.Expect(cv).NotTo(o.BeNil())
122+
123+
versionString, image := clusterversion.GetCurrentVersionAndImage(cv.Status.History)
124+
o.Expect(versionString).NotTo(o.BeNil())
125+
o.Expect(image).NotTo(o.BeNil())
126+
127+
ver, err := semver.Make(versionString)
128+
o.Expect(err).NotTo(o.HaveOccurred())
129+
channel := fmt.Sprintf("candidate-%d.%d", ver.Major, ver.Minor)
130+
131+
nodes := []util.Node{
132+
{Version: versionString, Payload: image, Channel: channel},
133+
}
134+
edges := []util.Edge{}
135+
conditionalEdges := []util.ConditionalEdge{
136+
{
137+
Edge: util.StringEdge{
138+
From: versionString,
139+
To: "",
140+
},
141+
Risks: []util.Risk{
142+
{
143+
Url: "https://bugzilla.redhat.com/show_bug.cgi?id=123456",
144+
Name: "Bug 123456",
145+
Message: "Empty target node",
146+
Rule: map[string]interface{}{"type": "Always"},
147+
},
148+
},
149+
},
150+
}
151+
g.By("create upstream graph template with invalid conditional edges")
152+
buf, err := util.CreateGraphTemplate(nodes, edges, conditionalEdges)
153+
o.Expect(err).NotTo(o.HaveOccurred())
154+
155+
label := map[string]string{"app": "test-update-service"}
156+
g.By("run update service with the graph template")
157+
deployment1, err := util.RunUpdateService(ctx, kubeClient, external.DefaultCVONamespace, buf.String(), label)
158+
o.Expect(err).NotTo(o.HaveOccurred())
159+
logger.Info(fmt.Sprintf("deployment1: %s, %s, %d", deployment1.Spec.Template.Spec.Containers[0].Image, deployment1.Spec.Template.Spec.Containers[0].Ports[0].Name, deployment1.Spec.Template.Spec.Containers[0].Ports[0].ContainerPort))
160+
161+
service1, url, err := util.CreateService(ctx, kubeClient, external.DefaultCVONamespace, deployment1, 46422)
162+
o.Expect(err).NotTo(o.HaveOccurred())
163+
logger.Info("Update Service URL: " + url.String())
164+
logger.Info(fmt.Sprintf("Service: %s, %v, %v", service1.Spec.Ports[0].Name, &service1.Spec.Ports[0].Port, service1.Spec.Ports[0].TargetPort))
165+
166+
policyName := service1.Name + "-policy"
167+
_, err = util.CreateNetworkPolicy(ctx, kubeClient, external.DefaultCVONamespace, policyName, label)
168+
o.Expect(err).NotTo(o.HaveOccurred())
169+
170+
pollErr := wait.PollUntilContextTimeout(ctx, 5*time.Second, 60*time.Second, true, func(ctx context.Context) (bool, error) {
171+
if res, getErr := util.NetworkRestricted(ctx, restCfg, url.String()); getErr != nil {
172+
return false, getErr
173+
} else if res {
174+
return false, fmt.Errorf("expected network to be available, but it is not")
175+
}
176+
return true, nil
177+
})
178+
o.Expect(pollErr).NotTo(o.HaveOccurred(), fmt.Sprintf("Failed to verify network connectivity to the update service at %s: %v", url.String(), pollErr))
179+
180+
g.By("patch upstream with null target node conditional edge")
181+
o.Expect(err).NotTo(o.HaveOccurred())
182+
_, err = util.PatchUpstream(ctx, clientconfigv1, url.String(), channel)
183+
o.Expect(err).NotTo(o.HaveOccurred())
184+
pollErr = wait.PollUntilContextTimeout(ctx, 5*time.Second, 1*time.Minute, true, func(ctx context.Context) (bool, error) {
185+
cv, err := clientconfigv1.ConfigV1().ClusterVersions().Get(ctx, "version", metav1.GetOptions{})
186+
if err != nil {
187+
return false, err
188+
}
189+
for _, condition := range cv.Status.Conditions {
190+
if condition.Type == configv1.ClusterStatusConditionType("RetrievedUpdates") {
191+
if condition.Status != configv1.ConditionFalse {
192+
return false, nil
193+
}
194+
if !strings.Contains(condition.Message, "no node for conditional update") {
195+
return false, nil
196+
}
197+
return true, nil
198+
}
199+
}
200+
return false, nil
201+
})
202+
o.Expect(pollErr).NotTo(o.HaveOccurred(), fmt.Sprintf("Failed to verify the cluster version condition for the null target node conditional edge: %v", pollErr))
203+
204+
g.By("stop update service")
205+
defer func() { _ = util.StopUpdateService(ctx, kubeClient, external.DefaultCVONamespace, deployment1) }()
206+
defer func() { _ = util.CleanupService(ctx, kubeClient, external.DefaultCVONamespace, service1.Name) }()
207+
defer func() { _ = util.DeleteDeployment(ctx, kubeClient, external.DefaultCVONamespace, deployment1.Name) }()
208+
209+
targetVersion := fmt.Sprintf("%d.%d.999", ver.Major, ver.Minor)
210+
nodes = append(nodes, util.Node{
211+
Version: targetVersion,
212+
Payload: "quay.io/openshift-release-dev/ocp-release@sha256:fc88d0bf145c81d9c9e5b8a1cfcaa7cbbacfa698f0c3a252fbbdbfbb1b2",
213+
Channel: channel,
214+
})
215+
edges = []util.Edge{}
216+
conditionalEdges = []util.ConditionalEdge{
217+
{
218+
Edge: util.StringEdge{
219+
From: versionString,
220+
To: targetVersion,
221+
},
222+
Risks: []util.Risk{
223+
{
224+
Url: "// example.com",
225+
Name: "InvalidURL",
226+
Message: "Invalid URL.",
227+
Rule: map[string]interface{}{"type": "PromQL", "promql": map[string]interface{}{"promql": "cluster_installer"}},
228+
},
229+
{
230+
Url: "https://bug.example.com/b",
231+
Name: "TypeNull",
232+
Message: "MatchingRules type is null.",
233+
Rule: "{\"type\": \"\"}",
234+
},
235+
{
236+
Url: "https://bug.example.com/c",
237+
Name: "InvalidMatchingRulesType",
238+
Message: "MatchingRules type is invalid, support Always and PromQL.",
239+
Rule: map[string]interface{}{"type": "nonexist", "promql": map[string]interface{}{"promql": "group(cluster_version_available_updates{channel=\"buggy\"})\nor\n0 * group(cluster_version_available_updates{channel!=\"buggy\"})"}},
240+
},
241+
{
242+
Url: "https://bug.example.com/d",
243+
Name: "InvalidPromQLQueryReturnValue",
244+
Message: "PromQL query return value is not supported, support 0 and 1.",
245+
Rule: map[string]interface{}{"type": "PromQL", "promql": map[string]interface{}{"promql": "max(cluster_version)"}},
246+
},
247+
{
248+
Url: "https://bug.example.com/d",
249+
Name: "InvalidPromQLQuery",
250+
Message: "Invalid PromQL Query.",
251+
Rule: map[string]interface{}{"type": "PromQL", "promql": map[string]interface{}{"promql": "cluster_infrastructure_provider{type=~\"VSphere|None\"}"}},
252+
},
253+
},
254+
},
255+
}
256+
g.By("create upstream graph template with multi risks conditional edges")
257+
buf, err = util.CreateGraphTemplate(nodes, edges, conditionalEdges)
258+
o.Expect(err).NotTo(o.HaveOccurred())
259+
260+
g.By("run update service with the multi risks graph template")
261+
deployment2, err := util.RunUpdateService(ctx, kubeClient, external.DefaultCVONamespace, buf.String(), label)
262+
o.Expect(err).NotTo(o.HaveOccurred())
263+
service2, url2, err := util.CreateService(ctx, kubeClient, external.DefaultCVONamespace, deployment2, 46422)
264+
o.Expect(err).NotTo(o.HaveOccurred())
265+
logger.Info("Update Service URL: " + url2.String())
266+
logger.Info(fmt.Sprintf("Service: %s, %v, %v", service2.Spec.Ports[0].Name, &service2.Spec.Ports[0].Port, service2.Spec.Ports[0].TargetPort))
267+
268+
pollErr = wait.PollUntilContextTimeout(ctx, 5*time.Second, 60*time.Second, true, func(ctx context.Context) (bool, error) {
269+
if res, getErr := util.NetworkRestricted(ctx, restCfg, url2.String()); getErr != nil {
270+
return false, getErr
271+
} else if res {
272+
return false, fmt.Errorf("expected network to be available, but it is not")
273+
}
274+
return true, nil
275+
})
276+
o.Expect(pollErr).NotTo(o.HaveOccurred(), fmt.Sprintf("Failed to verify network connectivity to the update service at %s: %v", url2.String(), pollErr))
277+
278+
g.By("patch upstream with multi risks conditional edge")
279+
_, err = util.PatchUpstream(ctx, clientconfigv1, url2.String(), channel)
280+
o.Expect(err).NotTo(o.HaveOccurred())
281+
pollErr = wait.PollUntilContextTimeout(ctx, 5*time.Second, 1*time.Minute, true, func(ctx context.Context) (bool, error) {
282+
cv, err := clientconfigv1.ConfigV1().ClusterVersions().Get(ctx, "version", metav1.GetOptions{})
283+
if err != nil {
284+
return false, err
285+
}
286+
if cv.Status.AvailableUpdates != nil {
287+
return false, nil
288+
}
289+
for _, condition := range cv.Status.Conditions {
290+
if condition.Type == configv1.ClusterStatusConditionType("RetrievedUpdates") {
291+
if condition.Status != configv1.ConditionTrue {
292+
return false, nil
293+
}
294+
return true, nil
295+
}
296+
}
297+
return true, nil
298+
})
299+
o.Expect(pollErr).NotTo(o.HaveOccurred(), fmt.Sprintf("Failed to verify the cluster version condition for the multi risks target node conditional edge: %v", pollErr))
300+
301+
g.By("stop update service again")
302+
defer func() { _ = util.StopUpdateService(ctx, kubeClient, external.DefaultCVONamespace, deployment2) }()
303+
defer func() { _ = util.CleanupService(ctx, kubeClient, external.DefaultCVONamespace, service2.Name) }()
304+
defer func() { _ = util.DeleteDeployment(ctx, kubeClient, external.DefaultCVONamespace, deployment2.Name) }()
305+
defer func() {
306+
_ = util.DeleteNetworkPolicy(ctx, kubeClient, external.DefaultCVONamespace, policyName)
307+
}()
308+
})
102309
})

0 commit comments

Comments
 (0)