From d7e59e6eecbd41415818b563eab272de6a4469c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 28 Aug 2024 11:25:57 +0200 Subject: [PATCH] Worker: move GCE image guest OS features to upload target options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, the worker was determining the GCE image guest OS Features on its own, based on the OS name. This caused problems, in case the osbuild-composer was of a newer version than the worker. Example: osbuild-composer contained support for c10s GCE image type and its implementation also contained the proper guest OS Features list for it. However, when the worker got the osbuild job, it built it and tried to fetch the guest OS Features for the distro. Since its implementation was too old, it didn't contain the code that added the actual support for c10s GCE images and got no guest OS features list (which is the default for unsupported distros). The image was successfully uploaded and shared, but it does not boot in GCP, because it does not know that it should use UEFI to boot it. This behavior could be considered a bug. The worker should be dumb. It should not be making decisions about the image features, but instead it should take them from the upload target options. And composer should be the authoritative source of truth for this. Because otherwise, we basically have two components that need to be updated in sync to add support for GCE images on a new distro. Move the GCE image guest OS features to the GCP upload target options. The worker will just take what is specified there and use it when importing the image to GCP. As a compatibility layer for the case when the composer would be older than the worker (unlikely, but still), worker will try to determine the image guest OS features in case the list in the upload target options is empty. Extend the GCP functional tests to check that the imported image has at least some guest OS features set. Signed-off-by: Tomáš Hozza --- cmd/osbuild-worker/jobimpl-osbuild.go | 10 +++++++++- internal/cloudapi/v2/imagerequest.go | 5 ++++- internal/target/gcp_target.go | 5 +++++ internal/weldr/upload.go | 14 +++++++++----- test/cases/api/gcp.sh | 7 +++++++ test/cases/gcp.sh | 7 +++++++ 6 files changed, 41 insertions(+), 7 deletions(-) diff --git a/cmd/osbuild-worker/jobimpl-osbuild.go b/cmd/osbuild-worker/jobimpl-osbuild.go index 35da651d47..ce89a18f71 100644 --- a/cmd/osbuild-worker/jobimpl-osbuild.go +++ b/cmd/osbuild-worker/jobimpl-osbuild.go @@ -807,9 +807,17 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error { break } + guestOSFeatures := targetOptions.GuestOsFeatures + // TODO: Remove this after "some time" + // This is just a backward compatibility for the old composer versions, + // which did not set the guest OS features in the target options. + if len(guestOSFeatures) == 0 { + guestOSFeatures = gcp.GuestOsFeaturesByDistro(targetOptions.Os) + } + logWithId.Infof("[GCP] 📥 Importing image into Compute Engine as '%s'", jobTarget.ImageName) - _, importErr := g.ComputeImageInsert(ctx, bucket, targetOptions.Object, jobTarget.ImageName, []string{targetOptions.Region}, gcp.GuestOsFeaturesByDistro(targetOptions.Os)) + _, importErr := g.ComputeImageInsert(ctx, bucket, targetOptions.Object, jobTarget.ImageName, []string{targetOptions.Region}, guestOSFeatures) if importErr == nil { logWithId.Infof("[GCP] 🎉 Image import finished successfully") } diff --git a/internal/cloudapi/v2/imagerequest.go b/internal/cloudapi/v2/imagerequest.go index 664e93cca3..e666ff3ae6 100644 --- a/internal/cloudapi/v2/imagerequest.go +++ b/internal/cloudapi/v2/imagerequest.go @@ -11,6 +11,7 @@ import ( "github.com/osbuild/images/pkg/distro" "github.com/osbuild/images/pkg/ostree" "github.com/osbuild/osbuild-composer/internal/blueprint" + "github.com/osbuild/osbuild-composer/internal/cloud/gcp" "github.com/osbuild/osbuild-composer/internal/common" "github.com/osbuild/osbuild-composer/internal/target" ) @@ -153,13 +154,15 @@ func newGCPTarget(options UploadOptions, imageType distro.ImageType) (*target.Ta if gcpUploadOptions.Bucket != nil { bucket = *gcpUploadOptions.Bucket } + osName := imageType.Arch().Distro().Name() t := target.NewGCPTarget(&target.GCPTargetOptions{ Region: gcpUploadOptions.Region, - Os: imageType.Arch().Distro().Name(), // not exposed in cloudapi + Os: osName, // not exposed in cloudapi Bucket: bucket, // the uploaded object must have a valid extension Object: fmt.Sprintf("%s.tar.gz", imageName), ShareWithAccounts: share, + GuestOsFeatures: gcp.GuestOsFeaturesByDistro(osName), // not exposed in cloudapi }) // Import will fail if an image with this name already exists if gcpUploadOptions.ImageName != nil { diff --git a/internal/target/gcp_target.go b/internal/target/gcp_target.go index 5d43e7030a..9034c6caef 100644 --- a/internal/target/gcp_target.go +++ b/internal/target/gcp_target.go @@ -1,5 +1,7 @@ package target +import "cloud.google.com/go/compute/apiv1/computepb" + const TargetNameGCP TargetName = "org.osbuild.gcp" type GCPTargetOptions struct { @@ -13,6 +15,9 @@ type GCPTargetOptions struct { // to GCP. If not provided, the worker will try to authenticate using the // credentials from worker's configuration. Credentials []byte `json:"credentials,omitempty"` + + // The list of Guest OS Features to specify for the image being imported. + GuestOsFeatures []*computepb.GuestOsFeature `json:"guestOsFeatures,omitempty"` } func (GCPTargetOptions) isTargetOptions() {} diff --git a/internal/weldr/upload.go b/internal/weldr/upload.go index 461f83c4a5..c67116a8d8 100644 --- a/internal/weldr/upload.go +++ b/internal/weldr/upload.go @@ -10,6 +10,7 @@ import ( ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/osbuild/images/pkg/distro" + "github.com/osbuild/osbuild-composer/internal/cloud/gcp" "github.com/osbuild/osbuild-composer/internal/common" "github.com/sirupsen/logrus" @@ -346,12 +347,15 @@ func uploadRequestToTarget(u uploadRequest, imageType distro.ImageType) *target. logrus.Infof("[GCP] object name must end with '.tar.gz', using %q as the object name", objectName) } + osName := imageType.Arch().Distro().Name() + t.Options = &target.GCPTargetOptions{ - Region: options.Region, - Os: imageType.Arch().Distro().Name(), - Bucket: options.Bucket, - Object: objectName, - Credentials: gcpCredentials, + Region: options.Region, + Os: osName, + Bucket: options.Bucket, + Object: objectName, + Credentials: gcpCredentials, + GuestOsFeatures: gcp.GuestOsFeaturesByDistro(osName), } case *vmwareUploadSettings: t.Name = target.TargetNameVMWare diff --git a/test/cases/api/gcp.sh b/test/cases/api/gcp.sh index a57297345d..b928ce1102 100644 --- a/test/cases/api/gcp.sh +++ b/test/cases/api/gcp.sh @@ -131,6 +131,13 @@ function verify() { exit 1 fi + # Verify that the image has guestOsFeatures set + GCP_IMAGE_GUEST_OS_FEATURES_LEN=$($GCP_CMD compute images describe --project="$GCP_PROJECT" --format="json" "$GCP_IMAGE_NAME" | jq -r '.guestOsFeatures | length') + if [ "$GCP_IMAGE_GUEST_OS_FEATURES_LEN" -eq 0 ]; then + echo "❌ Image does not have guestOsFeatures set" + exit 1 + fi + # Verify that the image boots and have customizations applied # Create SSH keys to use GCP_SSH_KEY="$WORKDIR/id_google_compute_engine" diff --git a/test/cases/gcp.sh b/test/cases/gcp.sh index 56bd460fcf..3182848e67 100755 --- a/test/cases/gcp.sh +++ b/test/cases/gcp.sh @@ -107,6 +107,13 @@ function verifyInGCP() { # Add "gitlab-ci-test" label to the image $GCP_CMD compute images add-labels "$GCP_IMAGE_NAME" --labels=gitlab-ci-test=true + # Verify that the image has guestOsFeatures set + GCP_IMAGE_GUEST_OS_FEATURES_LEN=$($GCP_CMD compute images describe --project="$GCP_PROJECT" --format="json" "$GCP_IMAGE_NAME" | jq -r '.guestOsFeatures | length') + if [ "$GCP_IMAGE_GUEST_OS_FEATURES_LEN" -eq 0 ]; then + echo "❌ Image does not have guestOsFeatures set" + exit 1 + fi + # Verify that the image boots and have customizations applied # Create SSH keys to use GCP_SSH_KEY="$TEMPDIR/id_google_compute_engine"