Skip to content

Commit e45b404

Browse files
author
Moritz Clasmeier
committed
Merge branch 'main' into mc/olm-tests
2 parents 0f11508 + cd8a8d5 commit e45b404

9 files changed

Lines changed: 159 additions & 82 deletions

File tree

cmd/deploy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func runDeploy(cmd *cobra.Command, args []string) error {
111111
return errors.New("cannot use both --olm and --konflux flags together (not currently implemented)")
112112
}
113113
clusterType := env.GetCurrentClusterType()
114-
if clusterType != env.InfraOpenShift4 {
114+
if !clusterType.IsOpenShift() {
115115
return fmt.Errorf("--konflux flag is only supported on OpenShift 4 clusters (current cluster type: %s)", clusterType.String())
116116
}
117117
}

internal/deployer/deploy_via_operator.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ func (d *Deployer) ensureOperatorDeployed(ctx context.Context) error {
5555
}
5656

5757
// Detect current operator deployment mode
58-
operatorExists, currentMode := d.detectOperatorDeploymentMode(ctx)
58+
operatorExists, currentMode, err := d.detectOperatorDeploymentMode(ctx)
59+
if err != nil {
60+
return fmt.Errorf("detecting operator deployment mode: %w", err)
61+
}
5962
needsDeployment := false
6063
needsTeardown := false
6164

internal/deployer/deployer.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ var (
100100
}
101101
)
102102

103+
const (
104+
injectedCABundleConfigMap = "injected-cabundle-stackrox-central-services"
105+
)
106+
103107
// Deployer is the base deployer for ACS
104108
type Deployer struct {
105109
logger *logger.Logger
@@ -222,6 +226,9 @@ func (d *Deployer) deleteCentralResources(ctx context.Context, wait bool) error
222226
{Name: "central-db-backup", Kind: "pvc", OwnerName: centralCrName},
223227
{Name: "admin-password", Kind: "secret"},
224228
{Name: "scanner-db-password", Kind: "secret", OwnerName: centralCrName},
229+
// In case the Cluster Network Operator has succeeded in re-creating the injectedCABundleConfigMap
230+
// after our operator has already deleted it.
231+
{Name: injectedCABundleConfigMap, Kind: "configmap"},
225232
} {
226233
d.logger.Dimf("Attempting to delete %s/%s", resource.Kind, resource.Name)
227234
if resource.OwnerName != "" {
@@ -261,11 +268,7 @@ func (d *Deployer) preventOtherControllersFromReconciling(ctx context.Context) e
261268
}
262269

263270
func (d *Deployer) preventCABundleInjection(ctx context.Context) error {
264-
configMapName := "injected-cabundle-stackrox-central-services"
265-
266-
if !d.doesResourceExist(ctx, "configmap", configMapName, d.centralNamespace) {
267-
return nil
268-
}
271+
configMapName := injectedCABundleConfigMap
269272

270273
d.logger.Info("Removing CNO label from injected-cabundle ConfigMap to prevent CNO from injecting the CA bundle during cleanup")
271274
_, err := d.runKubectl(ctx, k8s.KubectlOptions{

internal/deployer/operator.go

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ func (d *Deployer) downloadAndExtractOperatorBundle(ctx context.Context, bundleI
9191
return bundleDir, nil
9292
}
9393

94-
// identifyCRDFileNames identifies CRD files in the bundle directory
94+
// identifyCRDFileNames identifies CRD files in the bundle directory.
95+
// Returns list of CRD files found in the bundle.
9596
func (d *Deployer) identifyCRDFileNames(bundleDir string) ([]string, error) {
9697
var crdFiles []string
9798

@@ -108,24 +109,22 @@ func (d *Deployer) identifyCRDFileNames(bundleDir string) ([]string, error) {
108109
return nil
109110
}
110111

111-
// TODO(ROX-34499): The following detection logic does not seem particularly robust. We should
112-
// probably parse the YAML and check api group and kind fields.
113-
name := strings.ToLower(info.Name())
114-
if strings.Contains(name, "customresourcedefinition") ||
115-
strings.Contains(name, "platform.stackrox.io") ||
116-
strings.Contains(name, "config.stackrox.io") {
117-
if strings.Contains(name, "clusterserviceversion") {
118-
return nil
119-
}
112+
content, err := os.ReadFile(path)
113+
if err != nil {
114+
d.logger.Warningf("Failed to read file %q from extracted bundle: %v", path, err)
115+
return nil
116+
}
120117

121-
content, err := os.ReadFile(path)
122-
if err != nil {
123-
return nil
124-
}
118+
var meta struct {
119+
Kind string `yaml:"kind"`
120+
}
121+
if err := yaml.Unmarshal(content, &meta); err != nil {
122+
d.logger.Warningf("Failed to unmarshal file %q from extracted bundle: %v", path, err)
123+
return nil
124+
}
125125

126-
if strings.Contains(string(content), "kind: CustomResourceDefinition") {
127-
crdFiles = append(crdFiles, path)
128-
}
126+
if meta.Kind == "CustomResourceDefinition" {
127+
crdFiles = append(crdFiles, path)
129128
}
130129

131130
return nil
@@ -202,7 +201,7 @@ func (d *Deployer) getOperatorBundleImage() string {
202201

203202
// ensureKonfluxImageRewriting configures image rewriting for Konflux images
204203
func (d *Deployer) ensureKonfluxImageRewriting(ctx context.Context) error {
205-
if env.GetCurrentClusterType() != env.InfraOpenShift4 {
204+
if !env.GetCurrentClusterType().IsOpenShift() {
206205
return errors.New("image rewriting for Konflux is only supported on OpenShift4 clusters")
207206
}
208207

@@ -290,7 +289,7 @@ func (d *Deployer) applyImageContentSourcePolicy(ctx context.Context) error {
290289

291290
// removeKonfluxImageRewriting removes the ImageContentSourcePolicy for Konflux images if it exists
292291
func (d *Deployer) removeKonfluxImageRewriting(ctx context.Context) error {
293-
if env.GetCurrentClusterType() != env.InfraOpenShift4 {
292+
if !env.GetCurrentClusterType().IsOpenShift() {
294293
return nil
295294
}
296295

@@ -643,7 +642,10 @@ func (d *Deployer) teardownOperatorNonOLM(ctx context.Context) error {
643642

644643
// teardownOperator removes the operator if it exists, detecting the deployment mode automatically.
645644
func (d *Deployer) teardownOperator(ctx context.Context) error {
646-
operatorExists, operatorMode := d.detectOperatorDeploymentMode(ctx)
645+
operatorExists, operatorMode, err := d.detectOperatorDeploymentMode(ctx)
646+
if err != nil {
647+
return fmt.Errorf("detecting operator deployment mode: %w", err)
648+
}
647649
if !operatorExists {
648650
d.logger.Dim("No operator deployment found, skipping operator teardown")
649651
return nil

internal/deployer/operator_olm.go

Lines changed: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -72,26 +72,46 @@ func (d *Deployer) deployOperatorViaOLM(ctx context.Context) error {
7272
return nil
7373
}
7474

75-
// checkOLMInstalled checks if OLM is installed in the cluster.
75+
// checkOLMInstalled checks if OLM is installed in the cluster by verifying
76+
// the API server is ready to serve the required OLM resource types.
7677
func (d *Deployer) checkOLMInstalled(ctx context.Context) error {
77-
// Check for OLM CRDs
78-
requiredCRDs := []string{
78+
requiredResources := []string{
7979
"catalogsources.operators.coreos.com",
8080
"subscriptions.operators.coreos.com",
8181
"installplans.operators.coreos.com",
8282
"clusterserviceversions.operators.coreos.com",
8383
}
8484

85-
for _, crd := range requiredCRDs {
86-
// TODO(ROX-34499): actually this is not the right way to check whether it's safe to create a resource of a given kind.
87-
// A CRD can be present, but still being loaded or end up not accepted by the API server.
88-
// Instead we should use the `kubectl api-resources` subcommand which exposes the status we're looking for.
89-
_, err := d.runKubectl(ctx, k8s.KubectlOptions{
90-
Args: []string{"get", "crd", crd},
91-
})
92-
if err != nil {
93-
return fmt.Errorf("OLM not installed: CRD %s not found. Please install OLM first", crd)
85+
result, err := d.runKubectl(ctx, k8s.KubectlOptions{
86+
Args: []string{"api-resources", "--api-group=operators.coreos.com", "-o", "name"},
87+
})
88+
if err != nil {
89+
if result.Stderr != "" {
90+
d.logger.Error("kubectl stderr:")
91+
for stderrLine := range strings.SplitSeq(result.Stderr, "\n") {
92+
d.logger.Errorf("stderr: %s", stderrLine)
93+
}
94+
}
95+
return fmt.Errorf("failed to query api-group operators.coreos.com: %w", err)
96+
}
97+
98+
available := make(map[string]bool)
99+
for line := range strings.SplitSeq(strings.TrimSpace(result.Stdout), "\n") {
100+
name := strings.TrimSpace(line)
101+
available[name] = true
102+
}
103+
104+
var missingResources []string
105+
for _, resource := range requiredResources {
106+
if !available[resource] {
107+
missingResources = append(missingResources, resource)
108+
}
109+
}
110+
if len(missingResources) > 0 {
111+
for _, resource := range missingResources {
112+
d.logger.Errorf("OLM resource not served by the API server: %s", resource)
94113
}
114+
return fmt.Errorf("OLM is not properly installed, %d required resource(s) missing", len(missingResources))
95115
}
96116

97117
d.logger.Success("✓ OLM detected in cluster")
@@ -335,35 +355,34 @@ func (d *Deployer) waitForCSVSuccess(ctx context.Context) error {
335355

336356
// detectOperatorDeploymentMode detects how the operator is currently deployed.
337357
// Returns (operatorExists bool, isOLM OperatorDeploymentMode)
338-
func (d *Deployer) detectOperatorDeploymentMode(ctx context.Context) (bool, OperatorDeploymentMode) {
358+
func (d *Deployer) detectOperatorDeploymentMode(ctx context.Context) (bool, OperatorDeploymentMode, error) {
359+
const olmOwnerLabel = "olm.owner"
360+
339361
// First, check if a Subscription exists (OLM-specific resource)
340362
_, err := d.runKubectl(ctx, k8s.KubectlOptions{
341363
Args: []string{"get", "subscription", subscriptionName, "-n", operatorNamespace},
342364
})
343365
if err == nil {
344-
return true, OperatorModeOLM
366+
return true, OperatorModeOLM, nil
345367
}
346368

347-
// If no subscription, check if operator deployment exists.
348-
_, err = d.runKubectl(ctx, k8s.KubectlOptions{
349-
Args: []string{"get", "deployment", operatorDeploymentName, "-n", operatorNamespace},
350-
})
351-
if err == nil {
352-
// Deployment exists - check if it has OLM owner labels.
353-
result, err := d.runKubectl(ctx, k8s.KubectlOptions{
354-
Args: []string{"get", "deployment", operatorDeploymentName, "-n", operatorNamespace, "-o", "jsonpath={.metadata.labels}"},
355-
})
356-
// TODO(ROX-34499): This is not very robust. Better retrieve a specific label in the `get`
357-
// command?
358-
if err == nil && strings.Contains(result.Stdout, "olm.owner") {
359-
return true, OperatorModeOLM
360-
}
361-
// Deployment exists without OLM labels = non-OLM deployment.
362-
return true, OperatorModeNonOLM
369+
// If no subscription, check if operator deployment exists/if it has the expected OLM label.
370+
labelValue, err := k8s.RetrieveClusterResourceLabel(ctx, d.logger, operatorNamespace, "deployment", operatorDeploymentName, olmOwnerLabel)
371+
if k8s.IsResourceNotFound(err) {
372+
// No operator deployment found.
373+
return false, OperatorModeNonOLM, nil
374+
}
375+
if err != nil {
376+
return false, OperatorModeNonOLM, err
377+
}
378+
379+
if labelValue == "" {
380+
// Deployment exists without OLM labels -> non-OLM deployment.
381+
return true, OperatorModeNonOLM, nil
363382
}
364383

365-
// No operator found.
366-
return false, OperatorModeNonOLM
384+
// Label set -> OLM deployment.
385+
return true, OperatorModeOLM, nil
367386
}
368387

369388
// teardownOperatorOLM removes the operator when installed via OLM.

internal/env/env.go

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ const (
3333
InfraGKE
3434
// InfraOpenShift4 represents an OpenShift 4 cluster
3535
InfraOpenShift4
36+
// Generic OpenShift4 cluster (e.g. for prow CI)
37+
OpenShift4
3638
// LocalKind represents a Kind (Kubernetes in Docker) cluster
3739
LocalKind
3840
)
@@ -115,6 +117,8 @@ func (ct ClusterType) String() string {
115117
case InfraGKE:
116118
return "GKE"
117119
case InfraOpenShift4:
120+
return "OpenShift4 (infra)"
121+
case OpenShift4:
118122
return "OpenShift4"
119123
case LocalKind:
120124
return "Kind"
@@ -123,6 +127,10 @@ func (ct ClusterType) String() string {
123127
}
124128
}
125129

130+
func (ct ClusterType) IsOpenShift() bool {
131+
return ct == InfraOpenShift4 || ct == OpenShift4
132+
}
133+
126134
// KubeConfig represents a simplified kubectl configuration
127135
type KubeConfig struct {
128136
CurrentContext string
@@ -183,17 +191,12 @@ func detectClusterType(config KubeConfig, apiResources []string) ClusterType {
183191
return InfraGKE
184192
}
185193

186-
// Check for OpenShift 4 clusters by examining the server hostname
187-
if serverURL := getServerURL(config); serverURL != "" {
188-
if parsedURL, err := url.Parse(serverURL); err == nil {
189-
hostname := parsedURL.Hostname()
190-
if strings.HasSuffix(hostname, ".ocp.infra.rox.systems") {
191-
// Further verify it's OpenShift 4 by checking the API resources
192-
if isOpenShift4(apiResources) {
193-
return InfraOpenShift4
194-
}
195-
}
194+
// Check for OpenShift 4 clusters.
195+
if isOpenShift4(apiResources) {
196+
if isInfraOpenShift4(config) {
197+
return InfraOpenShift4
196198
}
199+
return OpenShift4
197200
}
198201

199202
// Check for Kind clusters
@@ -205,6 +208,19 @@ func detectClusterType(config KubeConfig, apiResources []string) ClusterType {
205208
return ClusterTypeUnknown
206209
}
207210

211+
func isInfraOpenShift4(config KubeConfig) bool {
212+
serverURL := getServerURL(config)
213+
if serverURL == "" {
214+
return false
215+
}
216+
parsedURL, err := url.Parse(serverURL)
217+
if err != nil {
218+
return false
219+
}
220+
hostname := parsedURL.Hostname()
221+
return strings.HasSuffix(hostname, ".ocp.infra.rox.systems")
222+
}
223+
208224
// getServerURL retrieves the server URL from the KubeConfig
209225
func getServerURL(config KubeConfig) string {
210226
if len(config.Clusters) == 0 {

internal/env/env_integration_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func TestDetectClusterType_Integration(t *testing.T) {
1919
t.Logf("Detected cluster type: %s", clusterType)
2020

2121
// The cluster type should never be invalid (even if Unknown)
22-
validTypes := []ClusterType{ClusterTypeUnknown, InfraGKE, InfraOpenShift4, LocalKind}
22+
validTypes := []ClusterType{ClusterTypeUnknown, InfraGKE, InfraOpenShift4, OpenShift4, LocalKind}
2323
found := false
2424
for _, valid := range validTypes {
2525
if clusterType == valid {

internal/env/env_test.go

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package env
22

33
import (
44
"testing"
5+
6+
"github.com/stretchr/testify/assert"
57
)
68

79
func TestDetectClusterType_GKE(t *testing.T) {
@@ -40,7 +42,7 @@ func TestDetectClusterType_GKE_ExactMatch(t *testing.T) {
4042
}
4143
}
4244

43-
func TestDetectClusterType_OpenShift4(t *testing.T) {
45+
func TestDetectClusterType_InfraOpenShift4(t *testing.T) {
4446
config := KubeConfig{
4547
CurrentContext: "admin",
4648
Clusters: []KubeCluster{
@@ -58,31 +60,28 @@ func TestDetectClusterType_OpenShift4(t *testing.T) {
5860
}
5961

6062
result := detectClusterType(config, apiResources)
61-
if result != InfraOpenShift4 {
62-
t.Errorf("detectClusterType() = %v (%s), want %v (%s)", result, result.String(), InfraOpenShift4, InfraOpenShift4.String())
63-
}
63+
assert.Equal(t, InfraOpenShift4, result)
6464
}
6565

66-
func TestDetectClusterType_OpenShift4_WrongHostname(t *testing.T) {
66+
func TestDetectClusterType_OpenShift4(t *testing.T) {
6767
config := KubeConfig{
68-
CurrentContext: "admin",
68+
CurrentContext: "some-context-name",
6969
Clusters: []KubeCluster{
7070
{
71-
Name: "openshift-cluster",
72-
Server: "https://api.my-cluster.example.com:6443",
71+
Name: "some-other-name",
72+
Server: "https://my-cluster.example.com:6443",
7373
},
7474
},
7575
}
7676
apiResources := []string{
7777
"pods",
7878
"services",
7979
"clusterversions.config.openshift.io",
80+
"clusteroperators.config.openshift.io",
8081
}
8182

8283
result := detectClusterType(config, apiResources)
83-
if result != ClusterTypeUnknown {
84-
t.Errorf("detectClusterType() = %v (%s), want %v (%s)", result, result.String(), ClusterTypeUnknown, ClusterTypeUnknown.String())
85-
}
84+
assert.Equal(t, OpenShift4, result)
8685
}
8786

8887
func TestDetectClusterType_OpenShift4_NoAPIResources(t *testing.T) {
@@ -295,6 +294,11 @@ func TestClusterTypeString(t *testing.T) {
295294
{
296295
name: "InfraOpenShift4",
297296
clusterType: InfraOpenShift4,
297+
want: "OpenShift4 (infra)",
298+
},
299+
{
300+
name: "OpenShift4",
301+
clusterType: OpenShift4,
298302
want: "OpenShift4",
299303
},
300304
{

0 commit comments

Comments
 (0)