Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add Configuration interface #1847

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ manifests: $(CONTROLLER_GEN) #EXHELP Generate WebhookConfiguration, ClusterRole,
$(CONTROLLER_GEN) crd paths="./api/v1/..." output:crd:artifacts:config=$(CRD_WORKING_DIR)
mv $(CRD_WORKING_DIR)/olm.operatorframework.io_clusterextensions.yaml $(KUSTOMIZE_OPCON_CRDS_DIR)
mv $(CRD_WORKING_DIR)/olm.operatorframework.io_clustercatalogs.yaml $(KUSTOMIZE_CATD_CRDS_DIR)
rmdir $(CRD_WORKING_DIR)
rm -rf $(CRD_WORKING_DIR)
# Generate the remaining operator-controller manifests
$(CONTROLLER_GEN) rbac:roleName=manager-role paths="./internal/operator-controller/..." output:rbac:artifacts:config=$(KUSTOMIZE_OPCON_RBAC_DIR)
# Generate the remaining catalogd manifests
Expand Down
27 changes: 27 additions & 0 deletions api/v1/bundleconfig_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package v1

import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

// BundleConfig ...
// +kubebuilder:object:root=true
type BundleConfig struct {
metav1.TypeMeta `json:",inline"`
// metadata is the standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
metav1.ObjectMeta `json:"metadata"`

// spec is the desired state of the BundleConfig.
// spec is required.
Spec BundleConfigSpec `json:"spec"`
}

type BundleConfigSpec struct {
// watchNamespace configures what Namespace the Operator should watch.
// setting it to the same value as the install namespace corresponds to OwnNamespace mode,
// setting it to a different value corresponds to SingleNamespace mode.
WatchNamespace string `json:"watchNamespace,omitempty"`
}

func init() {
SchemeBuilder.Register(&BundleConfig{})
}
7 changes: 7 additions & 0 deletions api/v1/clusterextension_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)

var ClusterExtensionKind = "ClusterExtension"
Expand Down Expand Up @@ -92,6 +93,12 @@ type ClusterExtensionSpec struct {
//
// +optional
Install *ClusterExtensionInstallConfig `json:"install,omitempty"`

// config can be used to pass parameters to the underlying runtime.
// +optional
// +kubebuilder:validation:items:XEmbeddedResource
// +kubebuilder:validation:items:XPreserveUnknownFields
Config []runtime.RawExtension `json:"config,omitempty"`
}

const SourceTypeCatalog = "Catalog"
Expand Down
50 changes: 49 additions & 1 deletion api/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ spec:
description: spec is an optional field that defines the desired state
of the ClusterExtension.
properties:
config:
description: config can be used to pass parameters to the underlying
runtime.
items:
type: object
x-kubernetes-embedded-resource: true
x-kubernetes-preserve-unknown-fields: true
type: array
install:
description: |-
install is an optional field used to configure the installation options
Expand Down
9 changes: 7 additions & 2 deletions internal/operator-controller/applier/helm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"helm.sh/helm/v3/pkg/storage/driver"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"sigs.k8s.io/controller-runtime/pkg/client"

Expand Down Expand Up @@ -467,8 +468,12 @@ func TestApply_InstallationWithSingleOwnNamespaceInstallSupportEnabled(t *testin
testExt := &ocv1.ClusterExtension{
ObjectMeta: metav1.ObjectMeta{
Name: "testExt",
Annotations: map[string]string{
applier.AnnotationClusterExtensionWatchNamespace: expectedWatchNamespace,
},
Spec: ocv1.ClusterExtensionSpec{
Config: []runtime.RawExtension{
{Raw: []byte(
`{"apiVersion":"olm.operatorframework.io/v1","kind":"BundleConfig",` +
`"spec":{"watchNamespace":"` + expectedWatchNamespace + `"}}`)},
},
},
}
Expand Down
28 changes: 21 additions & 7 deletions internal/operator-controller/applier/watchnamespace.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package applier

import (
"encoding/json"
"fmt"

corev1 "k8s.io/api/core/v1"
Expand All @@ -19,14 +20,27 @@ const (
// for registry+v1 bundles. This will go away once the ClusterExtension API is updated to include
// (opaque) runtime configuration.
func GetWatchNamespace(ext *ocv1.ClusterExtension) (string, error) {
if features.OperatorControllerFeatureGate.Enabled(features.SingleOwnNamespaceInstallSupport) {
if ext != nil && ext.Annotations[AnnotationClusterExtensionWatchNamespace] != "" {
watchNamespace := ext.Annotations[AnnotationClusterExtensionWatchNamespace]
if errs := validation.IsDNS1123Subdomain(watchNamespace); len(errs) > 0 {
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)
}
return ext.Annotations[AnnotationClusterExtensionWatchNamespace], nil
if !features.OperatorControllerFeatureGate.Enabled(features.SingleOwnNamespaceInstallSupport) ||
ext == nil {
return corev1.NamespaceAll, nil
}

for _, c := range ext.Spec.Config {
maybeConfig := &ocv1.BundleConfig{}
if err := json.Unmarshal(c.Raw, maybeConfig); err != nil {
return "", err
}

if maybeConfig.GroupVersionKind() != ocv1.GroupVersion.WithKind("BundleConfig") {
continue
}

watchNamespace := maybeConfig.Spec.WatchNamespace
if errs := validation.IsDNS1123Subdomain(watchNamespace); len(errs) > 0 {
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)
}
return watchNamespace, nil
}

return corev1.NamespaceAll, nil
}
39 changes: 29 additions & 10 deletions internal/operator-controller/applier/watchnamespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
featuregatetesting "k8s.io/component-base/featuregate/testing"

v1 "github.com/operator-framework/operator-controller/api/v1"
Expand All @@ -17,11 +18,17 @@ func TestGetWatchNamespacesWhenFeatureGateIsDisabled(t *testing.T) {
watchNamespace, err := applier.GetWatchNamespace(&v1.ClusterExtension{
ObjectMeta: metav1.ObjectMeta{
Name: "extension",
Annotations: map[string]string{
"olm.operatorframework.io/watch-namespace": "watch-namespace",
// Annotations: map[string]string{
// "olm.operatorframework.io/watch-namespace": "watch-namespace",
// },
},
Spec: v1.ClusterExtensionSpec{
Config: []runtime.RawExtension{
{Raw: []byte(
`{"apiVersion":"olm.operatorframework.io/v1","kind":"BundleConfig",` +
`"spec":{"watchNamespace":"watch-namespace"}}`)},
},
},
Spec: v1.ClusterExtensionSpec{},
})
require.NoError(t, err)
t.Log("Check watchNamespace is '' even if the annotation is set")
Expand Down Expand Up @@ -54,11 +61,14 @@ func TestGetWatchNamespace(t *testing.T) {
csv: &v1.ClusterExtension{
ObjectMeta: metav1.ObjectMeta{
Name: "extension",
Annotations: map[string]string{
"olm.operatorframework.io/watch-namespace": "watch-namespace",
},
Spec: v1.ClusterExtensionSpec{
Config: []runtime.RawExtension{
{Raw: []byte(
`{"apiVersion":"olm.operatorframework.io/v1","kind":"BundleConfig",` +
`"spec":{"watchNamespace":"watch-namespace"}}`)},
},
},
Spec: v1.ClusterExtensionSpec{},
},
expectError: false,
}, {
Expand All @@ -67,11 +77,14 @@ func TestGetWatchNamespace(t *testing.T) {
csv: &v1.ClusterExtension{
ObjectMeta: metav1.ObjectMeta{
Name: "extension",
Annotations: map[string]string{
"olm.operatorframework.io/watch-namespace": "watch-namespace,watch-namespace2,watch-namespace3",
},
Spec: v1.ClusterExtensionSpec{
Config: []runtime.RawExtension{
{Raw: []byte(
`{"apiVersion":"olm.operatorframework.io/v1","kind":"BundleConfig",` +
`"spec":{"watchNamespace":"watch-namespace,watch-namespace2,watch-namespace3"}}`)},
},
},
Spec: v1.ClusterExtensionSpec{},
},
expectError: true,
}, {
Expand All @@ -84,7 +97,13 @@ func TestGetWatchNamespace(t *testing.T) {
"olm.operatorframework.io/watch-namespace": "watch-namespace-",
},
},
Spec: v1.ClusterExtensionSpec{},
Spec: v1.ClusterExtensionSpec{
Config: []runtime.RawExtension{
{Raw: []byte(
`{"apiVersion":"olm.operatorframework.io/v1","kind":"BundleConfig",` +
`"spec":{"watchNamespace":"watch-namespace-"}}`)},
},
},
},
expectError: true,
},
Expand Down
Loading