Skip to content

Commit 76b196f

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

File tree

10 files changed

+175
-15
lines changed

10 files changed

+175
-15
lines changed

api/v1alpha1/clusterextension_types.go

Lines changed: 28 additions & 5 deletions
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

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/manager/main.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ import (
4848

4949
catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1"
5050
helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client"
51+
"github.com/operator-framework/operator-registry/pkg/image"
52+
"github.com/operator-framework/operator-registry/pkg/image/containerdregistry"
5153

5254
ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
5355
"github.com/operator-framework/operator-controller/internal/action"
@@ -258,7 +260,15 @@ func main() {
258260
return httputil.BuildHTTPClient(certPoolWatcher)
259261
})
260262

261-
resolver := &resolve.CatalogResolver{
263+
bundleResolverRegistry, err := createRegistry(filepath.Join(cachePath, "registry"))
264+
if err != nil {
265+
setupLog.Error(err, "unable to create registry")
266+
os.Exit(1)
267+
}
268+
bundleResolver := &resolve.BundleResolver{
269+
Registry: bundleResolverRegistry,
270+
}
271+
catalogResolver := &resolve.CatalogResolver{
262272
WalkCatalogsFunc: resolve.CatalogWalker(
263273
func(ctx context.Context, option ...client.ListOption) ([]catalogd.ClusterCatalog, error) {
264274
var catalogs catalogd.ClusterCatalogList
@@ -273,6 +283,9 @@ func main() {
273283
resolve.NoDependencyValidation,
274284
},
275285
}
286+
resolver := resolve.MultiResolver{}
287+
resolver.RegisterType(ocv1alpha1.SourceTypeBundle, bundleResolver)
288+
resolver.RegisterType(ocv1alpha1.SourceTypeCatalog, catalogResolver)
276289

277290
aeClient, err := apiextensionsv1client.NewForConfig(mgr.GetConfig())
278291
if err != nil {
@@ -356,3 +369,10 @@ func main() {
356369
os.Exit(1)
357370
}
358371
}
372+
373+
func createRegistry(cacheDir string) (image.Registry, error) {
374+
if err := os.MkdirAll(cacheDir, 0700); err != nil {
375+
return nil, err
376+
}
377+
return containerdregistry.NewRegistry(containerdregistry.WithCacheDir(cacheDir))
378+
}

config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml

Lines changed: 24 additions & 3 deletions
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

config/samples/olm_v1alpha1_clusterextension.yaml

Lines changed: 5 additions & 4 deletions
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

docs/api-reference/operator-controller-api-reference.md

Lines changed: 19 additions & 2 deletions
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

go.mod

Lines changed: 1 addition & 0 deletions
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

go.sum

Lines changed: 2 additions & 0 deletions
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=

internal/resolve/bundle.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package resolve
2+
3+
import (
4+
"context"
5+
"errors"
6+
7+
bsemver "github.com/blang/semver/v4"
8+
9+
"github.com/operator-framework/operator-registry/alpha/action"
10+
"github.com/operator-framework/operator-registry/alpha/declcfg"
11+
"github.com/operator-framework/operator-registry/pkg/image"
12+
13+
ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
14+
"github.com/operator-framework/operator-controller/internal/bundleutil"
15+
)
16+
17+
type BundleResolver struct {
18+
Registry image.Registry
19+
}
20+
21+
func (r *BundleResolver) Resolve(ctx context.Context, ext *ocv1alpha1.ClusterExtension, _ *ocv1alpha1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) {
22+
render := action.Render{
23+
Refs: []string{ext.Spec.Source.Bundle.Ref},
24+
Registry: r.Registry,
25+
}
26+
fbc, err := render.Run(ctx)
27+
if err != nil {
28+
return nil, nil, nil, err
29+
}
30+
if len(fbc.Bundles) != 1 {
31+
return nil, nil, nil, errors.New("expected exactly one bundle")
32+
}
33+
bundle := fbc.Bundles[0]
34+
v, err := bundleutil.GetVersion(bundle)
35+
if err != nil {
36+
return nil, nil, nil, err
37+
}
38+
return &bundle, v, nil, nil
39+
}

internal/resolve/resolver.go

Lines changed: 16 additions & 0 deletions
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)