Skip to content

Commit

Permalink
operator: image bundle < 1000 references test (#2549)
Browse files Browse the repository at this point in the history
* operator: image bundle < 1000 references test

Look through CSVs and PackageManifests

Remove curl lookup; use relatedImages

Add filter for already reported catalogsources

additional cleanup; catalogsource pkg and tests

total up channel entries for bundle count

cleanup and rebuild catalog

* use grpcurl for ocp 4.12 or below

* Remove packageManifest name filter

* Test grpcurl

* Add skip for channel entry loop
  • Loading branch information
sebrandon1 authored Dec 5, 2024
1 parent af93477 commit c90ecc2
Show file tree
Hide file tree
Showing 19 changed files with 539 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pre-main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ env:
OCT_IMAGE_NAME: redhat-best-practices-for-k8s/oct
OCT_IMAGE_TAG: latest
PROBE_IMAGE_NAME: redhat-best-practices-for-k8s/certsuite-probe
PROBE_IMAGE_TAG: v0.0.10
PROBE_IMAGE_TAG: v0.0.11
CERTSUITE_CONFIG_DIR: /tmp/certsuite/config
CERTSUITE_OUTPUT_DIR: /tmp/certsuite/output
SMOKE_TESTS_LOG_LEVEL: debug
Expand Down
24 changes: 20 additions & 4 deletions CATALOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Depending on the workload type, not all tests are required to pass to satisfy be

## Test cases summary

### Total test cases: 116
### Total test cases: 117

### Total suites: 10

Expand All @@ -19,7 +19,7 @@ Depending on the workload type, not all tests are required to pass to satisfy be
|manageability|2|
|networking|12|
|observability|5|
|operator|10|
|operator|11|
|performance|6|
|platform-alteration|13|
|preflight|17|
Expand All @@ -36,11 +36,11 @@ Depending on the workload type, not all tests are required to pass to satisfy be
|---|---|
|8|1|

### Non-Telco specific tests only: 68
### Non-Telco specific tests only: 69

|Mandatory|Optional|
|---|---|
|43|25|
|44|25|

### Telco specific tests only: 27

Expand Down Expand Up @@ -1186,6 +1186,22 @@ Tags|telco,observability

### operator

#### operator-catalogsource-bundle-count

Property|Description
---|---
Unique ID|operator-catalogsource-bundle-count
Description|Tests operator catalog source bundle count is less than 1000
Suggested Remediation|Ensure that the Operator's catalog source has a valid bundle count less than 1000.
Best Practice Reference|https://redhat-best-practices-for-k8s.github.io/guide/#redhat-best-practices-for-k8s-cnf-operator-requirements
Exception Process|No exceptions
Tags|common,operator
|**Scenario**|**Optional/Mandatory**|
|Extended|Mandatory|
|Far-Edge|Mandatory|
|Non-Telco|Mandatory|
|Telco|Mandatory|

#### operator-crd-openapi-schema

Property|Description
Expand Down
2 changes: 1 addition & 1 deletion cmd/certsuite/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func NewCommand() *cobra.Command {
runCmd.PersistentFlags().Bool("include-web-files", false, "Save web files in the configured output folder")
runCmd.PersistentFlags().Bool("enable-data-collection", false, "Allow sending test results to an external data collector")
runCmd.PersistentFlags().Bool("create-xml-junit-file", false, "Create a JUnit file with the test results")
runCmd.PersistentFlags().String("certsuite-probe-image", "quay.io/redhat-best-practices-for-k8s/certsuite-probe:v0.0.10", "Certsuite probe image")
runCmd.PersistentFlags().String("certsuite-probe-image", "quay.io/redhat-best-practices-for-k8s/certsuite-probe:v0.0.11", "Certsuite probe image")
runCmd.PersistentFlags().String("daemonset-cpu-req", "100m", "CPU request for the probe daemonset container")
runCmd.PersistentFlags().String("daemonset-cpu-lim", "100m", "CPU limit for the probe daemonset container")
runCmd.PersistentFlags().String("daemonset-mem-req", "100M", "Memory request for the probe daemonset container")
Expand Down
2 changes: 1 addition & 1 deletion docs/runtime-env.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,4 @@ See more about this variable [here](https://github.com/redhat-openshift-ecosyste
against a private container registry that has self-signed certificates.

Note that you can also specify the probe pod image to use with `SUPPORT_IMAGE`
environment variable, default to `certsuite-probe:v0.0.10`.
environment variable, default to `certsuite-probe:v0.0.11`.
2 changes: 1 addition & 1 deletion expected_results.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ testCases:
- platform-alteration-tainted-node-kernel
fail:
- affiliated-certification-container-is-certified-digest # test container image is not certified

skip:
- access-control-sys-ptrace-capability
- affiliated-certification-helm-version
Expand All @@ -87,6 +86,7 @@ testCases:
- operator-single-crd-owner
- operator-pods-no-hugepages
- operator-multiple-same-operators
- operator-catalogsource-bundle-count
- performance-exclusive-cpu-pool-rt-scheduling-policy
- performance-isolated-cpu-pool-rt-scheduling-policy
- performance-shared-cpu-pool-non-rt-scheduling-policy
Expand Down
8 changes: 8 additions & 0 deletions pkg/autodiscover/autodiscover.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ type DiscoveredTestData struct {
RoleBindings []rbacv1.RoleBinding // Contains all rolebindings from all namespaces
Roles []rbacv1.Role // Contains all roles from all namespaces
Services []*corev1.Service
AllServices []*corev1.Service
ServiceAccounts []*corev1.ServiceAccount
AllServiceAccounts []*corev1.ServiceAccount
Hpas []*scalingv1.HorizontalPodAutoscaler
Expand Down Expand Up @@ -169,7 +170,10 @@ func DoAutoDiscover(config *configuration.TestConfiguration) DiscoveredTestData
}
data.AllInstallPlans = getAllInstallPlans(oc.OlmClient)
data.AllCatalogSources = getAllCatalogSources(oc.OlmClient)
log.Info("Collected %d catalog sources during autodiscovery", len(data.AllCatalogSources))

data.AllPackageManifests = getAllPackageManifests(oc.OlmPkgClient)

data.Namespaces = namespacesListToStringList(config.TargetNameSpaces)
data.Pods, data.AllPods = findPodsByLabels(oc.K8sClient.CoreV1(), podsUnderTestLabelsObjects, data.Namespaces)
data.AbnormalEvents = findAbnormalEvents(oc.K8sClient.CoreV1(), data.Namespaces)
Expand Down Expand Up @@ -276,6 +280,10 @@ func DoAutoDiscover(config *configuration.TestConfiguration) DiscoveredTestData
if err != nil {
log.Fatal("Cannot get list of services, err: %v", err)
}
data.AllServices, err = getServices(oc.K8sClient.CoreV1(), data.AllNamespaces, data.ServicesIgnoreList)
if err != nil {
log.Fatal("Cannot get list of all services, err: %v", err)
}
data.ServiceAccounts, err = getServiceAccounts(oc.K8sClient.CoreV1(), data.Namespaces)
if err != nil {
log.Fatal("Cannot get list of service accounts under test, err: %v", err)
Expand Down
106 changes: 106 additions & 0 deletions pkg/provider/catalogsources.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package provider

import (
"strconv"
"strings"

"github.com/Masterminds/semver"
olmv1Alpha "github.com/operator-framework/api/pkg/operators/v1alpha1"
"github.com/redhat-best-practices-for-k8s/certsuite/internal/clientsholder"
"github.com/redhat-best-practices-for-k8s/certsuite/internal/log"
)

func GetCatalogSourceBundleCount(env *TestEnvironment, cs *olmv1Alpha.CatalogSource) int {
// Now that we know the catalog source, we are going to count up all of the relatedImages
// that are associated with the catalog source. This will give us the number of bundles that
// are available in the catalog source.

// If the OCP version is <= 4.12, we need to use the probe container to get the bundle count
const (
ocpMajorVersion = 4
ocpMinorVersion = 12
)

// Check if the cluster is running an OCP version <= 4.12
if env.OpenshiftVersion != "" {
log.Info("Cluster is determined to be running Openshift version %q.", env.OpenshiftVersion)
version, err := semver.NewVersion(env.OpenshiftVersion)
if err != nil {
log.Error("Failed to parse Openshift version %q.", env.OpenshiftVersion)
return 0
}

if version.Major() < ocpMajorVersion || (version.Major() == ocpMajorVersion && version.Minor() <= ocpMinorVersion) {
return getCatalogSourceBundleCountFromProbeContainer(env, cs)
}

// If we didn't find the bundle count via the probe container, we can attempt to use the package manifests
}

// If we didn't find the bundle count via the probe container, we can use the package manifests
// to get the bundle count
return getCatalogSourceBundleCountFromPackageManifests(env, cs)
}

func getCatalogSourceBundleCountFromProbeContainer(env *TestEnvironment, cs *olmv1Alpha.CatalogSource) int {
// We need to use the probe container to get the bundle count
// This is because the package manifests are not available in the cluster
// for OCP versions <= 4.12
o := clientsholder.GetClientsHolder()

// Find the kubernetes service associated with the catalog source
for _, svc := range env.AllServices {
// Skip if the service is not associated with the catalog source
if svc.Spec.Selector["olm.catalogSource"] != cs.Name {
continue
}

log.Info("Found service %q associated with catalog source %q.", svc.Name, cs.Name)

// Use a probe pod to get the bundle count
for _, probePod := range env.ProbePods {
ctx := clientsholder.NewContext(probePod.Namespace, probePod.Name, probePod.Spec.Containers[0].Name)
cmd := "grpcurl -plaintext " + svc.Spec.ClusterIP + ":50051 api.Registry.ListBundles | jq -s 'length'"
cmdValue, errStr, err := o.ExecCommandContainer(ctx, cmd)
if err != nil || errStr != "" {
log.Error("Failed to execute command %s in probe pod %s", cmd, probePod.String())
continue
}

// Sanitize the command output
cmdValue = strings.TrimSpace(cmdValue)
cmdValue = strings.Trim(cmdValue, "\"")

// Parse the command output
bundleCount, err := strconv.Atoi(cmdValue)
if err != nil {
log.Error("Failed to convert bundle count to integer: %s", cmdValue)
continue
}

// Try each probe pod until we get a valid bundle count (which should only be 1 probe pod)
log.Info("Found bundle count via grpcurl %d for catalog source %q.", bundleCount, cs.Name)
return bundleCount
}
}

log.Warn("Warning: No services found associated with catalog source %q.", cs.Name)
return -1
}

func getCatalogSourceBundleCountFromPackageManifests(env *TestEnvironment, cs *olmv1Alpha.CatalogSource) int {
totalRelatedBundles := 0
for _, pm := range env.AllPackageManifests {
// Skip if the package manifest is not associated with the catalog source
if pm.Status.CatalogSource != cs.Name || pm.Status.CatalogSourceNamespace != cs.Namespace {
continue
}

// Count up the number of related bundles
for c := range pm.Status.Channels {
totalRelatedBundles += len(pm.Status.Channels[c].Entries)
}
}

return totalRelatedBundles
}
85 changes: 85 additions & 0 deletions pkg/provider/catalogsources_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package provider

import (
"testing"

olmv1Alpha "github.com/operator-framework/api/pkg/operators/v1alpha1"
olmpkgv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestGetCatalogSourceBundleCount(t *testing.T) {
generateEnv := func(channelEntries []olmpkgv1.ChannelEntry) *TestEnvironment {
return &TestEnvironment{
AllPackageManifests: []*olmpkgv1.PackageManifest{
{
Status: olmpkgv1.PackageManifestStatus{
CatalogSource: "test-catalog-source",
CatalogSourceNamespace: "test-catalog-source-namespace",
Channels: []olmpkgv1.PackageChannel{
{
Entries: channelEntries,
},
},
},
},
},
}
}

testCases := []struct {
testEnv *TestEnvironment
testCS *olmv1Alpha.CatalogSource
expected int
}{
{ // Test case 1
testEnv: generateEnv([]olmpkgv1.ChannelEntry{
{
Name: "test-csv.v1.0.0",
},
{
Name: "test-csv.v1.0.1",
},
}),
testCS: &olmv1Alpha.CatalogSource{
ObjectMeta: metav1.ObjectMeta{
Name: "test-catalog-source",
Namespace: "test-catalog-source-namespace",
},
},
expected: 2,
},
{ // Test Case 2 - No matching catalog source found, expecting 0
testEnv: generateEnv([]olmpkgv1.ChannelEntry{
{
Name: "test-csv.v1.0.0",
},
{
Name: "test-csv.v1.0.1",
},
}),
testCS: &olmv1Alpha.CatalogSource{
ObjectMeta: metav1.ObjectMeta{
Name: "test-catalog-source2",
Namespace: "test-catalog-source-namespace",
},
},
expected: 0,
},
{ // Test Case 3 - No images in the catalog source, expecting 0
testEnv: generateEnv([]olmpkgv1.ChannelEntry{}),
testCS: &olmv1Alpha.CatalogSource{
ObjectMeta: metav1.ObjectMeta{
Name: "test-catalog-source",
Namespace: "test-catalog-source-namespace",
},
},
expected: 0,
},
}

for _, testCase := range testCases {
assert.Equal(t, testCase.expected, GetCatalogSourceBundleCount(testCase.testEnv, testCase.testCS))
}
}
3 changes: 3 additions & 0 deletions pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ type TestEnvironment struct { // rename this with testTarget

HorizontalScaler []*scalingv1.HorizontalPodAutoscaler `json:"testHorizontalScaler"`
Services []*corev1.Service `json:"testServices"`
AllServices []*corev1.Service `json:"testAllServices"`
ServiceAccounts []*corev1.ServiceAccount `json:"testServiceAccounts"`
AllServiceAccounts []*corev1.ServiceAccount `json:"AllServiceAccounts"`
AllServiceAccountsMap map[string]*corev1.ServiceAccount
Expand Down Expand Up @@ -261,6 +262,7 @@ func buildTestEnvironment() { //nolint:funlen,gocyclo
aEvent := NewEvent(&data.AbnormalEvents[i])
env.AbnormalEvents = append(env.AbnormalEvents, &aEvent)
}

// Service accounts
env.ServiceAccounts = data.ServiceAccounts
env.AllServiceAccounts = data.AllServiceAccounts
Expand Down Expand Up @@ -334,6 +336,7 @@ func buildTestEnvironment() { //nolint:funlen,gocyclo
env.RoleBindings = data.RoleBindings
env.Roles = data.Roles
env.Services = data.Services
env.AllServices = data.AllServices
env.NetworkPolicies = data.NetworkPolicies
for _, nsHelmChartReleases := range data.HelmChartReleases {
for _, helmChartRelease := range nsHelmChartReleases {
Expand Down
20 changes: 20 additions & 0 deletions pkg/testhelper/testhelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ const (
HelmType = "Helm"
OperatorType = "Operator"
ContainerType = "Container"
CatalogSourceType = "Catalog Source"
ContainerImageType = "Container Image"
NodeType = "Node"
OCPClusterType = "OCP Cluster"
Expand Down Expand Up @@ -309,6 +310,16 @@ func NewOperatorReportObject(aNamespace, aOperatorName, aReason string, isCompli
return out
}

// NewCatalogSourceReportObject creates a new ReportObject for a catalog source.
// It takes the namespace, catalog source name, reason, and compliance status as input parameters.
// It returns the created ReportObject.
func NewCatalogSourceReportObject(aNamespace, aCatalogSourceName, aReason string, isCompliant bool) (out *ReportObject) {
out = NewReportObject(aReason, CatalogSourceType, isCompliant)
out.AddField(Namespace, aNamespace)
out.AddField(Name, aCatalogSourceName)
return out
}

// NewDeploymentReportObject creates a new ReportObject for a deployment.
// It takes the namespace, deployment name, reason, and compliance status as input parameters.
// It returns a pointer to the created ReportObject.
Expand Down Expand Up @@ -638,6 +649,15 @@ func GetNoHugepagesPodsSkipFn(env *provider.TestEnvironment) func() (bool, strin
}
}

func GetNoCatalogSourcesSkipFn(env *provider.TestEnvironment) func() (bool, string) {
return func() (bool, string) {
if len(env.AllCatalogSources) == 0 {
return true, "no catalog sources found"
}
return false, ""
}
}

func GetNoOperatorsSkipFn(env *provider.TestEnvironment) func() (bool, string) {
return func() (bool, string) {
if len(env.Operators) == 0 {
Expand Down
Loading

0 comments on commit c90ecc2

Please sign in to comment.