-
Notifications
You must be signed in to change notification settings - Fork 1.8k
internal/operator-sdk/run|cleanup: initial boilerplate #3636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5de69b1
897d43d
6e4dd90
9b5d834
f0542bc
c3b0e37
ae254fd
e251520
b560e68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| // Copyright 2020 The Operator-SDK Authors | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| package bundle | ||
|
|
||
| import ( | ||
| "context" | ||
| "time" | ||
|
|
||
| "github.com/sirupsen/logrus" | ||
| "github.com/spf13/cobra" | ||
|
|
||
| "github.com/operator-framework/operator-sdk/internal/operator" | ||
| "github.com/operator-framework/operator-sdk/internal/operator/bundle" | ||
| ) | ||
|
|
||
| func NewCmd() *cobra.Command { | ||
| var timeout time.Duration | ||
|
|
||
| // TODO(joelanford): move the initialization of cfg up to | ||
| // the "run" subcommand when migrating packagemanifests | ||
| // to this design. | ||
| cfg := &operator.Configuration{} | ||
|
|
||
| i := bundle.NewInstall(cfg) | ||
| cmd := &cobra.Command{ | ||
| Use: "bundle <bundle-image>", | ||
| Short: "Deploy an Operator in the bundle format with OLM", | ||
| Args: cobra.ExactArgs(1), | ||
| PersistentPreRunE: func(_ *cobra.Command, _ []string) error { | ||
| return cfg.Load() | ||
| }, | ||
| Run: func(cmd *cobra.Command, args []string) { | ||
| ctx, cancel := context.WithTimeout(cmd.Context(), timeout) | ||
| defer cancel() | ||
|
|
||
| i.BundleImage = args[0] | ||
|
|
||
| // TODO(joelanford): Add cleanup logic if this fails? | ||
| csv, err := i.Run(ctx) | ||
| if err != nil { | ||
| logrus.Fatalf("Failed to run bundle: %v\n", err) | ||
| } | ||
| logrus.Infof("CSV %q installed\n", csv.Name) | ||
| }, | ||
| } | ||
| cmd.Flags().SortFlags = false | ||
| cfg.BindFlags(cmd.PersistentFlags()) | ||
| i.BindFlags(cmd.Flags()) | ||
|
|
||
| cmd.Flags().DurationVar(&timeout, "timeout", 2*time.Minute, "install timeout") | ||
| return cmd | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| // Copyright 2020 The Operator-SDK Authors | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| package bundle | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "os" | ||
| "path/filepath" | ||
| "strings" | ||
|
|
||
| "github.com/operator-framework/api/pkg/operators/v1alpha1" | ||
| "github.com/operator-framework/operator-registry/pkg/registry" | ||
| "github.com/spf13/pflag" | ||
|
|
||
| "github.com/operator-framework/operator-sdk/internal/operator" | ||
| "github.com/operator-framework/operator-sdk/internal/operator/internal" | ||
| registryutil "github.com/operator-framework/operator-sdk/internal/registry" | ||
| ) | ||
|
|
||
| type Install struct { | ||
| BundleImage string | ||
|
|
||
| *internal.IndexImageCatalogCreator | ||
| *internal.OperatorInstaller | ||
| } | ||
|
|
||
| func NewInstall(cfg *operator.Configuration) Install { | ||
| i := Install{ | ||
| OperatorInstaller: internal.NewOperatorInstaller(cfg), | ||
| } | ||
| i.IndexImageCatalogCreator = internal.NewIndexImageCatalogCreator(cfg) | ||
| i.CatalogCreator = i.IndexImageCatalogCreator | ||
| return i | ||
| } | ||
|
|
||
| const defaultIndexImage = "quay.io/operator-framework/upstream-opm-builder:latest" | ||
|
|
||
| func (i *Install) BindFlags(fs *pflag.FlagSet) { | ||
| fs.StringVar(&i.IndexImage, "index-image", defaultIndexImage, "index image in which to inject bundle") | ||
| fs.Var(&i.InstallMode, "install-mode", "install mode") | ||
| fs.StringVar(&i.InjectBundleMode, "mode", "", "mode to use for adding bundle to index") | ||
| _ = fs.MarkHidden("mode") | ||
| } | ||
|
|
||
| func (i Install) Run(ctx context.Context) (*v1alpha1.ClusterServiceVersion, error) { | ||
| if err := i.setup(ctx); err != nil { | ||
| return nil, err | ||
| } | ||
| return i.InstallOperator(ctx) | ||
| } | ||
|
|
||
| func (i *Install) setup(ctx context.Context) error { | ||
| labels, csv, err := loadBundle(ctx, i.BundleImage) | ||
| if err != nil { | ||
| return fmt.Errorf("load bundle: %v", err) | ||
| } | ||
|
|
||
| i.OperatorInstaller.PackageName = labels["operators.operatorframework.io.bundle.package.v1"] | ||
|
rashmigottipati marked this conversation as resolved.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Related: I have a PR open that makes getting bundle metadata very straightforward, which we should start using once merged (no changes required now)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sweet! Look forward to that. |
||
| i.OperatorInstaller.CatalogSourceName = fmt.Sprintf("%s-catalog", i.OperatorInstaller.PackageName) | ||
| i.OperatorInstaller.StartingCSV = csv.Name | ||
| i.OperatorInstaller.Channel = strings.Split(labels["operators.operatorframework.io.bundle.channels.v1"], ",")[0] | ||
|
|
||
| i.IndexImageCatalogCreator.InjectBundles = []string{i.BundleImage} | ||
| i.IndexImageCatalogCreator.InjectBundleMode = "replaces" | ||
| if i.IndexImageCatalogCreator.IndexImage == defaultIndexImage { | ||
| i.IndexImageCatalogCreator.InjectBundleMode = "semver" | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func loadBundle(ctx context.Context, bundleImage string) (labels registryutil.Labels, csv *registry.ClusterServiceVersion, err error) { | ||
| bundlePath, err := registryutil.ExtractBundleImage(ctx, nil, bundleImage, false) | ||
| if err != nil { | ||
| return nil, nil, fmt.Errorf("pull bundle image: %v", err) | ||
| } | ||
| defer func() { | ||
| _ = os.RemoveAll(bundlePath) | ||
| }() | ||
|
|
||
| labels, _, err = registryutil.FindBundleMetadata(bundlePath) | ||
| if err != nil { | ||
| return nil, nil, fmt.Errorf("load bundle metadata: %v", err) | ||
| } | ||
|
|
||
| relManifestsDir, ok := labels.GetManifestsDir() | ||
| if !ok { | ||
| return nil, nil, fmt.Errorf("manifests directory not defined in bundle metadata") | ||
| } | ||
| manifestsDir := filepath.Join(bundlePath, relManifestsDir) | ||
| csv, err = registry.ReadCSVFromBundleDirectory(manifestsDir) | ||
| if err != nil { | ||
| return nil, nil, fmt.Errorf("read bundle csv: %v", err) | ||
| } | ||
|
|
||
| return labels, csv, nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| // Copyright 2020 The Operator-SDK Authors | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| package internal | ||
|
|
||
| import ( | ||
| "context" | ||
|
|
||
| "github.com/operator-framework/api/pkg/operators/v1alpha1" | ||
| ) | ||
|
|
||
| type CatalogCreator interface { | ||
| CreateCatalog(ctx context.Context, name string) (*v1alpha1.CatalogSource, error) | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,82 @@ | ||||||
| // Copyright 2020 The Operator-SDK Authors | ||||||
| // | ||||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with the License. | ||||||
| // You may obtain a copy of the License at | ||||||
| // | ||||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||||
| // | ||||||
| // Unless required by applicable law or agreed to in writing, software | ||||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
| // See the License for the specific language governing permissions and | ||||||
| // limitations under the License. | ||||||
|
|
||||||
| package internal | ||||||
|
|
||||||
| import ( | ||||||
| "context" | ||||||
| "fmt" | ||||||
| "strings" | ||||||
|
|
||||||
| "github.com/operator-framework/api/pkg/operators/v1alpha1" | ||||||
|
|
||||||
| "github.com/operator-framework/operator-sdk/internal/operator" | ||||||
| registryutil "github.com/operator-framework/operator-sdk/internal/registry" | ||||||
| ) | ||||||
|
|
||||||
| type IndexImageCatalogCreator struct { | ||||||
| IndexImage string | ||||||
| InjectBundles []string | ||||||
| InjectBundleMode string | ||||||
|
|
||||||
| cfg *operator.Configuration | ||||||
|
rashmigottipati marked this conversation as resolved.
|
||||||
| } | ||||||
|
|
||||||
| func NewIndexImageCatalogCreator(cfg *operator.Configuration) *IndexImageCatalogCreator { | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason
Suggested change
?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll take a look at this for a follow-up.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, one reason is that we could make |
||||||
| return &IndexImageCatalogCreator{ | ||||||
| cfg: cfg, | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| func (c IndexImageCatalogCreator) CreateCatalog(ctx context.Context, name string) (*v1alpha1.CatalogSource, error) { | ||||||
| dbPath, err := c.getDBPath(ctx) | ||||||
| if err != nil { | ||||||
| return nil, fmt.Errorf("get database path: %v", err) | ||||||
| } | ||||||
|
|
||||||
| fmt.Printf("IndexImageCatalogCreator.IndexImage: %q\n", c.IndexImage) | ||||||
| fmt.Printf("IndexImageCatalogCreator.IndexImageDBPath: %v\n", dbPath) | ||||||
| fmt.Printf("IndexImageCatalogCreator.InjectBundles: %q\n", strings.Join(c.InjectBundles, ",")) | ||||||
| fmt.Printf("IndexImageCatalogCreator.InjectBundleMode: %q\n", c.InjectBundleMode) | ||||||
|
|
||||||
| // Create barebones catalog source | ||||||
|
|
||||||
| // Create registry pod, assigning its owner as the catalog source | ||||||
|
|
||||||
| // Wait for registry pod to be ready | ||||||
|
|
||||||
| // Update catalog source with `spec.Address = pod.status.podIP` | ||||||
|
|
||||||
| // Update catalog source with annotations for index image, | ||||||
| // injected bundle, and registry add mode | ||||||
|
|
||||||
| // Wait for catalog source status to indicate a successful | ||||||
| // connection with the registry pod | ||||||
|
|
||||||
| // Return the catalog source | ||||||
| return nil, nil | ||||||
| } | ||||||
|
|
||||||
| const defaultDBPath = "/database/index.db" | ||||||
|
|
||||||
| func (c IndexImageCatalogCreator) getDBPath(ctx context.Context) (string, error) { | ||||||
| labels, err := registryutil.GetImageLabels(ctx, nil, c.IndexImage, false) | ||||||
| if err != nil { | ||||||
| return "", fmt.Errorf("get index image labels: %v", err) | ||||||
| } | ||||||
| if dbPath, ok := labels["operators.operatorframework.io.index.database.v1"]; ok { | ||||||
| return dbPath, nil | ||||||
| } | ||||||
| return defaultDBPath, nil | ||||||
| } | ||||||
Uh oh!
There was an error while loading. Please reload this page.