Skip to content

Commit 522c789

Browse files
committed
Get subscription channel from package manifest if it is not present
1 parent 833a39d commit 522c789

File tree

6 files changed

+103
-9
lines changed

6 files changed

+103
-9
lines changed

pkg/autodiscover/autodiscover.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ type DiscoveredTestData struct {
7373
AllCsvs []*olmv1Alpha.ClusterServiceVersion
7474
AllInstallPlans []*olmv1Alpha.InstallPlan
7575
AllCatalogSources []*olmv1Alpha.CatalogSource
76+
AllPackageManifests []*PackageManifest
7677
Deployments []appsv1.Deployment
7778
StatefulSet []appsv1.StatefulSet
7879
PersistentVolumes []corev1.PersistentVolume
@@ -158,6 +159,7 @@ func DoAutoDiscover(config *configuration.TestConfiguration) DiscoveredTestData
158159
}
159160
data.AllInstallPlans = getAllInstallPlans(oc.OlmClient)
160161
data.AllCatalogSources = getAllCatalogSources(oc.OlmClient)
162+
data.AllPackageManifests = getAllPackageManifests(oc.DynamicClient)
161163
data.Namespaces = namespacesListToStringList(config.TargetNameSpaces)
162164
data.Pods, data.AllPods = findPodsByLabels(oc.K8sClient.CoreV1(), podsUnderTestLabelsObjects, data.Namespaces)
163165
data.AbnormalEvents = findAbnormalEvents(oc.K8sClient.CoreV1(), data.Namespaces)

pkg/autodiscover/autodiscover_operators.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,21 @@ package autodiscover
1919
import (
2020
"context"
2121
"fmt"
22+
"time"
2223

2324
helmclient "github.com/mittwald/go-helm-client"
2425
olmv1Alpha "github.com/operator-framework/api/pkg/operators/v1alpha1"
2526
clientOlm "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
2627
"github.com/redhat-best-practices-for-k8s/certsuite/internal/log"
2728
"github.com/redhat-best-practices-for-k8s/certsuite/pkg/configuration"
29+
2830
"github.com/redhat-best-practices-for-k8s/certsuite/pkg/stringhelper"
2931
"helm.sh/helm/v3/pkg/release"
3032
"k8s.io/apimachinery/pkg/api/errors"
3133
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34+
"k8s.io/apimachinery/pkg/runtime"
35+
"k8s.io/apimachinery/pkg/runtime/schema"
36+
"k8s.io/client-go/dynamic"
3237
appv1client "k8s.io/client-go/kubernetes/typed/apps/v1"
3338
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
3439
"k8s.io/client-go/rest"
@@ -39,6 +44,34 @@ const (
3944
istioDeploymentName = "istiod"
4045
)
4146

47+
type PackageManifest struct {
48+
APIVersion string `json:"apiVersion"`
49+
Kind string `json:"kind"`
50+
Metadata struct {
51+
CreationTimestamp time.Time `json:"creationTimestamp"`
52+
Labels map[string]string `json:"labels"`
53+
Name string `json:"name"`
54+
Namespace string `json:"namespace"`
55+
} `json:"metadata"`
56+
Spec struct {
57+
} `json:"spec"`
58+
Status struct {
59+
CatalogSource string `json:"catalogSource"`
60+
CatalogSourceDisplayName string `json:"catalogSourceDisplayName"`
61+
CatalogSourceNamespace string `json:"catalogSourceNamespace"`
62+
CatalogSourcePublisher string `json:"catalogSourcePublisher"`
63+
Channels []struct {
64+
CurrentCSV string `json:"currentCSV"`
65+
Name string `json:"name"`
66+
} `json:"channels"`
67+
DefaultChannel string `json:"defaultChannel"`
68+
PackageName string `json:"packageName"`
69+
Provider struct {
70+
Name string `json:"name"`
71+
} `json:"provider"`
72+
} `json:"status"`
73+
}
74+
4275
func isIstioServiceMeshInstalled(appClient appv1client.AppsV1Interface, allNs []string) bool {
4376
// The Istio namespace must be present
4477
if !stringhelper.StringInSlice(allNs, istioNamespace, false) {
@@ -202,3 +235,27 @@ func getAllCatalogSources(olmClient clientOlm.Interface) (out []*olmv1Alpha.Cata
202235
}
203236
return out
204237
}
238+
239+
// getAllPackageManifests is a helper function to get the all the PackageManifests in a cluster.
240+
func getAllPackageManifests(dynamicClient dynamic.Interface) (out []*PackageManifest) {
241+
gvr := schema.GroupVersionResource{
242+
Group: "packages.operators.coreos.com",
243+
Version: "v1",
244+
Resource: "packagemanifests",
245+
}
246+
// Query the package manifest for the operator
247+
pkgManifest, err := dynamicClient.Resource(gvr).Namespace("").List(context.TODO(), metav1.ListOptions{})
248+
if err != nil {
249+
log.Error("Unable get PackageManifests in cluster, err: %v", err)
250+
return out
251+
}
252+
for _, item := range pkgManifest.Items {
253+
var manifest PackageManifest
254+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.Object, &manifest); err != nil {
255+
log.Error("Failed to convert to PackageManifestWithNs: %v", err)
256+
}
257+
258+
out = append(out, &manifest)
259+
}
260+
return out
261+
}

pkg/provider/operators.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
olmv1Alpha "github.com/operator-framework/api/pkg/operators/v1alpha1"
3333
"github.com/redhat-best-practices-for-k8s/certsuite/internal/clientsholder"
3434
"github.com/redhat-best-practices-for-k8s/certsuite/internal/log"
35+
"github.com/redhat-best-practices-for-k8s/certsuite/pkg/autodiscover"
3536
"github.com/redhat-openshift-ecosystem/openshift-preflight/artifacts"
3637
plibRuntime "github.com/redhat-openshift-ecosystem/openshift-preflight/certification"
3738
plibOperator "github.com/redhat-openshift-ecosystem/openshift-preflight/operator"
@@ -150,6 +151,7 @@ func getUniqueCsvListByName(csvs []*olmv1Alpha.ClusterServiceVersion) []*olmv1Al
150151

151152
func createOperators(csvs []*olmv1Alpha.ClusterServiceVersion,
152153
allSubscriptions []olmv1Alpha.Subscription,
154+
allPackageManifests []*autodiscover.PackageManifest,
153155
allInstallPlans []*olmv1Alpha.InstallPlan,
154156
allCatalogSources []*olmv1Alpha.CatalogSource,
155157
succeededRequired,
@@ -181,7 +183,7 @@ func createOperators(csvs []*olmv1Alpha.ClusterServiceVersion,
181183
op.PackageFromCsvName = packageAndVersion[0]
182184
op.Version = csv.Spec.Version.String()
183185
// Get at least one subscription and update the Operator object with it.
184-
if getAtLeastOneSubscription(op, csv, allSubscriptions) {
186+
if getAtLeastOneSubscription(op, csv, allSubscriptions, allPackageManifests) {
185187
targetNamespaces, err := getOperatorTargetNamespaces(op.SubscriptionNamespace)
186188
if err != nil {
187189
log.Error("Failed to get target namespaces for operator %s: %v", csv.Name, err)
@@ -200,7 +202,7 @@ func createOperators(csvs []*olmv1Alpha.ClusterServiceVersion,
200202
return operators
201203
}
202204

203-
func getAtLeastOneSubscription(op *Operator, csv *olmv1Alpha.ClusterServiceVersion, subscriptions []olmv1Alpha.Subscription) (atLeastOneSubscription bool) {
205+
func getAtLeastOneSubscription(op *Operator, csv *olmv1Alpha.ClusterServiceVersion, subscriptions []olmv1Alpha.Subscription, packageManifests []*autodiscover.PackageManifest) (atLeastOneSubscription bool) {
204206
atLeastOneSubscription = false
205207
for s := range subscriptions {
206208
subscription := &subscriptions[s]
@@ -214,11 +216,32 @@ func getAtLeastOneSubscription(op *Operator, csv *olmv1Alpha.ClusterServiceVersi
214216
op.Org = subscription.Spec.CatalogSource
215217
op.Channel = subscription.Spec.Channel
216218
atLeastOneSubscription = true
219+
220+
// If the channel is not present in the subscription, get the default channel from the package manifest
221+
if op.Channel == "" {
222+
aPackageManifest := getPackageManifestWithSubscription(subscription, packageManifests)
223+
if aPackageManifest != nil {
224+
op.Channel = aPackageManifest.Status.DefaultChannel
225+
} else {
226+
log.Error("Could not determine the default channel, this operator will always fail certification")
227+
}
228+
}
217229
break
218230
}
219231
return atLeastOneSubscription
220232
}
221233

234+
func getPackageManifestWithSubscription(subscription *olmv1Alpha.Subscription, packageManifests []*autodiscover.PackageManifest) *autodiscover.PackageManifest {
235+
for index := range packageManifests {
236+
if packageManifests[index].Status.PackageName == subscription.Spec.Package &&
237+
packageManifests[index].Metadata.Namespace == subscription.Spec.CatalogSourceNamespace &&
238+
packageManifests[index].Status.CatalogSource == subscription.Spec.CatalogSource {
239+
return packageManifests[index]
240+
}
241+
}
242+
return nil
243+
}
244+
222245
func getAtLeastOneCsv(csv *olmv1Alpha.ClusterServiceVersion, installPlan *olmv1Alpha.InstallPlan) (atLeastOneCsv bool) {
223246
atLeastOneCsv = false
224247
for _, csvName := range installPlan.Spec.ClusterServiceVersionNames {

pkg/provider/operators_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
olmv1 "github.com/operator-framework/api/pkg/operators/v1"
2525
olmv1Alpha "github.com/operator-framework/api/pkg/operators/v1alpha1"
2626
"github.com/redhat-best-practices-for-k8s/certsuite/internal/clientsholder"
27+
"github.com/redhat-best-practices-for-k8s/certsuite/pkg/autodiscover"
2728
"github.com/stretchr/testify/assert"
2829
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2930
"k8s.io/apimachinery/pkg/runtime"
@@ -296,7 +297,8 @@ func TestCreateOperators(t *testing.T) {
296297
_ = clientsholder.GetTestClientsHolder(nil)
297298
clientsholder.SetupFakeOlmClient(runtimeObjects)
298299

299-
ops := createOperators(tc.csvs, tc.subscriptions, tc.installPlan, tc.catalogSource, false, true)
300+
emptyManifests := []*autodiscover.PackageManifest{}
301+
ops := createOperators(tc.csvs, tc.subscriptions, emptyManifests, tc.installPlan, tc.catalogSource, false, true)
300302
assert.Equal(t, tc.expectedOperators, ops)
301303
}
302304
}

pkg/provider/provider.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,11 @@ type TestEnvironment struct { // rename this with testTarget
112112
ResourceQuotas []corev1.ResourceQuota
113113
PodDisruptionBudgets []policyv1.PodDisruptionBudget
114114
NetworkPolicies []networkingv1.NetworkPolicy
115-
AllInstallPlans []*olmv1Alpha.InstallPlan `json:"AllInstallPlans"`
116-
AllSubscriptions []olmv1Alpha.Subscription `json:"AllSubscriptions"`
117-
AllCatalogSources []*olmv1Alpha.CatalogSource `json:"-"`
118-
OperatorGroups []*olmv1.OperatorGroup `json:"OperatorGroups"`
115+
AllInstallPlans []*olmv1Alpha.InstallPlan `json:"AllInstallPlans"`
116+
AllSubscriptions []olmv1Alpha.Subscription `json:"AllSubscriptions"`
117+
AllCatalogSources []*olmv1Alpha.CatalogSource `json:"AllCatalogSources"`
118+
AllPackageManifests []*autodiscover.PackageManifest `json:"AllPackageManifests"`
119+
OperatorGroups []*olmv1.OperatorGroup `json:"OperatorGroups"`
119120
IstioServiceMeshFound bool
120121
ValidProtocolNames []string
121122
DaemonsetFailedToSpawn bool
@@ -240,7 +241,8 @@ func buildTestEnvironment() { //nolint:funlen
240241
}
241242
env.AllSubscriptions = data.AllSubscriptions
242243
env.AllCatalogSources = data.AllCatalogSources
243-
env.AllOperators = createOperators(data.AllCsvs, data.AllSubscriptions, data.AllInstallPlans, data.AllCatalogSources, false, true)
244+
env.AllPackageManifests = data.AllPackageManifests
245+
env.AllOperators = createOperators(data.AllCsvs, data.AllSubscriptions, data.AllPackageManifests, data.AllInstallPlans, data.AllCatalogSources, false, true)
244246
env.AllOperatorsSummary = getSummaryAllOperators(env.AllOperators)
245247
env.AllCrds = data.AllCrds
246248
env.Namespaces = data.Namespaces
@@ -344,7 +346,7 @@ func buildTestEnvironment() { //nolint:funlen
344346
env.CollectorAppPassword = data.CollectorAppPassword
345347
env.CollectorAppEndpoint = data.CollectorAppEndpoint
346348

347-
operators := createOperators(data.Csvs, data.AllSubscriptions,
349+
operators := createOperators(data.Csvs, data.AllSubscriptions, data.AllPackageManifests,
348350
data.AllInstallPlans, data.AllCatalogSources, false, true)
349351
env.Operators = operators
350352
log.Info("Operators found: %d", len(env.Operators))

tests/certification/suite.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@ func testAllOperatorCertified(check *checksdb.Check, env *provider.TestEnvironme
134134
}
135135
for _, operator := range operatorsUnderTest {
136136
check.LogInfo("Testing Operator %q", operator)
137+
if operator.Channel == "" {
138+
check.LogError("Operator %q failed to be certified for OpenShift %s because its subscription channel is empty (also failing to retrieve default channel)", operator.Name, ocpMinorVersion)
139+
nonCompliantObjects = append(nonCompliantObjects, testhelper.NewOperatorReportObject(operator.Namespace, operator.Name,
140+
"Operator failed to be certified for OpenShift because its subscription does not have a valid channel (non-empty) and a default channel could not be found"+
141+
". Maybe this operator was using a default channel and its catalog source was removed.", false).
142+
AddField(testhelper.OCPVersion, ocpMinorVersion).
143+
AddField(testhelper.OCPChannel, operator.Channel))
144+
}
137145
isCertified := validator.IsOperatorCertified(operator.Name, ocpMinorVersion, operator.Channel)
138146
if !isCertified {
139147
check.LogError("Operator %q (channel %q) failed to be certified for OpenShift %s", operator.Name, operator.Channel, ocpMinorVersion)

0 commit comments

Comments
 (0)