Skip to content

Commit b7984ba

Browse files
committed
Add Configuration interface
Implements RFC "Arbitrary Configuration".
1 parent e69f1ec commit b7984ba

File tree

8 files changed

+149
-21
lines changed

8 files changed

+149
-21
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ manifests: $(CONTROLLER_GEN) #EXHELP Generate WebhookConfiguration, ClusterRole,
139139
$(CONTROLLER_GEN) crd paths="./api/v1/..." output:crd:artifacts:config=$(CRD_WORKING_DIR)
140140
mv $(CRD_WORKING_DIR)/olm.operatorframework.io_clusterextensions.yaml $(KUSTOMIZE_OPCON_CRDS_DIR)
141141
mv $(CRD_WORKING_DIR)/olm.operatorframework.io_clustercatalogs.yaml $(KUSTOMIZE_CATD_CRDS_DIR)
142-
rmdir $(CRD_WORKING_DIR)
142+
rm -rf $(CRD_WORKING_DIR)
143143
# Generate the remaining operator-controller manifests
144144
$(CONTROLLER_GEN) rbac:roleName=manager-role paths="./internal/operator-controller/..." output:rbac:artifacts:config=$(KUSTOMIZE_OPCON_RBAC_DIR)
145145
# Generate the remaining catalogd manifests

api/v1/bundleconfig_types.go

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package v1
2+
3+
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
4+
5+
// BundleConfig ...
6+
// +kubebuilder:object:root=true
7+
type BundleConfig struct {
8+
metav1.TypeMeta `json:",inline"`
9+
// metadata is the standard object's metadata.
10+
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
11+
metav1.ObjectMeta `json:"metadata"`
12+
13+
// spec is the desired state of the BundleConfig.
14+
// spec is required.
15+
Spec BundleConfigSpec `json:"spec"`
16+
}
17+
18+
type BundleConfigSpec struct {
19+
// watchNamespace configures what Namespace the Operator should watch.
20+
// setting it to the same value as the install namespace corresponds to OwnNamespace mode,
21+
// setting it to a different value corresponds to SingleNamespace mode.
22+
WatchNamespace string `json:"watchNamespace,omitempty"`
23+
}
24+
25+
func init() {
26+
SchemeBuilder.Register(&BundleConfig{})
27+
}

api/v1/clusterextension_types.go

+7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package v1
1818

1919
import (
2020
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
runtime "k8s.io/apimachinery/pkg/runtime"
2122
)
2223

2324
var ClusterExtensionKind = "ClusterExtension"
@@ -92,6 +93,12 @@ type ClusterExtensionSpec struct {
9293
//
9394
// +optional
9495
Install *ClusterExtensionInstallConfig `json:"install,omitempty"`
96+
97+
// config can be used to pass parameters to the underlying runtime.
98+
// +optional
99+
// +kubebuilder:validation:items:XEmbeddedResource
100+
// +kubebuilder:validation:items:XPreserveUnknownFields
101+
Config []runtime.RawExtension `json:"config,omitempty"`
95102
}
96103

97104
const SourceTypeCatalog = "Catalog"

api/v1/zz_generated.deepcopy.go

+49-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/base/operator-controller/crd/bases/olm.operatorframework.io_clusterextensions.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ spec:
5656
description: spec is an optional field that defines the desired state
5757
of the ClusterExtension.
5858
properties:
59+
config:
60+
description: config can be used to pass parameters to the underlying
61+
runtime.
62+
items:
63+
type: object
64+
x-kubernetes-embedded-resource: true
65+
x-kubernetes-preserve-unknown-fields: true
66+
type: array
5967
install:
6068
description: |-
6169
install is an optional field used to configure the installation options

internal/operator-controller/applier/helm_test.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"helm.sh/helm/v3/pkg/storage/driver"
1717
corev1 "k8s.io/api/core/v1"
1818
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19+
"k8s.io/apimachinery/pkg/runtime"
1920
featuregatetesting "k8s.io/component-base/featuregate/testing"
2021
"sigs.k8s.io/controller-runtime/pkg/client"
2122

@@ -467,8 +468,12 @@ func TestApply_InstallationWithSingleOwnNamespaceInstallSupportEnabled(t *testin
467468
testExt := &ocv1.ClusterExtension{
468469
ObjectMeta: metav1.ObjectMeta{
469470
Name: "testExt",
470-
Annotations: map[string]string{
471-
applier.AnnotationClusterExtensionWatchNamespace: expectedWatchNamespace,
471+
},
472+
Spec: ocv1.ClusterExtensionSpec{
473+
Config: []runtime.RawExtension{
474+
{Raw: []byte(
475+
`{"apiVersion":"olm.operatorframework.io/v1","kind":"BundleConfig",` +
476+
`"spec":{"watchNamespace":"` + expectedWatchNamespace + `"}}`)},
472477
},
473478
},
474479
}

internal/operator-controller/applier/watchnamespace.go

+21-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package applier
22

33
import (
4+
"encoding/json"
45
"fmt"
56

67
corev1 "k8s.io/api/core/v1"
@@ -19,14 +20,27 @@ const (
1920
// for registry+v1 bundles. This will go away once the ClusterExtension API is updated to include
2021
// (opaque) runtime configuration.
2122
func GetWatchNamespace(ext *ocv1.ClusterExtension) (string, error) {
22-
if features.OperatorControllerFeatureGate.Enabled(features.SingleOwnNamespaceInstallSupport) {
23-
if ext != nil && ext.Annotations[AnnotationClusterExtensionWatchNamespace] != "" {
24-
watchNamespace := ext.Annotations[AnnotationClusterExtensionWatchNamespace]
25-
if errs := validation.IsDNS1123Subdomain(watchNamespace); len(errs) > 0 {
26-
return "", fmt.Errorf("invalid watch namespace '%s': namespace must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character", watchNamespace)
27-
}
28-
return ext.Annotations[AnnotationClusterExtensionWatchNamespace], nil
23+
if !features.OperatorControllerFeatureGate.Enabled(features.SingleOwnNamespaceInstallSupport) ||
24+
ext == nil {
25+
return corev1.NamespaceAll, nil
26+
}
27+
28+
for _, c := range ext.Spec.Config {
29+
maybeConfig := &ocv1.BundleConfig{}
30+
if err := json.Unmarshal(c.Raw, maybeConfig); err != nil {
31+
return "", err
32+
}
33+
34+
if maybeConfig.GroupVersionKind() != ocv1.GroupVersion.WithKind("BundleConfig") {
35+
continue
2936
}
37+
38+
watchNamespace := maybeConfig.Spec.WatchNamespace
39+
if errs := validation.IsDNS1123Subdomain(watchNamespace); len(errs) > 0 {
40+
return "", fmt.Errorf("invalid watch namespace '%s': namespace must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character", watchNamespace)
41+
}
42+
return watchNamespace, nil
3043
}
44+
3145
return corev1.NamespaceAll, nil
3246
}

internal/operator-controller/applier/watchnamespace_test.go

+29-10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/stretchr/testify/require"
77
corev1 "k8s.io/api/core/v1"
88
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
"k8s.io/apimachinery/pkg/runtime"
910
featuregatetesting "k8s.io/component-base/featuregate/testing"
1011

1112
v1 "github.com/operator-framework/operator-controller/api/v1"
@@ -17,11 +18,17 @@ func TestGetWatchNamespacesWhenFeatureGateIsDisabled(t *testing.T) {
1718
watchNamespace, err := applier.GetWatchNamespace(&v1.ClusterExtension{
1819
ObjectMeta: metav1.ObjectMeta{
1920
Name: "extension",
20-
Annotations: map[string]string{
21-
"olm.operatorframework.io/watch-namespace": "watch-namespace",
21+
// Annotations: map[string]string{
22+
// "olm.operatorframework.io/watch-namespace": "watch-namespace",
23+
// },
24+
},
25+
Spec: v1.ClusterExtensionSpec{
26+
Config: []runtime.RawExtension{
27+
{Raw: []byte(
28+
`{"apiVersion":"olm.operatorframework.io/v1","kind":"BundleConfig",` +
29+
`"spec":{"watchNamespace":"watch-namespace"}}`)},
2230
},
2331
},
24-
Spec: v1.ClusterExtensionSpec{},
2532
})
2633
require.NoError(t, err)
2734
t.Log("Check watchNamespace is '' even if the annotation is set")
@@ -54,11 +61,14 @@ func TestGetWatchNamespace(t *testing.T) {
5461
csv: &v1.ClusterExtension{
5562
ObjectMeta: metav1.ObjectMeta{
5663
Name: "extension",
57-
Annotations: map[string]string{
58-
"olm.operatorframework.io/watch-namespace": "watch-namespace",
64+
},
65+
Spec: v1.ClusterExtensionSpec{
66+
Config: []runtime.RawExtension{
67+
{Raw: []byte(
68+
`{"apiVersion":"olm.operatorframework.io/v1","kind":"BundleConfig",` +
69+
`"spec":{"watchNamespace":"watch-namespace"}}`)},
5970
},
6071
},
61-
Spec: v1.ClusterExtensionSpec{},
6272
},
6373
expectError: false,
6474
}, {
@@ -67,11 +77,14 @@ func TestGetWatchNamespace(t *testing.T) {
6777
csv: &v1.ClusterExtension{
6878
ObjectMeta: metav1.ObjectMeta{
6979
Name: "extension",
70-
Annotations: map[string]string{
71-
"olm.operatorframework.io/watch-namespace": "watch-namespace,watch-namespace2,watch-namespace3",
80+
},
81+
Spec: v1.ClusterExtensionSpec{
82+
Config: []runtime.RawExtension{
83+
{Raw: []byte(
84+
`{"apiVersion":"olm.operatorframework.io/v1","kind":"BundleConfig",` +
85+
`"spec":{"watchNamespace":"watch-namespace,watch-namespace2,watch-namespace3"}}`)},
7286
},
7387
},
74-
Spec: v1.ClusterExtensionSpec{},
7588
},
7689
expectError: true,
7790
}, {
@@ -84,7 +97,13 @@ func TestGetWatchNamespace(t *testing.T) {
8497
"olm.operatorframework.io/watch-namespace": "watch-namespace-",
8598
},
8699
},
87-
Spec: v1.ClusterExtensionSpec{},
100+
Spec: v1.ClusterExtensionSpec{
101+
Config: []runtime.RawExtension{
102+
{Raw: []byte(
103+
`{"apiVersion":"olm.operatorframework.io/v1","kind":"BundleConfig",` +
104+
`"spec":{"watchNamespace":"watch-namespace-"}}`)},
105+
},
106+
},
88107
},
89108
expectError: true,
90109
},

0 commit comments

Comments
 (0)