Skip to content
This repository has been archived by the owner on Aug 12, 2024. It is now read-only.

✨ Support for other Install strategies #802

Merged
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
5 changes: 5 additions & 0 deletions api/v1alpha2/bundledeployment_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ const (
ReasonUpgradeFailed = "UpgradeFailed"
)

// Add limit to the number of watchNamespaces allowed, as the estimated cost of this rule is linear per BD.
//+kubebuilder:validation:XValidation:rule="!has(self.watchNamespaces) || size(self.watchNamespaces) <= 1 || (size(self.watchNamespaces) > 1 && !self.watchNamespaces.exists(e, e == ''))",message="Empty string not accepted if length of watchNamespaces is more than 1."

// BundleDeploymentSpec defines the desired state of BundleDeployment
type BundleDeploymentSpec struct {
//+kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
Expand All @@ -57,6 +60,8 @@ type BundleDeploymentSpec struct {
// Config is provisioner specific configurations
// +kubebuilder:pruning:PreserveUnknownFields
Config runtime.RawExtension `json:"config,omitempty"`
// watchNamespaces indicates which namespaces the operator should watch.
WatchNamespaces []string `json:"watchNamespaces,omitempty"`
}

// BundleDeploymentStatus defines the observed state of BundleDeployment
Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha2/zz_generated.deepcopy.go

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

32 changes: 14 additions & 18 deletions internal/convert/registryv1.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type Plain struct {
Objects []client.Object
}

func RegistryV1ToPlain(rv1 fs.FS) (fs.FS, error) {
func RegistryV1ToPlain(rv1 fs.FS, watchNamespaces []string) (fs.FS, error) {
reg := RegistryV1{}
fileData, err := fs.ReadFile(rv1, filepath.Join("metadata", "annotations.yaml"))
if err != nil {
Expand Down Expand Up @@ -102,7 +102,7 @@ func RegistryV1ToPlain(rv1 fs.FS) (fs.FS, error) {
}
}

plain, err := Simple(reg)
plain, err := Convert(reg, "", watchNamespaces)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -158,17 +158,13 @@ func validateTargetNamespaces(supportedInstallModes sets.Set[string], installNam
return nil
}
default:
if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeMultiNamespace)) {
if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeMultiNamespace)) && !set.Has("") {
varshaprasad96 marked this conversation as resolved.
Show resolved Hide resolved
return nil
}
}
return fmt.Errorf("supported install modes %v do not support target namespaces %v", sets.List[string](supportedInstallModes), targetNamespaces)
}

func Simple(in RegistryV1) (*Plain, error) {
return Convert(in, "", nil)
}

func saNameOrDefault(saName string) string {
if saName == "" {
return "default"
Expand All @@ -189,10 +185,7 @@ func Convert(in RegistryV1, installNamespace string, targetNamespaces []string)
supportedInstallModes.Insert(string(im.Type))
}
}
if !supportedInstallModes.Has(string(v1alpha1.InstallModeTypeAllNamespaces)) {
return nil, fmt.Errorf("AllNamespace install mode must be enabled")
}
if targetNamespaces == nil {
if len(targetNamespaces) == 0 {
if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeAllNamespaces)) {
targetNamespaces = []string{""}
} else if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeOwnNamespace)) {
varshaprasad96 marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -274,15 +267,18 @@ func Convert(in RegistryV1, installNamespace string, targetNamespaces []string)
permissions = nil
}

for _, permission := range permissions {
saName := saNameOrDefault(permission.ServiceAccountName)
name, err := generateName(fmt.Sprintf("%s-%s", in.CSV.Name, saName), permission)
if err != nil {
return nil, err
for _, ns := range targetNamespaces {
for _, permission := range permissions {
saName := saNameOrDefault(permission.ServiceAccountName)
name, err := generateName(fmt.Sprintf("%s-%s", in.CSV.Name, saName), permission)
if err != nil {
return nil, err
}
roles = append(roles, newRole(ns, name, permission.Rules))
roleBindings = append(roleBindings, newRoleBinding(ns, name, name, installNamespace, saName))
everettraven marked this conversation as resolved.
Show resolved Hide resolved
}
roles = append(roles, newRole(installNamespace, name, permission.Rules))
roleBindings = append(roleBindings, newRoleBinding(installNamespace, name, name, installNamespace, saName))
}

for _, permission := range clusterPermissions {
saName := saNameOrDefault(permission.ServiceAccountName)
name, err := generateName(fmt.Sprintf("%s-%s", in.CSV.Name, saName), permission)
Expand Down
128 changes: 128 additions & 0 deletions internal/convert/registryv1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
. "github.com/onsi/gomega"
"github.com/operator-framework/api/pkg/operators/v1alpha1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
schedulingv1 "k8s.io/api/scheduling/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
Expand Down Expand Up @@ -159,6 +160,133 @@ var _ = Describe("RegistryV1 Suite", func() {
})
})

Context("Should generate objects successfully based on target namespaces", func() {
var (
svc corev1.Service
csv v1alpha1.ClusterServiceVersion
watchNamespaces []string
)

BeforeEach(func() {
csv = v1alpha1.ClusterServiceVersion{
ObjectMeta: metav1.ObjectMeta{
Name: "testCSV",
},
Spec: v1alpha1.ClusterServiceVersionSpec{
InstallModes: []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}},
InstallStrategy: v1alpha1.NamedInstallStrategy{
StrategySpec: v1alpha1.StrategyDetailsDeployment{
Permissions: []v1alpha1.StrategyDeploymentPermissions{
{
ServiceAccountName: "testServiceAccount",
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{"test"},
Resources: []string{"pods"},
Verbs: []string{"*"},
},
},
},
},
},
},
},
}
svc = corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "testService",
},
}
svc.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"})
installNamespace = "testInstallNamespace"
})

It("should convert into plain manifests successfully", func() {
By("creating a registry v1 bundle")
watchNamespaces = []string{"testWatchNs1", "testWatchNs2"}
unstructuredSvc := convertToUnstructured(svc)
registryv1Bundle = RegistryV1{
PackageName: "testPkg",
CSV: csv,
Others: []unstructured.Unstructured{unstructuredSvc},
}

By("converting to plain")
plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces)
Expect(err).NotTo(HaveOccurred())

By("verifying if plain bundle has required objects")
Expect(plainBundle).ShouldNot(BeNil())
Expect(len(plainBundle.Objects)).To(BeEquivalentTo(7))
})

It("should error when multinamespace mode is supported with an empty string in target namespaces", func() {
By("creating a registry v1 bundle")
watchNamespaces = []string{"testWatchNs1", ""}
unstructuredSvc := convertToUnstructured(svc)
registryv1Bundle = RegistryV1{
PackageName: "testPkg",
CSV: csv,
Others: []unstructured.Unstructured{unstructuredSvc},
}

By("converting to plain")
plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces)
Expect(err).To(HaveOccurred())
Expect(plainBundle).To(BeNil())
})

It("should error when single namespace mode is disabled with more than one target namespaces", func() {
csv = v1alpha1.ClusterServiceVersion{
ObjectMeta: metav1.ObjectMeta{
Name: "testCSV",
},
Spec: v1alpha1.ClusterServiceVersionSpec{
InstallModes: []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: false}},
},
}

By("creating a registry v1 bundle")
watchNamespaces = []string{"testWatchNs1", "testWatchNs2"}
unstructuredSvc := convertToUnstructured(svc)
registryv1Bundle = RegistryV1{
PackageName: "testPkg",
CSV: csv,
Others: []unstructured.Unstructured{unstructuredSvc},
}

By("converting to plain")
plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces)
Expect(err).To(HaveOccurred())
Expect(plainBundle).To(BeNil())
})

It("should error when all namespace mode is disabled with target namespace containing an empty string", func() {
csv = v1alpha1.ClusterServiceVersion{
ObjectMeta: metav1.ObjectMeta{
Name: "testCSV",
},
Spec: v1alpha1.ClusterServiceVersionSpec{
InstallModes: []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: false}},
},
}

By("creating a registry v1 bundle")
watchNamespaces = []string{""}
unstructuredSvc := convertToUnstructured(svc)
registryv1Bundle = RegistryV1{
PackageName: "testPkg",
CSV: csv,
Others: []unstructured.Unstructured{unstructuredSvc},
}

By("converting to plain")
plainBundle, err := Convert(registryv1Bundle, installNamespace, watchNamespaces)
Expect(err).To(HaveOccurred())
Expect(plainBundle).To(BeNil())
})
})

})
})

Expand Down
2 changes: 1 addition & 1 deletion internal/provisioner/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const (
)

func HandleBundleDeployment(ctx context.Context, fsys fs.FS, bd *rukpakv1alpha2.BundleDeployment) (*chart.Chart, chartutil.Values, error) {
plainFS, err := convert.RegistryV1ToPlain(fsys)
plainFS, err := convert.RegistryV1ToPlain(fsys, bd.Spec.WatchNamespaces)
if err != nil {
return nil, nil, fmt.Errorf("convert registry+v1 bundle to plain+v0 bundle: %v", err)
}
Expand Down
12 changes: 12 additions & 0 deletions manifests/base/apis/crds/core.rukpak.io_bundledeployments.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,22 @@ spec:
required:
- type
type: object
watchNamespaces:
description: watchNamespaces indicates which namespaces the operator
should watch.
items:
type: string
type: array
required:
- provisionerClassName
- source
type: object
x-kubernetes-validations:
- message: Empty string not accepted if length of watchNamespaces is more
than 1.
rule: '!has(self.watchNamespaces) || size(self.watchNamespaces) <= 1
|| (size(self.watchNamespaces) > 1 && !self.watchNamespaces.exists(e,
e == ''''))'
status:
description: BundleDeploymentStatus defines the observed state of BundleDeployment
properties:
Expand Down
3 changes: 2 additions & 1 deletion test/e2e/registry_provisioner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ var _ = Describe("registry provisioner bundle", func() {
Ref: fmt.Sprintf("%v/%v", ImageRepo, "registry:invalid"),
},
},
WatchNamespaces: []string{"test1"},
},
}
err := c.Create(ctx, bd)
Expand All @@ -108,7 +109,7 @@ var _ = Describe("registry provisioner bundle", func() {
WithTransform(func(c *metav1.Condition) string { return c.Type }, Equal(rukpakv1alpha2.TypeInstalled)),
WithTransform(func(c *metav1.Condition) metav1.ConditionStatus { return c.Status }, Equal(metav1.ConditionFalse)),
WithTransform(func(c *metav1.Condition) string { return c.Reason }, Equal(rukpakv1alpha2.ReasonInstallFailed)),
WithTransform(func(c *metav1.Condition) string { return c.Message }, ContainSubstring("convert registry+v1 bundle to plain+v0 bundle: AllNamespace install mode must be enabled")),
WithTransform(func(c *metav1.Condition) string { return c.Message }, ContainSubstring("convert registry+v1 bundle to plain+v0 bundle:")),
))
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ metadata:
namespace: placeholder
spec:
installModes:
- supported: true
- supported: false
type: OwnNamespace
- supported: false
type: SingleNamespace
- supported: false
- supported: true
type: MultiNamespace
- supported: false
type: AllNamespaces