Skip to content
Merged
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
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
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
72 changes: 72 additions & 0 deletions internal/operator-controller/applier/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,78 @@ 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: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace)),
},
)

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: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeSingleNamespace)),
},
)

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: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace)),
},
)

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,
Expand Down
Loading