Skip to content

Commit 5263633

Browse files
Address Reviews
Signed-off-by: Varsha Prasad Narsing <[email protected]>
1 parent ff0c3aa commit 5263633

File tree

6 files changed

+89
-37
lines changed

6 files changed

+89
-37
lines changed

api/v1alpha1/clusterextension_types.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,9 @@ const (
101101
ReasonInstallationSucceeded = "InstallationSucceeded"
102102
ReasonResolutionFailed = "ResolutionFailed"
103103

104-
ReasonSuccess = "Success"
105-
ReasonDeprecated = "Deprecated"
106-
ReasonErrorGettingReleaseState = "ErrorGettingReleaseState"
107-
ReasonUpgradeFailed = "UpgradeFailed"
108-
ReasonCreateDynamicWatchFailed = "CreateDynamicWatchFailed"
104+
ReasonSuccess = "Success"
105+
ReasonDeprecated = "Deprecated"
106+
ReasonUpgradeFailed = "UpgradeFailed"
109107
)
110108

111109
func init() {
@@ -124,14 +122,14 @@ func init() {
124122
ReasonInstallationSucceeded,
125123
ReasonResolutionFailed,
126124
ReasonInstallationFailed,
127-
ReasonInstallationStatusUnknown,
128125
ReasonSuccess,
129126
ReasonDeprecated,
130-
ReasonErrorGettingReleaseState,
131127
ReasonUpgradeFailed,
132-
ReasonCreateDynamicWatchFailed,
133128
ReasonBundleLoadFailed,
134129
ReasonErrorGettingClient,
130+
// TODO: this reason is not being used in the reconciler, it will be removed
131+
// when we fix the tests. Avoiding removal here, to reduce diffs.
132+
ReasonInstallationStatusUnknown,
135133
)
136134
}
137135

cmd/manager/main.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,12 @@ func main() {
7777
probeAddr string
7878
cachePath string
7979
operatorControllerVersion bool
80-
httpExternalAddr string
8180
systemNamespace string
8281
unpackImage string
8382
provisionerStorageDirectory string
8483
)
8584
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
8685
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
87-
flag.StringVar(&httpExternalAddr, "http-external-address", "http://localhost:8080", "The external address at which the http server is reachable.")
8886
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
8987
"Enable leader election for controller manager. "+
9088
"Enabling this will ensure there is only one active controller manager.")
@@ -177,15 +175,9 @@ func main() {
177175
os.Exit(1)
178176
}
179177

180-
storageURL, err := url.Parse(fmt.Sprintf("%s/bundles/", httpExternalAddr))
181-
if err != nil {
182-
setupLog.Error(err, "unable to parse bundle content server URL")
183-
os.Exit(1)
184-
}
185-
186178
localStorage := &storage.LocalDirectory{
187179
RootDirectory: provisionerStorageDirectory,
188-
URL: *storageURL,
180+
URL: url.URL{},
189181
}
190182

191183
if err = (&controllers.ClusterExtensionReconciler{

internal/catalogmetadata/types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func (b *Bundle) loadPackage() error {
7373
if b.semVersion == nil {
7474
semVer, err := bsemver.Parse(b.bundlePackage.Version)
7575
if err != nil {
76-
return fmt.Errorf("could not parse semver %q for bundle '%s': %s", b.bundlePackage.Version, b.Bundle.Name, err)
76+
return fmt.Errorf("could not parse semver %q for bundle '%s': %s", b.bundlePackage.Version, b.Name, err)
7777
}
7878
b.semVersion = &semVer
7979
}

internal/controllers/clusterextension_controller.go

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import (
4646
"k8s.io/apimachinery/pkg/runtime/schema"
4747
"k8s.io/apimachinery/pkg/types"
4848
utilerrors "k8s.io/apimachinery/pkg/util/errors"
49+
"k8s.io/apimachinery/pkg/util/sets"
4950
apimachyaml "k8s.io/apimachinery/pkg/util/yaml"
5051
ctrl "sigs.k8s.io/controller-runtime"
5152
"sigs.k8s.io/controller-runtime/pkg/cache"
@@ -63,6 +64,7 @@ import (
6364
catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1"
6465
helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client"
6566
"github.com/operator-framework/operator-registry/alpha/declcfg"
67+
"github.com/operator-framework/operator-registry/alpha/property"
6668
rukpakv1alpha2 "github.com/operator-framework/rukpak/api/v1alpha2"
6769
helmpredicate "github.com/operator-framework/rukpak/pkg/helm-operator-plugins/predicate"
6870
rukpaksource "github.com/operator-framework/rukpak/pkg/source"
@@ -106,6 +108,8 @@ type ClusterExtensionReconciler struct {
106108
//+kubebuilder:rbac:groups=catalogd.operatorframework.io,resources=catalogs,verbs=list;watch
107109
//+kubebuilder:rbac:groups=catalogd.operatorframework.io,resources=catalogmetadata,verbs=list;watch
108110

111+
// The operator controller needs to watch all the bundle objects and reconcile accordingly. Though not ideal, but these permissions are required.
112+
// This has been taken from rukpak, and an issue was created before to discuss it: https://github.com/operator-framework/rukpak/issues/800.
109113
func (r *ClusterExtensionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
110114
l := log.FromContext(ctx).WithName("operator-controller")
111115
l.V(1).Info("starting")
@@ -210,6 +214,15 @@ func (r *ClusterExtensionReconciler) handleResolutionErrors(ext *ocv1alpha1.Clus
210214
// But in the future we might update this function
211215
// to return different results (e.g. requeue).
212216
//
217+
/* The reconcile functions performs the following major tasks:
218+
1. Resolution: Run the resolution to find the bundle from the catalog which needs to be installed.
219+
2. Validate: Ensure that the bundle returned from the resolution for install meets our requirements.
220+
3. Unpack: Unpack the contents from the bundle and store in a localdir in the pod.
221+
4. Install: The process of installing involves:
222+
4.1 Converting the CSV in the bundle into a set of plain k8s objects.
223+
4.2 Generating a chart from k8s objects.
224+
4.3 Apply the release on cluster.
225+
*/
213226
//nolint:unparam
214227
func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.ClusterExtension) (ctrl.Result, error) {
215228
l := log.FromContext(ctx).WithName("operator-controller")
@@ -220,6 +233,12 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
220233
return r.handleResolutionErrors(ext, err)
221234
}
222235

236+
if err := r.validateBundle(bundle); err != nil {
237+
setInstalledStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration())
238+
setDeprecationStatusesUnknown(&ext.Status.Conditions, "deprecation checks have not been attempted as installation has failed", ext.GetGeneration())
239+
return ctrl.Result{}, err
240+
}
241+
223242
bundleVersion, err := bundle.Version()
224243
if err != nil {
225244
ext.Status.ResolvedBundle = nil
@@ -229,14 +248,12 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
229248
return ctrl.Result{}, err
230249
}
231250

232-
// Now we can set the Resolved Condition, and the resolvedBundleSource field to the bundle.Image value.
233251
ext.Status.ResolvedBundle = bundleMetadataFor(bundle)
234252
setResolvedStatusConditionSuccess(&ext.Status.Conditions, fmt.Sprintf("resolved to %q", bundle.Image), ext.GetGeneration())
235253

236-
// Unpack contents into a fs based on the bundle.
237-
// Considering only image source.
238-
239-
// Generate a BundleDeployment from the ClusterExtension to Unpack
254+
// Generate a BundleDeployment from the ClusterExtension to Unpack.
255+
// Note: The BundleDeployment here is not a k8s API, its a simple Go struct which
256+
// necessary embedded values.
240257
bd := r.generateBundleDeploymentForUnpack(bundle.Image, ext)
241258
unpackResult, err := r.Unpacker.Unpack(ctx, bd)
242259
if err != nil {
@@ -253,6 +270,8 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
253270
updateStatusUnpacking(&ext.Status, unpackResult)
254271
return ctrl.Result{}, nil
255272
case rukpaksource.StateUnpacked:
273+
// TODO: Add finalizer to clean the stored bundles, after https://github.com/operator-framework/rukpak/pull/897
274+
// merges.
256275
if err := r.Storage.Store(ctx, ext, unpackResult.Bundle); err != nil {
257276
return ctrl.Result{}, updateStatusUnpackFailing(&ext.Status, err)
258277
}
@@ -302,7 +321,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
302321

303322
rel, state, err := r.getReleaseState(ac, ext, chrt, values, post)
304323
if err != nil {
305-
setInstalledStatusConditionFailed(&ext.Status.Conditions, fmt.Sprintf("%s:%v", ocv1alpha1.ReasonErrorGettingReleaseState, err), ext.Generation)
324+
setInstalledStatusConditionFailed(&ext.Status.Conditions, fmt.Sprintf("%s:%v", rukpakv1alpha2.ReasonErrorGettingReleaseState, err), ext.Generation)
306325
return ctrl.Result{}, err
307326
}
308327

@@ -343,14 +362,14 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
343362

344363
relObjects, err := util.ManifestObjects(strings.NewReader(rel.Manifest), fmt.Sprintf("%s-release-manifest", rel.Name))
345364
if err != nil {
346-
setInstalledStatusConditionFailed(&ext.Status.Conditions, fmt.Sprintf("%s:%v", ocv1alpha1.ReasonCreateDynamicWatchFailed, err), ext.Generation)
365+
setInstalledStatusConditionFailed(&ext.Status.Conditions, fmt.Sprintf("%s:%v", rukpakv1alpha2.ReasonCreateDynamicWatchFailed, err), ext.Generation)
347366
return ctrl.Result{}, err
348367
}
349368

350369
for _, obj := range relObjects {
351370
uMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
352371
if err != nil {
353-
setInstalledStatusConditionFailed(&ext.Status.Conditions, fmt.Sprintf("%s:%v", ocv1alpha1.ReasonCreateDynamicWatchFailed, err), ext.Generation)
372+
setInstalledStatusConditionFailed(&ext.Status.Conditions, fmt.Sprintf("%s:%v", rukpakv1alpha2.ReasonCreateDynamicWatchFailed, err), ext.Generation)
354373
return ctrl.Result{}, err
355374
}
356375

@@ -372,17 +391,17 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
372391
return nil
373392
}(); err != nil {
374393
ext.Status.InstalledBundle = nil
375-
setInstalledStatusConditionFailed(&ext.Status.Conditions, fmt.Sprintf("%s:%v", ocv1alpha1.ReasonCreateDynamicWatchFailed, err), ext.Generation)
394+
setInstalledStatusConditionFailed(&ext.Status.Conditions, fmt.Sprintf("%s:%v", rukpakv1alpha2.ReasonCreateDynamicWatchFailed, err), ext.Generation)
376395
return ctrl.Result{}, err
377396
}
378397
}
379398
ext.Status.InstalledBundle = bundleMetadataFor(bundle)
380399
setInstalledStatusConditionSuccess(&ext.Status.Conditions, fmt.Sprintf("Instantiated bundle %s successfully", ext.GetName()), ext.Generation)
381400

382-
// set the status of the cluster extension based on the respective bundle deployment status conditions.
383401
return ctrl.Result{}, nil
384402
}
385403

404+
// resolve returns a Bundle from the catalog that needs to get installed on the cluster.
386405
func (r *ClusterExtensionReconciler) resolve(ctx context.Context, ext ocv1alpha1.ClusterExtension) (*catalogmetadata.Bundle, error) {
387406
allBundles, err := r.BundleProvider.Bundles(ctx)
388407
if err != nil {
@@ -663,17 +682,14 @@ func clusterExtensionRequestsForCatalog(c client.Reader, logger logr.Logger) crh
663682
}
664683
}
665684

685+
// getInstalledVersion fetches the installed version of a particular bundle from the cluster. To do so, we read the release label
686+
// which are added during install.
666687
func (r *ClusterExtensionReconciler) getInstalledVersion(ctx context.Context, clusterExtension ocv1alpha1.ClusterExtension) (*bsemver.Version, error) {
667688
cl, err := r.ActionClientGetter.ActionClientFor(ctx, &clusterExtension)
668689
if err != nil {
669690
return nil, err
670691
}
671692

672-
// Clarify - Every release will have a unique name as the cluster extension?
673-
// Also filter relases whose owner is the operator controller?
674-
// I think this should work, given we are setting the release Name to the clusterExtension name.
675-
// If not, the other option is to get the Helm secret in the release namespace, list all the releases,
676-
// get the chart annotations.
677693
release, err := cl.Get(clusterExtension.GetName())
678694
if err != nil && !errors.Is(err, driver.ErrReleaseNotFound) {
679695
return nil, err
@@ -682,7 +698,6 @@ func (r *ClusterExtensionReconciler) getInstalledVersion(ctx context.Context, cl
682698
return nil, nil
683699
}
684700

685-
// TODO: when the chart is created these annotations are to be added.
686701
existingVersion, ok := release.Labels[labels.BundleVersionKey]
687702
if !ok {
688703
return nil, fmt.Errorf("release %q: missing bundle version", release.Name)
@@ -804,3 +819,22 @@ func bundleMetadataFor(bundle *catalogmetadata.Bundle) *ocv1alpha1.BundleMetadat
804819
Version: ver.String(),
805820
}
806821
}
822+
823+
func (r *ClusterExtensionReconciler) validateBundle(bundle *catalogmetadata.Bundle) error {
824+
unsupportedProps := sets.New(
825+
property.TypePackageRequired,
826+
property.TypeGVKRequired,
827+
property.TypeConstraint,
828+
)
829+
for i := range bundle.Properties {
830+
if unsupportedProps.Has(bundle.Properties[i].Type) {
831+
return fmt.Errorf(
832+
"bundle %q has a dependency declared via property %q which is currently not supported",
833+
bundle.Name,
834+
bundle.Properties[i].Type,
835+
)
836+
}
837+
}
838+
839+
return nil
840+
}

internal/controllers/clusterextension_registryv1_validation_test.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func TestClusterExtensionRegistryV1DisallowDependencies(t *testing.T) {
3131
name string
3232
bundle *catalogmetadata.Bundle
3333
wantErr string
34+
skip bool
3435
}{
3536
{
3637
name: "package with no dependencies",
@@ -45,6 +46,9 @@ func TestClusterExtensionRegistryV1DisallowDependencies(t *testing.T) {
4546
},
4647
CatalogName: "fake-catalog",
4748
},
49+
// Skipping the happy path, since it requires us to mock unpacker, store and the
50+
// entire installation. This should be handled in an e2e instead.
51+
skip: true,
4852
},
4953
{
5054
name: "package with olm.package.required property",
@@ -96,15 +100,19 @@ func TestClusterExtensionRegistryV1DisallowDependencies(t *testing.T) {
96100
},
97101
} {
98102
t.Run(tt.name, func(t *testing.T) {
99-
t.Skip("Skip and replace with e2e for BundleDeployment")
100103
defer func() {
101104
require.NoError(t, cl.DeleteAllOf(ctx, &ocv1alpha1.ClusterExtension{}))
102105
}()
103106

107+
if tt.skip {
108+
return
109+
}
110+
104111
fakeCatalogClient := testutil.NewFakeCatalogClient([]*catalogmetadata.Bundle{tt.bundle})
105112
reconciler := &controllers.ClusterExtensionReconciler{
106-
Client: cl,
107-
BundleProvider: &fakeCatalogClient,
113+
Client: cl,
114+
BundleProvider: &fakeCatalogClient,
115+
ActionClientGetter: acg,
108116
}
109117

110118
installNamespace := fmt.Sprintf("test-ns-%s", rand.String(8))

internal/controllers/common_controller.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,23 @@ func setInstalledStatusConditionFailed(conditions *[]metav1.Condition, message s
7575
ObservedGeneration: generation,
7676
})
7777
}
78+
79+
// setDeprecationStatusesUnknown sets the deprecation status conditions to unknown.
80+
func setDeprecationStatusesUnknown(conditions *[]metav1.Condition, message string, generation int64) {
81+
conditionTypes := []string{
82+
ocv1alpha1.TypeDeprecated,
83+
ocv1alpha1.TypePackageDeprecated,
84+
ocv1alpha1.TypeChannelDeprecated,
85+
ocv1alpha1.TypeBundleDeprecated,
86+
}
87+
88+
for _, conditionType := range conditionTypes {
89+
apimeta.SetStatusCondition(conditions, metav1.Condition{
90+
Type: conditionType,
91+
Reason: ocv1alpha1.ReasonDeprecated,
92+
Status: metav1.ConditionUnknown,
93+
Message: message,
94+
ObservedGeneration: generation,
95+
})
96+
}
97+
}

0 commit comments

Comments
 (0)