Skip to content

Commit db2b962

Browse files
committed
poc: direct bundle install API + implementation
Signed-off-by: Joe Lanford <[email protected]>
1 parent 26e2534 commit db2b962

11 files changed

+198
-19
lines changed

Diff for: api/v1alpha1/clusterextension_types.go

+28-5
Original file line numberDiff line numberDiff line change
@@ -74,29 +74,44 @@ type ClusterExtensionSpec struct {
7474
Install ClusterExtensionInstallConfig `json:"install"`
7575
}
7676

77-
const SourceTypeCatalog = "Catalog"
77+
const (
78+
SourceTypeCatalog = "Catalog"
79+
SourceTypeBundle = "Bundle"
80+
)
7881

7982
// SourceConfig is a discriminated union which selects the installation source.
8083
// +union
81-
// +kubebuilder:validation:XValidation:rule="self.sourceType == 'Catalog' && has(self.catalog)",message="sourceType Catalog requires catalog field"
84+
85+
// +kubebuilder:validation:XValidation:rule="has(self.sourceType) && self.sourceType == 'Bundle' ?has(self.bundle) : !has(self.bundle)",message="bundle is required when sourceType is Bundle, and forbidden otherwise"
86+
// +kubebuilder:validation:XValidation:rule="has(self.sourceType) && self.sourceType == 'Catalog' ?has(self.catalog) : !has(self.catalog)",message="catalog is required when sourceType is Catalog, and forbidden otherwise"
8287
type SourceConfig struct {
8388
// sourceType is a required reference to the type of install source.
8489
//
85-
// Allowed values are ["Catalog"]
90+
// Allowed values are ["Catalog", "Bundle"]
8691
//
8792
// When this field is set to "Catalog", information for determining the appropriate
8893
// bundle of content to install will be fetched from ClusterCatalog resources existing
8994
// on the cluster. When using the Catalog sourceType, the catalog field must also be set.
9095
//
91-
// +unionDiscriminator
92-
// +kubebuilder:validation:Enum:="Catalog"
96+
// When this field is set to "Bundle", the bundle of content to install is specified
97+
// directly. In this case, no interaction with ClusterCatalog resources is necessary.
98+
// When using the Bundle sourceType, the bundle field must also be set.
99+
//
100+
// +unionDiscriminatorq
101+
// +kubebuilder:validation:Enum:=Bundle;Catalog
93102
SourceType string `json:"sourceType"`
94103

95104
// catalog is used to configure how information is sourced from a catalog. This field must be defined when sourceType is set to "Catalog",
96105
// and must be the only field defined for this sourceType.
97106
//
98107
// +optional.
99108
Catalog *CatalogSource `json:"catalog,omitempty"`
109+
110+
// bundle is used to configure how information is sourced from a bundle. This field must be defined when sourceType is set to "Bundle",
111+
// and must be the only field defined for this sourceType.
112+
//
113+
// +optional.
114+
Bundle *BundleSource `json:"bundle,omitempty"`
100115
}
101116

102117
// ClusterExtensionInstallConfig is a union which selects the clusterExtension installation config.
@@ -443,6 +458,14 @@ type CatalogSource struct {
443458
UpgradeConstraintPolicy UpgradeConstraintPolicy `json:"upgradeConstraintPolicy,omitempty"`
444459
}
445460

461+
type BundleSource struct {
462+
// ref is an OCI reference to an extension bundle image. Images referenced
463+
// must conform to the registry+v1 bundle format.
464+
465+
//+kubebuilder:validation:Required
466+
Ref string `json:"ref"`
467+
}
468+
446469
// ServiceAccountReference references a serviceAccount.
447470
type ServiceAccountReference struct {
448471
// name is a required, immutable reference to the name of the ServiceAccount

Diff for: api/v1alpha1/zz_generated.deepcopy.go

+20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: cmd/manager/main.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,11 @@ func main() {
258258
return httputil.BuildHTTPClient(certPoolWatcher)
259259
})
260260

261-
resolver := &resolve.CatalogResolver{
261+
bundleResolver := &resolve.BundleResolver{
262+
Unpacker: unpacker,
263+
BrittleUnpackerCacheDir: unpacker.BaseCachePath,
264+
}
265+
catalogResolver := &resolve.CatalogResolver{
262266
WalkCatalogsFunc: resolve.CatalogWalker(
263267
func(ctx context.Context, option ...client.ListOption) ([]catalogd.ClusterCatalog, error) {
264268
var catalogs catalogd.ClusterCatalogList
@@ -273,6 +277,9 @@ func main() {
273277
resolve.NoDependencyValidation,
274278
},
275279
}
280+
resolver := resolve.MultiResolver{}
281+
resolver.RegisterType(ocv1alpha1.SourceTypeBundle, bundleResolver)
282+
resolver.RegisterType(ocv1alpha1.SourceTypeCatalog, catalogResolver)
276283

277284
aeClient, err := apiextensionsv1client.NewForConfig(mgr.GetConfig())
278285
if err != nil {

Diff for: config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml

+24-3
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,16 @@ spec:
324324
catalog:
325325
packageName: example-package
326326
properties:
327+
bundle:
328+
description: |-
329+
bundle is used to configure how information is sourced from a bundle. This field must be defined when sourceType is set to "Bundle",
330+
and must be the only field defined for this sourceType.
331+
properties:
332+
ref:
333+
type: string
334+
required:
335+
- ref
336+
type: object
327337
catalog:
328338
description: |-
329339
catalog is used to configure how information is sourced from a catalog. This field must be defined when sourceType is set to "Catalog",
@@ -568,20 +578,31 @@ spec:
568578
description: |-
569579
sourceType is a required reference to the type of install source.
570580
571-
Allowed values are ["Catalog"]
581+
Allowed values are ["Catalog", "Bundle"]
572582
573583
When this field is set to "Catalog", information for determining the appropriate
574584
bundle of content to install will be fetched from ClusterCatalog resources existing
575585
on the cluster. When using the Catalog sourceType, the catalog field must also be set.
586+
587+
When this field is set to "Bundle", the bundle of content to install is specified
588+
directly. In this case, no interaction with ClusterCatalog resources is necessary.
589+
When using the Bundle sourceType, the bundle field must also be set.
576590
enum:
591+
- Bundle
577592
- Catalog
578593
type: string
579594
required:
580595
- sourceType
581596
type: object
582597
x-kubernetes-validations:
583-
- message: sourceType Catalog requires catalog field
584-
rule: self.sourceType == 'Catalog' && has(self.catalog)
598+
- message: bundle is required when sourceType is Bundle, and forbidden
599+
otherwise
600+
rule: 'has(self.sourceType) && self.sourceType == ''Bundle'' ?has(self.bundle)
601+
: !has(self.bundle)'
602+
- message: catalog is required when sourceType is Catalog, and forbidden
603+
otherwise
604+
rule: 'has(self.sourceType) && self.sourceType == ''Catalog'' ?has(self.catalog)
605+
: !has(self.catalog)'
585606
required:
586607
- install
587608
- source

Diff for: config/samples/cloudnative-pg-clusterextension.yaml

+3-4
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,9 @@ metadata:
2929
name: cloudnative-pg
3030
spec:
3131
source:
32-
sourceType: Catalog
33-
catalog:
34-
packageName: cloudnative-pg
35-
version: "1.24.1"
32+
sourceType: Bundle
33+
bundle:
34+
ref: quay.io/operatorhubio/cloudnative-pg@sha256:e960f799f3d2b2dd5ecc74bc576476fe9c70de6486ba5ffc7d6ef333bba186bc
3635
install:
3736
namespace: cloudnative-pg
3837
serviceAccount:

Diff for: config/samples/olm_v1alpha1_clusterextension.yaml

+5-4
Original file line numberDiff line numberDiff line change
@@ -273,11 +273,12 @@ metadata:
273273
name: argocd
274274
spec:
275275
source:
276-
sourceType: Catalog
277-
catalog:
278-
packageName: argocd-operator
279-
version: 0.6.0
276+
sourceType: Bundle
277+
bundle:
278+
ref: quay.io/operatorhubio/argocd-operator@sha256:d538c45a813b38ef0e44f40d279dc2653f97ca901fb660da5d7fe499d51ad3b3
280279
install:
281280
namespace: argocd
282281
serviceAccount:
283282
name: argocd-installer
283+
values:
284+
watchNamespace: argocd

Diff for: docs/api-reference/operator-controller-api-reference.md

+19-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,22 @@ _Appears in:_
3131
| `version` _string_ | version is a required field and is a reference<br />to the version that this bundle represents | | |
3232

3333

34+
#### BundleSource
35+
36+
37+
38+
39+
40+
41+
42+
_Appears in:_
43+
- [SourceConfig](#sourceconfig)
44+
45+
| Field | Description | Default | Validation |
46+
| --- | --- | --- | --- |
47+
| `ref` _string_ | | | Required: \{\} <br /> |
48+
49+
3450
#### CRDUpgradeSafetyPolicy
3551

3652
_Underlying type:_ _string_
@@ -248,7 +264,7 @@ _Appears in:_
248264

249265

250266

251-
SourceConfig is a discriminated union which selects the installation source.
267+
252268

253269

254270

@@ -257,8 +273,9 @@ _Appears in:_
257273

258274
| Field | Description | Default | Validation |
259275
| --- | --- | --- | --- |
260-
| `sourceType` _string_ | sourceType is a required reference to the type of install source.<br /><br />Allowed values are ["Catalog"]<br /><br />When this field is set to "Catalog", information for determining the appropriate<br />bundle of content to install will be fetched from ClusterCatalog resources existing<br />on the cluster. When using the Catalog sourceType, the catalog field must also be set. | | Enum: [Catalog] <br /> |
276+
| `sourceType` _string_ | sourceType is a required reference to the type of install source.<br /><br />Allowed values are ["Catalog", "Bundle"]<br /><br />When this field is set to "Catalog", information for determining the appropriate<br />bundle of content to install will be fetched from ClusterCatalog resources existing<br />on the cluster. When using the Catalog sourceType, the catalog field must also be set.<br /><br />When this field is set to "Bundle", the bundle of content to install is specified<br />directly. In this case, no interaction with ClusterCatalog resources is necessary.<br />When using the Bundle sourceType, the bundle field must also be set. | | Enum: [Bundle Catalog] <br /> |
261277
| `catalog` _[CatalogSource](#catalogsource)_ | catalog is used to configure how information is sourced from a catalog. This field must be defined when sourceType is set to "Catalog",<br />and must be the only field defined for this sourceType. | | |
278+
| `bundle` _[BundleSource](#bundlesource)_ | bundle is used to configure how information is sourced from a bundle. This field must be defined when sourceType is set to "Bundle",<br />and must be the only field defined for this sourceType. | | |
262279

263280

264281
#### UpgradeConstraintPolicy

Diff for: go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ require (
113113
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
114114
github.com/gobwas/glob v0.2.3 // indirect
115115
github.com/gogo/protobuf v1.3.2 // indirect
116+
github.com/golang-migrate/migrate/v4 v4.18.1 // indirect
116117
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
117118
github.com/golang/protobuf v1.5.4 // indirect
118119
github.com/google/btree v1.1.2 // indirect

Diff for: go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,8 @@ go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR
751751
go.starlark.net v0.0.0-20230612165344-9532f5667272 h1:2/wtqS591wZyD2OsClsVBKRPEvBsQt/Js+fsCiYhwu8=
752752
go.starlark.net v0.0.0-20230612165344-9532f5667272/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
753753
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
754+
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
755+
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
754756
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
755757
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
756758
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=

Diff for: internal/resolve/bundle.go

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package resolve
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"path/filepath"
8+
9+
bsemver "github.com/blang/semver/v4"
10+
"github.com/containers/image/v5/docker/reference"
11+
12+
"github.com/operator-framework/operator-registry/alpha/action"
13+
"github.com/operator-framework/operator-registry/alpha/declcfg"
14+
15+
ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
16+
"github.com/operator-framework/operator-controller/internal/bundleutil"
17+
"github.com/operator-framework/operator-controller/internal/rukpak/source"
18+
)
19+
20+
type BundleResolver struct {
21+
Unpacker source.Unpacker
22+
BrittleUnpackerCacheDir string
23+
}
24+
25+
func (r *BundleResolver) Resolve(ctx context.Context, ext *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) {
26+
res, err := r.Unpacker.Unpack(ctx, &source.BundleSource{
27+
Name: ext.Name,
28+
Type: source.SourceTypeImage,
29+
Image: &source.ImageSource{
30+
Ref: ext.Spec.Source.Bundle.Ref,
31+
},
32+
})
33+
if err != nil {
34+
return nil, nil, nil, err
35+
}
36+
if res.State != source.StateUnpacked {
37+
return nil, nil, nil, fmt.Errorf("bundle not unpacked: %v", res.Message)
38+
}
39+
40+
ref, err := reference.ParseNamed(res.ResolvedSource.Image.Ref)
41+
if err != nil {
42+
return nil, nil, nil, err
43+
}
44+
canonicalRef, ok := ref.(reference.Canonical)
45+
if !ok {
46+
return nil, nil, nil, errors.New("expected canonical reference")
47+
}
48+
bundlePath := filepath.Join(r.BrittleUnpackerCacheDir, ext.Name, canonicalRef.Digest().String())
49+
50+
// TODO: This is a temporary workaround to get the bundle from the filesystem
51+
// until the operator-registry library is updated to support reading from a
52+
// filesystem. This will be removed once the library is updated.
53+
54+
render := action.Render{
55+
Refs: []string{bundlePath},
56+
AllowedRefMask: action.RefBundleDir,
57+
}
58+
fbc, err := render.Run(ctx)
59+
if err != nil {
60+
return nil, nil, nil, err
61+
}
62+
if len(fbc.Bundles) != 1 {
63+
return nil, nil, nil, errors.New("expected exactly one bundle")
64+
}
65+
bundle := fbc.Bundles[0]
66+
bundle.Image = canonicalRef.String()
67+
v, err := bundleutil.GetVersion(bundle)
68+
if err != nil {
69+
return nil, nil, nil, err
70+
}
71+
return &bundle, v, nil, nil
72+
}

Diff for: internal/resolve/resolver.go

+16
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package resolve
22

33
import (
44
"context"
5+
"fmt"
56

67
bsemver "github.com/blang/semver/v4"
78

@@ -19,3 +20,18 @@ type Func func(ctx context.Context, ext *ocv1alpha1.ClusterExtension, installedB
1920
func (f Func) Resolve(ctx context.Context, ext *ocv1alpha1.ClusterExtension, installedBundle *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) {
2021
return f(ctx, ext, installedBundle)
2122
}
23+
24+
type MultiResolver map[string]Resolver
25+
26+
func (m MultiResolver) RegisterType(sourceType string, r Resolver) {
27+
m[sourceType] = r
28+
}
29+
30+
func (m MultiResolver) Resolve(ctx context.Context, ext *ocv1alpha1.ClusterExtension, installedBundle *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) {
31+
t := ext.Spec.Source.SourceType
32+
r, ok := m[t]
33+
if !ok {
34+
return nil, nil, nil, fmt.Errorf("no resolver for source type %q", t)
35+
}
36+
return r.Resolve(ctx, ext, installedBundle)
37+
}

0 commit comments

Comments
 (0)