Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
7 changes: 4 additions & 3 deletions cmd/operator-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -655,9 +655,10 @@ func setupHelm(
ActionClientGetter: acg,
Preflights: preflights,
HelmChartProvider: &applier.RegistryV1HelmChartProvider{
BundleRenderer: registryv1.Renderer,
CertificateProvider: certProvider,
IsWebhookSupportEnabled: certProvider != nil,
BundleRenderer: registryv1.Renderer,
CertificateProvider: certProvider,
IsWebhookSupportEnabled: certProvider != nil,
IsSingleOwnNamespaceEnabled: features.OperatorControllerFeatureGate.Enabled(features.SingleOwnNamespaceInstallSupport),
},
HelmReleaseToObjectsConverter: &applier.HelmReleaseToObjectsConverter{},
PreAuthorizer: preAuth,
Expand Down
2 changes: 1 addition & 1 deletion commitchecker.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
expectedMergeBase: 588ad5dff70f193ceb19820996d347430a631faa
expectedMergeBase: 35da385077935545a4eaadc338015e249a6df211
upstreamBranch: main
upstreamOrg: operator-framework
upstreamRepo: operator-controller
14 changes: 7 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ require (
github.com/klauspost/compress v1.18.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.1
github.com/operator-framework/api v0.34.0
github.com/operator-framework/api v0.35.0
github.com/operator-framework/helm-operator-plugins v0.8.0
github.com/operator-framework/operator-registry v1.59.0
github.com/prometheus/client_golang v1.23.2
Expand All @@ -31,13 +31,13 @@ require (
golang.org/x/sync v0.17.0
golang.org/x/tools v0.37.0
helm.sh/helm/v3 v3.19.0
k8s.io/api v0.34.0
k8s.io/apiextensions-apiserver v0.34.0
k8s.io/apimachinery v0.34.0
k8s.io/apiserver v0.34.0
k8s.io/api v0.34.1
k8s.io/apiextensions-apiserver v0.34.1
k8s.io/apimachinery v0.34.1
k8s.io/apiserver v0.34.1
k8s.io/cli-runtime v0.34.0
k8s.io/client-go v0.34.0
k8s.io/component-base v0.34.0
k8s.io/client-go v0.34.1
k8s.io/component-base v0.34.1
k8s.io/klog/v2 v2.130.1
k8s.io/kubernetes v1.34.0
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -388,8 +388,8 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/operator-framework/api v0.34.0 h1:REiEaYhG1CWmDoajdcAdZqtgoljWG+ixMY59vUX5pFI=
github.com/operator-framework/api v0.34.0/go.mod h1:eGncUNIYvWtfGCCKmLzGXvoi3P0TDf3Yd/Z0Sn9E6SQ=
github.com/operator-framework/api v0.35.0 h1:xKrffuGEagk3CWy6zqdK5YmIErlBtWUblNNK+q7ld7c=
github.com/operator-framework/api v0.35.0/go.mod h1:A9UNu/pdcO1RauMHvV54unp4DNm/Y5fMVbGDpnIIF+M=
github.com/operator-framework/helm-operator-plugins v0.8.0 h1:0f6HOQC5likkf0b/OvGvw7nhDb6h8Cj5twdCNjwNzMc=
github.com/operator-framework/helm-operator-plugins v0.8.0/go.mod h1:Sc+8bE38xTCgCChBUvtq/PxatEg9fAypr7S5iAw8nlA=
github.com/operator-framework/operator-lib v0.17.0 h1:cbz51wZ9+GpWR1ZYP4CSKSSBxDlWxmmnseaHVZZjZt4=
Expand Down
17 changes: 12 additions & 5 deletions internal/operator-controller/applier/boxcutter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"

"github.com/operator-framework/api/pkg/operators/v1alpha1"

ocv1 "github.com/operator-framework/operator-controller/api/v1"
"github.com/operator-framework/operator-controller/internal/operator-controller/applier"
"github.com/operator-framework/operator-controller/internal/operator-controller/controllers"
"github.com/operator-framework/operator-controller/internal/operator-controller/labels"
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle"
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render"
testutils "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util/testing"
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util/testing/bundlefs"
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util/testing/clusterserviceversion"
)

func Test_RegistryV1BundleRenderer_Render_Success(t *testing.T) {
Expand All @@ -52,8 +55,10 @@ func Test_RegistryV1BundleRenderer_Render_Success(t *testing.T) {
},
},
}
bundleFS := testutils.NewBundleFS()

bundleFS := bundlefs.Builder().
WithPackageName("some-package").
WithCSV(clusterserviceversion.Builder().WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces).Build()).
Build()
objs, err := r.Render(bundleFS, &ocv1.ClusterExtension{
Spec: ocv1.ClusterExtensionSpec{
Namespace: "some-namespace",
Expand All @@ -74,8 +79,10 @@ func Test_RegistryV1BundleRenderer_Render_Failure(t *testing.T) {
},
},
}
bundleFS := testutils.NewBundleFS()

bundleFS := bundlefs.Builder().
WithPackageName("some-package").
WithCSV(clusterserviceversion.Builder().WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces).Build()).
Build()
objs, err := r.Render(bundleFS, &ocv1.ClusterExtension{
Spec: ocv1.ClusterExtensionSpec{
Namespace: "some-namespace",
Expand Down
53 changes: 36 additions & 17 deletions internal/operator-controller/applier/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ import (
"fmt"

"helm.sh/helm/v3/pkg/chart"
"k8s.io/apimachinery/pkg/util/sets"

"github.com/operator-framework/api/pkg/operators/v1alpha1"

ocv1 "github.com/operator-framework/operator-controller/api/v1"
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle/source"
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render"
)

type RegistryV1HelmChartProvider struct {
BundleRenderer render.BundleRenderer
CertificateProvider render.CertificateProvider
IsWebhookSupportEnabled bool
BundleRenderer render.BundleRenderer
CertificateProvider render.CertificateProvider
IsWebhookSupportEnabled bool
IsSingleOwnNamespaceEnabled bool
}

func (r *RegistryV1HelmChartProvider) Get(bundle source.BundleSource, ext *ocv1.ClusterExtension) (*chart.Chart, error) {
Expand All @@ -24,18 +28,6 @@ func (r *RegistryV1HelmChartProvider) Get(bundle source.BundleSource, ext *ocv1.
return nil, err
}

watchNamespace, err := GetWatchNamespace(ext)
if err != nil {
return nil, err
}

opts := []render.Option{
render.WithCertificateProvider(r.CertificateProvider),
}
if watchNamespace != "" {
opts = append(opts, render.WithTargetNamespaces(watchNamespace))
}

if len(rv1.CSV.Spec.APIServiceDefinitions.Owned) > 0 {
return nil, fmt.Errorf("unsupported bundle: apiServiceDefintions are not supported")
}
Expand All @@ -48,8 +40,35 @@ func (r *RegistryV1HelmChartProvider) Get(bundle source.BundleSource, ext *ocv1.
}
}

if r.CertificateProvider == nil && len(rv1.CSV.Spec.WebhookDefinitions) > 0 {
return nil, fmt.Errorf("unsupported bundle: webhookDefinitions are not supported")
installModes := sets.New(rv1.CSV.Spec.InstallModes...)
if !r.IsSingleOwnNamespaceEnabled && !installModes.Has(v1alpha1.InstallMode{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true}) {
return nil, fmt.Errorf("unsupported bundle: bundle does not support AllNamespaces install mode")
}

if !installModes.HasAny(
v1alpha1.InstallMode{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true},
v1alpha1.InstallMode{Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: true},
v1alpha1.InstallMode{Type: v1alpha1.InstallModeTypeOwnNamespace, Supported: true},
) {
return nil, fmt.Errorf("unsupported bundle: bundle must support at least one of [AllNamespaces SingleNamespace OwnNamespace] install modes")
}

opts := []render.Option{
render.WithCertificateProvider(r.CertificateProvider),
}

// TODO: in a follow up PR we'll split this into two components:
// 1. takes a bundle + cluster extension => manifests
// 2. takes a bundle + cluster extension => chart (which will use the component in 1. under the hood)
// GetWatchNamespace will move under the component in 1. and also be reused by the component that
// takes bundle + cluster extension => revision
watchNamespace, err := GetWatchNamespace(ext)
if err != nil {
return nil, err
}

if watchNamespace != "" {
opts = append(opts, render.WithTargetNamespaces(watchNamespace))
}

objs, err := r.BundleRenderer.Render(rv1, ext.Spec.Namespace, opts...)
Expand Down
97 changes: 84 additions & 13 deletions internal/operator-controller/applier/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle/source"
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render"
. "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util/testing"
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util/testing/clusterserviceversion"
)

func Test_RegistryV1HelmChartProvider_Get_ReturnsBundleSourceFailures(t *testing.T) {
Expand Down Expand Up @@ -51,7 +52,7 @@ func Test_RegistryV1HelmChartProvider_Get_ReturnsBundleRendererFailures(t *testi

b := source.FromBundle(
bundle.RegistryV1{
CSV: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces)),
CSV: clusterserviceversion.Builder().WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces).Build(),
},
)

Expand All @@ -70,7 +71,7 @@ func Test_RegistryV1HelmChartProvider_Get_NoAPIServiceDefinitions(t *testing.T)

b := source.FromBundle(
bundle.RegistryV1{
CSV: MakeCSV(WithOwnedAPIServiceDescriptions(v1alpha1.APIServiceDescription{})),
CSV: clusterserviceversion.Builder().WithOwnedAPIServiceDescriptions(v1alpha1.APIServiceDescription{}).Build(),
},
)

Expand All @@ -85,14 +86,86 @@ func Test_RegistryV1HelmChartProvider_Get_NoAPIServiceDefinitions(t *testing.T)
require.Contains(t, err.Error(), "unsupported bundle: apiServiceDefintions are not supported")
}

func Test_RegistryV1HelmChartProvider_Get_SingleOwnNamespace(t *testing.T) {
t.Run("rejects bundles without AllNamespaces install mode support if SingleOwnNamespace is not enabled", func(t *testing.T) {
provider := applier.RegistryV1HelmChartProvider{}

b := source.FromBundle(
bundle.RegistryV1{
CSV: clusterserviceversion.Builder().WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace).Build(),
},
)

ext := &ocv1.ClusterExtension{
Spec: ocv1.ClusterExtensionSpec{
Namespace: "install-namespace",
},
}

_, err := provider.Get(b, ext)
require.Error(t, err)
require.Contains(t, err.Error(), "unsupported bundle: bundle does not support AllNamespaces install mode")
})
t.Run("accepts bundles with SingleNamespace install mode support if SingleOwnNamespace is enabled", func(t *testing.T) {
// TODO: this will be removed in a follow-up PR that will refactor GetWatchNamespace's location
featuregatetesting.SetFeatureGateDuringTest(t, features.OperatorControllerFeatureGate, features.SingleOwnNamespaceInstallSupport, true)
provider := applier.RegistryV1HelmChartProvider{
IsSingleOwnNamespaceEnabled: true,
}

b := source.FromBundle(
bundle.RegistryV1{
CSV: clusterserviceversion.Builder().WithInstallModeSupportFor(v1alpha1.InstallModeTypeSingleNamespace).Build(),
},
)

ext := &ocv1.ClusterExtension{
Spec: ocv1.ClusterExtensionSpec{
Namespace: "install-namespace",
Config: &ocv1.ClusterExtensionConfig{
ConfigType: ocv1.ClusterExtensionConfigTypeInline,
Inline: &apiextensionsv1.JSON{
Raw: []byte(`{"watchNamespace": "some-namespace"}`),
},
},
},
}

_, err := provider.Get(b, ext)
require.NoError(t, err)
})
t.Run("accepts bundles with OwnNamespace install mode support if SingleOwnNamespace is enabled", func(t *testing.T) {
// TODO: this will be removed in a follow-up PR that will refactor GetWatchNamespace's location
featuregatetesting.SetFeatureGateDuringTest(t, features.OperatorControllerFeatureGate, features.SingleOwnNamespaceInstallSupport, true)
provider := applier.RegistryV1HelmChartProvider{
IsSingleOwnNamespaceEnabled: true,
}

b := source.FromBundle(
bundle.RegistryV1{
CSV: clusterserviceversion.Builder().WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace).Build(),
},
)

ext := &ocv1.ClusterExtension{
Spec: ocv1.ClusterExtensionSpec{
Namespace: "install-namespace",
},
}

_, err := provider.Get(b, ext)
require.NoError(t, err)
})
}

func Test_RegistryV1HelmChartProvider_Get_NoWebhooksWithoutCertProvider(t *testing.T) {
provider := applier.RegistryV1HelmChartProvider{
IsWebhookSupportEnabled: true,
}

b := source.FromBundle(
bundle.RegistryV1{
CSV: MakeCSV(WithWebhookDefinitions(v1alpha1.WebhookDescription{})),
CSV: clusterserviceversion.Builder().WithWebhookDefinitions(v1alpha1.WebhookDescription{}).Build(),
},
)

Expand All @@ -114,7 +187,7 @@ func Test_RegistryV1HelmChartProvider_Get_WebhooksSupportDisabled(t *testing.T)

b := source.FromBundle(
bundle.RegistryV1{
CSV: MakeCSV(WithWebhookDefinitions(v1alpha1.WebhookDescription{})),
CSV: clusterserviceversion.Builder().WithWebhookDefinitions(v1alpha1.WebhookDescription{}).Build(),
},
)

Expand All @@ -137,10 +210,9 @@ func Test_RegistryV1HelmChartProvider_Get_WebhooksWithCertProvider(t *testing.T)

b := source.FromBundle(
bundle.RegistryV1{
CSV: MakeCSV(
WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces),
WithWebhookDefinitions(v1alpha1.WebhookDescription{}),
),
CSV: clusterserviceversion.Builder().
WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces).
WithWebhookDefinitions(v1alpha1.WebhookDescription{}).Build(),
},
)

Expand Down Expand Up @@ -173,7 +245,7 @@ func Test_RegistryV1HelmChartProvider_Get_BundleRendererIntegration(t *testing.T

b := source.FromBundle(
bundle.RegistryV1{
CSV: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeSingleNamespace)),
CSV: clusterserviceversion.Builder().WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeSingleNamespace).Build(),
},
)

Expand Down Expand Up @@ -243,10 +315,9 @@ func Test_RegistryV1HelmChartProvider_Get_Success(t *testing.T) {

b := source.FromBundle(
bundle.RegistryV1{
CSV: MakeCSV(
WithAnnotations(map[string]string{"foo": "bar"}),
WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces),
),
CSV: clusterserviceversion.Builder().
WithAnnotations(map[string]string{"foo": "bar"}).
WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces).Build(),
Others: []unstructured.Unstructured{
*ToUnstructuredT(t, &corev1.Service{
TypeMeta: metav1.TypeMeta{
Expand Down
10 changes: 5 additions & 5 deletions internal/operator-controller/rukpak/bundle/source/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ type BundleSource interface {
GetBundle() (bundle.RegistryV1, error)
}

type RegistryV1Properties struct {
Properties []property.Property `json:"properties"`
}

// identitySource is a bundle source that returns itself
type identitySource bundle.RegistryV1

Expand Down Expand Up @@ -158,11 +162,7 @@ func copyMetadataPropertiesToCSV(csv *v1alpha1.ClusterServiceVersion, fsys fs.FS

// Otherwise, we need to parse the properties.yaml file and
// append its properties into the CSV annotation.
type registryV1Properties struct {
Properties []property.Property `json:"properties"`
}

var metadataProperties registryV1Properties
var metadataProperties RegistryV1Properties
if err := yaml.Unmarshal(metadataPropertiesJSON, &metadataProperties); err != nil {
return fmt.Errorf("failed to unmarshal metadata/properties.yaml: %w", err)
}
Expand Down
Loading