Skip to content

Commit a616db6

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

11 files changed

+198
-19
lines changed

Diff for: api/v1alpha1/clusterextension_types.go

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

76-
const SourceTypeCatalog = "Catalog"
76+
const (
77+
SourceTypeCatalog = "Catalog"
78+
SourceTypeBundle = "Bundle"
79+
)
7780

7881
// SourceConfig is a discriminated union which selects the installation source.
7982
// +union
80-
// +kubebuilder:validation:XValidation:rule="self.sourceType == 'Catalog' && has(self.catalog)",message="sourceType Catalog requires catalog field"
83+
84+
// +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"
85+
// +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"
8186
type SourceConfig struct {
8287
// sourceType is a required reference to the type of install source.
8388
//
84-
// Allowed values are ["Catalog"]
89+
// Allowed values are ["Catalog", "Bundle"]
8590
//
8691
// When this field is set to "Catalog", information for determining the appropriate
8792
// bundle of content to install will be fetched from ClusterCatalog resources existing
8893
// on the cluster. When using the Catalog sourceType, the catalog field must also be set.
8994
//
90-
// +unionDiscriminator
91-
// +kubebuilder:validation:Enum:="Catalog"
95+
// When this field is set to "Bundle", the bundle of content to install is specified
96+
// directly. In this case, no interaction with ClusterCatalog resources is necessary.
97+
// When using the Bundle sourceType, the bundle field must also be set.
98+
//
99+
// +unionDiscriminatorq
100+
// +kubebuilder:validation:Enum:=Bundle;Catalog
92101
SourceType string `json:"sourceType"`
93102

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

101116
// ClusterExtensionInstallConfig is a union which selects the clusterExtension installation config.
@@ -364,6 +379,14 @@ type CatalogSource struct {
364379
UpgradeConstraintPolicy UpgradeConstraintPolicy `json:"upgradeConstraintPolicy,omitempty"`
365380
}
366381

382+
type BundleSource struct {
383+
// ref is an OCI reference to an extension bundle image. Images referenced
384+
// must conform to the registry+v1 bundle format.
385+
386+
//+kubebuilder:validation:Required
387+
Ref string `json:"ref"`
388+
}
389+
367390
// ServiceAccountReference references a serviceAccount.
368391
type ServiceAccountReference struct {
369392
// 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
@@ -216,6 +216,16 @@ spec:
216216
catalog:
217217
packageName: example-package
218218
properties:
219+
bundle:
220+
description: |-
221+
bundle is used to configure how information is sourced from a bundle. This field must be defined when sourceType is set to "Bundle",
222+
and must be the only field defined for this sourceType.
223+
properties:
224+
ref:
225+
type: string
226+
required:
227+
- ref
228+
type: object
219229
catalog:
220230
description: |-
221231
catalog is used to configure how information is sourced from a catalog. This field must be defined when sourceType is set to "Catalog",
@@ -460,20 +470,31 @@ spec:
460470
description: |-
461471
sourceType is a required reference to the type of install source.
462472
463-
Allowed values are ["Catalog"]
473+
Allowed values are ["Catalog", "Bundle"]
464474
465475
When this field is set to "Catalog", information for determining the appropriate
466476
bundle of content to install will be fetched from ClusterCatalog resources existing
467477
on the cluster. When using the Catalog sourceType, the catalog field must also be set.
478+
479+
When this field is set to "Bundle", the bundle of content to install is specified
480+
directly. In this case, no interaction with ClusterCatalog resources is necessary.
481+
When using the Bundle sourceType, the bundle field must also be set.
468482
enum:
483+
- Bundle
469484
- Catalog
470485
type: string
471486
required:
472487
- sourceType
473488
type: object
474489
x-kubernetes-validations:
475-
- message: sourceType Catalog requires catalog field
476-
rule: self.sourceType == 'Catalog' && has(self.catalog)
490+
- message: bundle is required when sourceType is Bundle, and forbidden
491+
otherwise
492+
rule: 'has(self.sourceType) && self.sourceType == ''Bundle'' ?has(self.bundle)
493+
: !has(self.bundle)'
494+
- message: catalog is required when sourceType is Catalog, and forbidden
495+
otherwise
496+
rule: 'has(self.sourceType) && self.sourceType == ''Catalog'' ?has(self.catalog)
497+
: !has(self.catalog)'
477498
required:
478499
- install
479500
- 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_
@@ -232,7 +248,7 @@ _Appears in:_
232248

233249

234250

235-
SourceConfig is a discriminated union which selects the installation source.
251+
236252

237253

238254

@@ -241,8 +257,9 @@ _Appears in:_
241257

242258
| Field | Description | Default | Validation |
243259
| --- | --- | --- | --- |
244-
| `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 /> |
260+
| `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 /> |
245261
| `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. | | |
262+
| `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. | | |
246263

247264

248265
#### 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)