diff --git a/mantle/cmd/ore/gcloud/deprecate-image.go b/mantle/cmd/ore/gcloud/deprecate-image.go new file mode 100644 index 0000000000..762b2d9391 --- /dev/null +++ b/mantle/cmd/ore/gcloud/deprecate-image.go @@ -0,0 +1,74 @@ +// Copyright 2020 Red Hat +// +// 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 gcloud + +import ( + "fmt" + + "github.com/coreos/mantle/platform/api/gcloud" + "github.com/spf13/cobra" +) + +var ( + cmdDeprecateImage = &cobra.Command{ + Use: "deprecate-image --image=ImageName [--state=DeprecationState] [--replacement=Replacement]", + Short: "Deprecate GCP image", + Long: "Change deprecation status of existing GCP image", + Run: runDeprecateImage, + } + + deprecateImageState string + deprecateImageReplacement string +) + +func init() { + // note that --image comes from the toplevel options in gcloud.go + cmdDeprecateImage.Flags().StringVar(&deprecateImageState, "state", + string(gcloud.DeprecationStateDeprecated), + fmt.Sprintf("Deprecation state must be one of: %s,%s,%s,%s", + gcloud.DeprecationStateActive, + gcloud.DeprecationStateDeprecated, + gcloud.DeprecationStateObsolete, + gcloud.DeprecationStateDeleted)) + cmdDeprecateImage.Flags().StringVar(&deprecateImageReplacement, + "replacement", "", "optional: link to replacement for the deprecated image") + GCloud.AddCommand(cmdDeprecateImage) +} + +func runDeprecateImage(cmd *cobra.Command, args []string) { + // Check that the user provided an image + if opts.Image == "" { + plog.Fatal("Must provide an image name via --image") + } + + // Check that the deprecation state is a valid one + switch gcloud.DeprecationState(deprecateImageState) { + case gcloud.DeprecationStateActive, + gcloud.DeprecationStateDeprecated, + gcloud.DeprecationStateObsolete, + gcloud.DeprecationStateDeleted: + // Do nothing, state is valid + default: + plog.Fatalf("Specified deprecation state is invalid: %s\n", deprecateImageState) + } + + plog.Debugf("Attempting to change GCP image deprecation state of %s to %s\n", + opts.Image, deprecateImageState) + _, err := api.DeprecateImage(opts.Image, + gcloud.DeprecationState(deprecateImageState), deprecateImageReplacement) + if err != nil { + plog.Fatalf("Changing deprecation state of image failed: %v\n", err) + } +} diff --git a/mantle/cmd/ore/gcloud/upload.go b/mantle/cmd/ore/gcloud/upload.go index c3fabf250c..64eaee17e3 100644 --- a/mantle/cmd/ore/gcloud/upload.go +++ b/mantle/cmd/ore/gcloud/upload.go @@ -36,12 +36,13 @@ var ( Run: runUpload, } - uploadBucket string - uploadImageName string - uploadFile string - uploadFedora bool - uploadForce bool - uploadWriteUrl string + uploadBucket string + uploadImageName string + uploadFile string + uploadFedora bool + uploadForce bool + uploadWriteUrl string + uploadImageFamily string ) func init() { @@ -54,6 +55,7 @@ func init() { cmdUpload.Flags().BoolVar(&uploadFedora, "fcos", false, "Flag this is Fedora CoreOS (or a derivative); currently enables SECURE_BOOT and UEFI_COMPATIBLE") cmdUpload.Flags().BoolVar(&uploadForce, "force", false, "overwrite existing GS and GCE images without prompt") cmdUpload.Flags().StringVar(&uploadWriteUrl, "write-url", "", "output the uploaded URL to the named file") + cmdUpload.Flags().StringVar(&uploadImageFamily, "family", "", "GCP image family to attach image to") GCloud.AddCommand(cmdUpload) } @@ -128,6 +130,7 @@ func runUpload(cmd *cobra.Command, args []string) { storageSrc := fmt.Sprintf("https://storage.googleapis.com/%v/%v", uploadBucket, imageNameGS) _, pending, err := api.CreateImage(&gcloud.ImageSpec{ Name: imageNameGCE, + Family: uploadImageFamily, SourceImage: storageSrc, }, uploadForce, uploadFedora) if err == nil { diff --git a/mantle/platform/api/gcloud/api.go b/mantle/platform/api/gcloud/api.go index 763ac140b3..e0ea4dd16d 100644 --- a/mantle/platform/api/gcloud/api.go +++ b/mantle/platform/api/gcloud/api.go @@ -57,12 +57,14 @@ func New(opts *Options) (*API, error) { // If the image name isn't a full api endpoint accept a name beginning // with "projects/" to specify a different project from the instance. // Also accept a short name and use instance project. - if strings.HasPrefix(opts.Image, "projects/") { - opts.Image = endpointPrefix + opts.Image - } else if !strings.Contains(opts.Image, "/") { - opts.Image = fmt.Sprintf("%sprojects/%s/global/images/%s", endpointPrefix, opts.Project, opts.Image) - } else if !strings.HasPrefix(opts.Image, endpointPrefix) { - return nil, fmt.Errorf("GCE Image argument must be the full api endpoint, begin with 'projects/', or use the short name") + if opts.Image != "" { + if strings.HasPrefix(opts.Image, "projects/") { + opts.Image = endpointPrefix + opts.Image + } else if !strings.Contains(opts.Image, "/") { + opts.Image = fmt.Sprintf("%sprojects/%s/global/images/%s", endpointPrefix, opts.Project, opts.Image) + } else if !strings.HasPrefix(opts.Image, endpointPrefix) { + return nil, fmt.Errorf("GCE Image argument must be the full api endpoint, begin with 'projects/', or use the short name") + } } var ( diff --git a/src/cosalib/gcp.py b/src/cosalib/gcp.py index d8ed6227d1..db420cc751 100644 --- a/src/cosalib/gcp.py +++ b/src/cosalib/gcp.py @@ -39,6 +39,11 @@ def gcp_run_ore(build, args): if args.project is None: raise Exception(arg_exp_str.format("project", "GCP_PROJECT")) + # compat for RHCOS pipeline - remove after cloud-gcp.groovy is updated + # to pass --bucket without prepending `gs://` + if not args.bucket.startswith('gs://'): + args.bucket = f"gs://{args.bucket}" + ore_args = ['ore'] if args.log_level == "DEBUG": ore_args.extend(['--log-level', "DEBUG"]) @@ -54,13 +59,18 @@ def gcp_run_ore(build, args): '--basename', build.build_name, 'upload', '--force', # We want to support restarting the pipeline - '--bucket', f'gs://{args.bucket}/{build.build_name}', + '--bucket', f'{args.bucket}', '--json-key', args.json_key, '--name', gcp_name, '--file', f"{build.image_path}", '--write-url', urltmp, ]) + if args.fcos: + ore_args.extend(['--fcos']) + if args.family: + ore_args.extend(['--family', args.family]) + run_verbose(ore_args) build.meta['gcp'] = { 'image': gcp_name, @@ -98,4 +108,12 @@ def gcp_cli(parser): parser.add_argument("--project", help="GCP Project name", default=os.environ.get("GCP_PROJECT_NAME")) + parser.add_argument("--fcos", + help="""Flag this is Fedora CoreOS (or a derivative); + Currently enables SECURE_BOOT and UEFI_COMPATIBLE""", + action="store_true", + default=False) + parser.add_argument("--family", + help="GCP image family to attach image to", + default=None) return parser