cli: support for GCP marketplace images (#2792)

* cli: support GCP marketplace images

* ci: support GCP marketplace images

* docs: support GCP marketplace images

* bazel: generate

* ci: allow GCP for mpi e2e test

* Update docs/docs/overview/license.md

Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com>

* terraform-provider: allow GCP MPIs

* terraform-provider: fix error message

---------

Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com>
This commit is contained in:
Moritz Sanft 2024-01-08 15:51:39 +01:00 committed by GitHub
parent d3b951300d
commit e691e26bd3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 77 additions and 21 deletions

View file

@ -113,6 +113,13 @@ runs:
yq eval -i "(.provider.azure.useMarketplaceImage) = true" constellation-conf.yaml yq eval -i "(.provider.azure.useMarketplaceImage) = true" constellation-conf.yaml
yq eval -i "(.image) = \"${{ inputs.marketplaceImageVersion }}\"" constellation-conf.yaml yq eval -i "(.image) = \"${{ inputs.marketplaceImageVersion }}\"" constellation-conf.yaml
- name: Set marketplace image flag (GCP)
if: inputs.marketplaceImageVersion != '' && inputs.cloudProvider == 'gcp'
shell: bash
run: |
yq eval -i "(.provider.gcp.useMarketplaceImage) = true" constellation-conf.yaml
yq eval -i "(.image) = \"${{ inputs.marketplaceImageVersion }}\"" constellation-conf.yaml
- name: Update measurements for non-stable images - name: Update measurements for non-stable images
if: inputs.fetchMeasurements if: inputs.fetchMeasurements
shell: bash shell: bash

View file

@ -12,7 +12,7 @@ on:
type: choice type: choice
options: options:
- "azure" - "azure"
default: "azure" - "gcp"
required: true required: true
runner: runner:
description: "Architecture of the runner that executes the CLI" description: "Architecture of the runner that executes the CLI"

View file

@ -24,4 +24,13 @@ And ensure that the cluster uses a release image (i.e. `.image=vX.Y.Z` in the `c
## GCP ## GCP
Marketplace Images on GCP are not available yet. On GCP, to use a marketplace image, ensure that the account is entitled to use marketplace images by Edgeless Systems
by accepting the terms through the [web portal](https://console.cloud.google.com/marketplace/vm/config/edgeless-systems-public/constellation).
Then, set the VMs to use the marketplace image in the `constellation-conf.yaml` file:
```bash
yq eval -i ".provider.gcp.useMarketplaceImage = true" constellation-conf.yaml
```
And ensure that the cluster uses a release image (i.e. `.image=vX.Y.Z` in the `constellation-conf.yaml` file). Afterwards, proceed with the cluster creation as usual.

View file

@ -28,6 +28,6 @@ Enterprise Licenses don't have the above limitations and come with support and a
Once you have received your Enterprise License file, place it in your [Constellation workspace](../architecture/orchestration.md#workspaces) in a file named `constellation.license`. Once you have received your Enterprise License file, place it in your [Constellation workspace](../architecture/orchestration.md#workspaces) in a file named `constellation.license`.
## Azure Marketplace ## CSP Marketplaces
Constellation is available through the Azure Marketplace. This allows you to create self-managed Constellation clusters that are billed on a pay-per-use basis (hourly, per vCPU) with your Azure account. You can still get direct support by Edgeless Systems. For more information, please [contact us](https://www.edgeless.systems/enterprise-support/). Constellation is available through the Marketplaces of Azure and GCP. This allows you to create self-managed Constellation clusters that are billed on a pay-per-use basis (hourly, per vCPU) with your CSP account. You can still get direct support by Edgeless Systems. For more information, please [contact us](https://www.edgeless.systems/enterprise-support/).

View file

@ -183,6 +183,9 @@ type GCPConfig struct {
// description: | // description: |
// Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage // Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage
DeployCSIDriver *bool `yaml:"deployCSIDriver" validate:"required"` DeployCSIDriver *bool `yaml:"deployCSIDriver" validate:"required"`
// description: |
// Use the specified GCP Marketplace image offering.
UseMarketplaceImage *bool `yaml:"useMarketplaceImage" validate:"omitempty"`
} }
// OpenStackConfig holds config information for OpenStack based Constellation deployments. // OpenStackConfig holds config information for OpenStack based Constellation deployments.
@ -349,6 +352,7 @@ func Default() *Config {
Zone: "", Zone: "",
ServiceAccountKeyPath: "", ServiceAccountKeyPath: "",
DeployCSIDriver: toPtr(true), DeployCSIDriver: toPtr(true),
UseMarketplaceImage: toPtr(false),
}, },
OpenStack: &OpenStackConfig{ OpenStack: &OpenStackConfig{
DirectDownload: toPtr(true), DirectDownload: toPtr(true),
@ -699,7 +703,8 @@ func (c *Config) DeployYawolLoadBalancer() bool {
// UseMarketplaceImage returns whether a marketplace image should be used. // UseMarketplaceImage returns whether a marketplace image should be used.
func (c *Config) UseMarketplaceImage() bool { func (c *Config) UseMarketplaceImage() bool {
return c.Provider.Azure != nil && c.Provider.Azure.UseMarketplaceImage != nil && *c.Provider.Azure.UseMarketplaceImage return (c.Provider.Azure != nil && c.Provider.Azure.UseMarketplaceImage != nil && *c.Provider.Azure.UseMarketplaceImage) ||
(c.Provider.GCP != nil && c.Provider.GCP.UseMarketplaceImage != nil && *c.Provider.GCP.UseMarketplaceImage)
} }
// Validate checks the config values and returns validation errors. // Validate checks the config values and returns validation errors.

View file

@ -229,7 +229,7 @@ func init() {
FieldName: "gcp", FieldName: "gcp",
}, },
} }
GCPConfigDoc.Fields = make([]encoder.Doc, 5) GCPConfigDoc.Fields = make([]encoder.Doc, 6)
GCPConfigDoc.Fields[0].Name = "project" GCPConfigDoc.Fields[0].Name = "project"
GCPConfigDoc.Fields[0].Type = "string" GCPConfigDoc.Fields[0].Type = "string"
GCPConfigDoc.Fields[0].Note = "" GCPConfigDoc.Fields[0].Note = ""
@ -255,6 +255,11 @@ func init() {
GCPConfigDoc.Fields[4].Note = "" GCPConfigDoc.Fields[4].Note = ""
GCPConfigDoc.Fields[4].Description = "Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage" GCPConfigDoc.Fields[4].Description = "Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage"
GCPConfigDoc.Fields[4].Comments[encoder.LineComment] = "Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage" GCPConfigDoc.Fields[4].Comments[encoder.LineComment] = "Deploy Persistent Disk CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage"
GCPConfigDoc.Fields[5].Name = "useMarketplaceImage"
GCPConfigDoc.Fields[5].Type = "bool"
GCPConfigDoc.Fields[5].Note = ""
GCPConfigDoc.Fields[5].Description = "Use the specified GCP Marketplace image offering."
GCPConfigDoc.Fields[5].Comments[encoder.LineComment] = "Use the specified GCP Marketplace image offering."
OpenStackConfigDoc.Type = "OpenStackConfig" OpenStackConfigDoc.Type = "OpenStackConfig"
OpenStackConfigDoc.Comments[encoder.LineComment] = "OpenStackConfig holds config information for OpenStack based Constellation deployments." OpenStackConfigDoc.Comments[encoder.LineComment] = "OpenStackConfig holds config information for OpenStack based Constellation deployments."

View file

@ -18,6 +18,7 @@ import (
"fmt" "fmt"
"io/fs" "io/fs"
"regexp" "regexp"
"strings"
"github.com/edgelesssys/constellation/v2/internal/api/fetcher" "github.com/edgelesssys/constellation/v2/internal/api/fetcher"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi" "github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
@ -52,10 +53,6 @@ func (f *Fetcher) FetchReference(ctx context.Context,
return "", fmt.Errorf("parsing config image short path: %w", err) return "", fmt.Errorf("parsing config image short path: %w", err)
} }
if useMarketplaceImage {
return buildMarketplaceImage(ver, provider)
}
imgInfoReq := versionsapi.ImageInfo{ imgInfoReq := versionsapi.ImageInfo{
Ref: ver.Ref(), Ref: ver.Ref(),
Stream: ver.Stream(), Stream: ver.Stream(),
@ -85,21 +82,54 @@ func (f *Fetcher) FetchReference(ctx context.Context,
return "", fmt.Errorf("validating image info file: %w", err) return "", fmt.Errorf("validating image info file: %w", err)
} }
if useMarketplaceImage {
return buildMarketplaceImage(marketplaceImagePayload{
ver: ver,
provider: provider,
attestationVariant: attestationVariant,
imgInfo: imgInfo,
filters: filters(provider, region),
})
}
return getReferenceFromImageInfo(provider, attestationVariant.String(), imgInfo, filters(provider, region)...) return getReferenceFromImageInfo(provider, attestationVariant.String(), imgInfo, filters(provider, region)...)
} }
// marketplaceImagePayload is a helper struct to pass around the required information to build a marketplace image URI.
type marketplaceImagePayload struct {
ver versionsapi.Version
provider cloudprovider.Provider
attestationVariant variant.Variant
imgInfo versionsapi.ImageInfo
filters []filter
}
// buildMarketplaceImage returns a marketplace image URI for the given CSP and version. // buildMarketplaceImage returns a marketplace image URI for the given CSP and version.
func buildMarketplaceImage(ver versionsapi.Version, provider cloudprovider.Provider) (string, error) { func buildMarketplaceImage(payload marketplaceImagePayload) (string, error) {
sv, err := semver.New(ver.Version()) sv, err := semver.New(payload.ver.Version())
if err != nil { if err != nil {
return "", fmt.Errorf("parsing image version: %w", err) return "", fmt.Errorf("parsing image version: %w", err)
} }
switch provider { if sv.Prerelease() != "" {
return "", fmt.Errorf("marketplace images are not supported for prerelease versions")
}
switch payload.provider {
case cloudprovider.Azure: case cloudprovider.Azure:
// For Azure, multiple fields of information are required to use marketplace images,
// so we pack them in a custom URI.
return mpimage.NewAzureMarketplaceImage(sv).URI(), nil return mpimage.NewAzureMarketplaceImage(sv).URI(), nil
case cloudprovider.GCP:
// For GCP, we just need to replace the GCP project name (constellation-images) to the public project that
// hosts the marketplace images (mpi-edgeless-systems-public).
imageRef, err := getReferenceFromImageInfo(payload.provider, payload.attestationVariant.String(), payload.imgInfo, payload.filters...)
if err != nil {
return "", fmt.Errorf("getting image reference: %w", err)
}
return strings.Replace(imageRef, "constellation-images", "mpi-edgeless-systems-public", 1), nil
default: default:
return "", fmt.Errorf("marketplace images are not supported for csp %s", provider.String()) return "", fmt.Errorf("marketplace images are not supported for csp %s", payload.provider.String())
} }
} }

View file

@ -36,7 +36,7 @@ See the [full list of CSPs](https://docs.edgeless.systems/constellation/overview
### Optional ### Optional
- `marketplace_image` (Boolean) Whether a marketplace image should be used. Currently only supported for Azure. - `marketplace_image` (Boolean) Whether a marketplace image should be used. Currently only supported for Azure and GCP.
- `region` (String) Region to retrieve the image for. Only required for AWS. - `region` (String) Region to retrieve the image for. Only required for AWS.
The Constellation OS image must be [replicated to the region](https://docs.edgeless.systems/constellation/workflows/config),and the region must [support AMD SEV-SNP](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/snp-requirements.html), if it is used for Attestation. The Constellation OS image must be [replicated to the region](https://docs.edgeless.systems/constellation/workflows/config),and the region must [support AMD SEV-SNP](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/snp-requirements.html), if it is used for Attestation.
- `version` (String) Version of the Constellation OS image to use. (e.g. `v2.13.0`). If not set, the provider version value is used. - `version` (String) Version of the Constellation OS image to use. (e.g. `v2.13.0`). If not set, the provider version value is used.

View file

@ -87,8 +87,8 @@ func (d *ImageDataSource) Schema(_ context.Context, _ datasource.SchemaRequest,
}, },
"csp": newCSPAttributeSchema(), "csp": newCSPAttributeSchema(),
"marketplace_image": schema.BoolAttribute{ "marketplace_image": schema.BoolAttribute{
Description: "Whether a marketplace image should be used. Currently only supported for Azure.", Description: "Whether a marketplace image should be used. Currently only supported for Azure and GCP.",
MarkdownDescription: "Whether a marketplace image should be used. Currently only supported for Azure.", MarkdownDescription: "Whether a marketplace image should be used. Currently only supported for Azure and GCP.",
Optional: true, Optional: true,
}, },
"region": schema.StringAttribute{ "region": schema.StringAttribute{
@ -128,11 +128,11 @@ func (d *ImageDataSource) ValidateConfig(ctx context.Context, req datasource.Val
) )
} }
// Marketplace image is only supported for Azure // Marketplace image is only supported for Azure and GCP
if !data.CSP.Equal(types.StringValue("azure")) && !data.MarketplaceImage.IsNull() { if data.CSP.Equal(types.StringValue("aws")) && !data.MarketplaceImage.IsNull() {
resp.Diagnostics.AddAttributeWarning( resp.Diagnostics.AddAttributeError(
path.Root("marketplace_image"), path.Root("marketplace_image"),
"Marketplace images are currently only supported on Azure", "When another CSP than Azure is used, setting 'marketplace_image' has no effect.", "Marketplace images are currently only supported on Azure and GCP", "When another CSP than Azure or GCP is used, marketplace images are unavailable.",
) )
} }