10000 terraform: Azure Marketplace image support by msanft · Pull Request #2651 · edgelesssys/constellation · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

terraform: Azure Marketplace image support #2651

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

Merged
merged 34 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
07e3d7e
terraform: add Azure marketplace variable
msanft Nov 27, 2023
96611e4
config: add Azure marketplace variable
msanft Nov 27, 2023
339aacb
cli: use Terraform variables from config
msanft Nov 27, 2023
20bb587
terraform: pass down marketplace variable
msanft Nov 27, 2023
8eb400e
image: pad Azure images to 1GiB
msanft Nov 27, 2023
4244fec
terraform: add version attribute to marketplace image
msanft Nov 27, 2023
3206425
semver: allow versions to be exported without prefix
msanft Nov 27, 2023
4fdb853
cli: boolean var to use marketplace images
msanft Nov 27, 2023
a2fa034
config: remove dive key
msanft Nov 27, 2023
dca944c
dev-docs: add instructions on how to use marketplace images
msanft Nov 27, 2023
572d1dd
terraform: fix unit test
msanft Nov 28, 2023
3c48fa9
terraform: only fetch image for non-marketplace images
msanft Nov 28, 2023
54b17ea
mpimage: refactor image selection
msanft Nov 28, 2023
0e7ac90
[remove] increase minor version for image build
msanft Nov 28, 2023
39e90eb
terraform: ignore changes to source_image_reference on upgrade
msanft Nov 28, 2023
2881aa2
operator: add support for parsing Azure marketplace images
msanft Nov 28, 2023
8ff7c3f
upgrade: fix imagefetcher call
msanft Nov 28, 2023
564e7a1
docs: add info about azure marketplace
m1ghtym0 Nov 29, 2023
e1f23fb
image: ensure more than 1GiB in size
msanft Nov 29, 2023
ce72d25
image: test to pad to 2GiB
msanft Nov 30, 2023
748db29
version: change back to v2.14.0-pre
msanft Nov 30, 2023
3a9d2e9
image: GPT-conformant image size padding
msanft Nov 30, 2023
1edf16b
[remove] increase version
msanft Nov 30, 2023
fa4d57e
mpimage: inline prefix func
msanft Nov 30, 2023
3ba5396
ci: add marketplace image e2e test
msanft Dec 5, 2023
f9cb9ae
[remove] register workflow
msanft Dec 6, 2023
42d5b1e
ci: fix workflow name
msanft Dec 6, 2023
98b1f3d
ci: only allow azure test
msanft Dec 6, 2023
e48dab5
cli: add marketplace image input to interface
msanft Dec 6, 2023
d18c459
cli: fix argument passing
msanft Dec 6, 2023
e91b67b
version: roll back to v2.14.0
msanft Dec 6, 2023
b9fe369
ci: add force-flag support
msanft Dec 7, 2023
5d2ad8c
Update docs/docs/overview/license.md
thomasten Dec 7, 2023
f0ce0a8
Update dev-docs/workflows/marketplace-images.md
msanft Dec 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .github/actions/constellation_create/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ inputs:
selfManagedInfra:
description: "Use self-managed infrastructure instead of infrastructure created by the Constellation CLI."
required: true
marketplaceImageVersion:
description: "Marketplace OS image version. Used instead of osImage."
required: false
force:
description: "Set the force-flag on apply to ignore version mismatches."
required: false

outputs:
kubeconfig:
Expand Down Expand Up @@ -97,6 +103,13 @@ runs:
yq eval -i "(.image) = \"${imageInput}\"" constellation-conf.yaml
echo "image=${imageInput}" | tee -a "$GITHUB_OUTPUT"

- name: Set marketplace image flag (Azure)
if: inputs.marketplaceImageVersion != '' && inputs.cloudProvider == 'azure'
shell: bash
run: |
yq eval -i "(.provider.azure.useMarketplaceImage) = true" constellation-conf.yaml
yq eval -i "(.image) = \"${{ inputs.marketplaceImageVersion }}\"" constellation-conf.yaml

- name: Update measurements for non-stable images
if: inputs.fetchMeasurements
shell: bash
Expand Down Expand Up @@ -163,11 +176,18 @@ runs:
kubernetesVersion: ${{ inputs.kubernetesVersion }}
selfManagedInfra: ${{ inputs.selfManagedInfra }}

- name: Set force flag
id: set-force-flag
if: inputs.force == 'true'
shell: bash
run: |
echo "flag=--force" | tee -a $GITHUB_OUTPUT

- name: Constellation init
id: constellation-init
shell: bash
run: |
constellation apply --skip-phases=infrastructure --debug
constellation apply --skip-phases=infrastructure --debug ${{ steps.set-force-flag.outputs.flag }}
echo "KUBECONFIG=$(pwd)/constellation-admin.conf" | tee -a $GITHUB_OUTPUT

- name: Wait for nodes to join and become ready
Expand Down
8 changes: 8 additions & 0 deletions .github/actions/e2e_test/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ inputs:
description: "Access key for s3proxy"
s3SecretKey:
description: "Secret key for s3proxy"
marketplaceImageVersion:
description: "Marketplace OS image version. Used instead of osImage."
required: false
force:
description: "Set the force-flag on apply to ignore version mismatches."
required: false

outputs:
kubeconfig:
Expand Down Expand Up @@ -266,6 +272,8 @@ runs:
internalLoadBalancer: ${{ inputs.internalLoadBalancer }}
test: ${{ inputs.test }}
selfManagedInfra: ${{ inputs.selfManagedInfra }}
marketplaceImageVersion: ${{ inputs.marketplaceImageVersion }}
force: ${{ inputs.force }}

- name: Deploy log- and metrics-collection (Kubernetes)
id: deploy-logcollection
Expand Down
87 changes: 87 additions & 0 deletions .github/workflows/e2e-test-marketplace-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: e2e test marketplace image

on:
workflow_dispatch:
inputs:
nodeCount:
description: "Number of nodes to use in the cluster. Given in format `<control-plane nodes>:<worker nodes>`."
default: "3:2"
type: string
cloudProvider:
description: "Which cloud provider to use."
type: choice
options:
- "azure"
default: "azure"
required: true
runner:
description: "Architecture of the runner that executes the CLI"
type: choice
options:
- "ubuntu-22.04"
- "macos-12"
default: "ubuntu-22.04"
test:
description: "The test to run."
type: choice
options:
- "sonobuoy quick"
- "sonobuoy full"
- "autoscaling"
- "lb"
- "perf-bench"
- "verify"
- "recover"
- "malicious join"
- "nop"
required: true
kubernetesVersion:
description: "Kubernetes version to create the cluster from."
default: "1.27"
required: true
cliVersion:
description: "Version of a released CLI to download. Leave empty to build the CLI from the checked out ref."
type: string
default: ""
required: false
marketplaceImageVersion:
description: "Marketplace image version to use in the cluster's nodes. Needs to be a release semver."
type: string
default: ""
required: true
machineType:
description: "Override VM machine type. Leave as 'default' or empty to use the default VM type for the selected cloud provider."
type: string
default: "default"
required: false
regionZone:
description: "Region or zone to create the cluster in. Leave empty for default region/zone."
type: string
git-ref:
description: "Git ref to checkout."
type: string
default: "head"
required: false

jobs:
e2e-test:
permissions:
id-token: write
checks: write
contents: read
packages: write
secrets: inherit
uses: ./.github/workflows/e2e-test.yml
with:
nodeCount: ${{ inputs.nodeCount }}
cloudProvider: ${{ inputs.cloudProvider }}
runner: ${{ inputs.runner }}
test: ${{ inputs.test }}
kubernetesVersion: ${{ inputs.kubernetesVersion }}
cliVersion: ${{ inputs.cliVersion }}
imageVersion: ${{ inputs.marketplaceImageVersion }}
machineType: ${{ inputs.machineType }}
regionZone: ${{ inputs.regionZone }}
git-ref: ${{ inputs.git-ref }}
marketplaceImageVersion: ${{ inputs.marketplaceImageVersion }}
force: true
8 changes: 8 additions & 0 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ on:
description: "Use self-managed infrastructure."
type: boolean
default: false
marketplaceImageVersion:
description: "Marketplace image version to use."
type: string
force:
description: "Use the force-flag when applying to ignore version mismatches."
type: boolean

jobs:
split-nodeCount:
Expand Down Expand Up @@ -238,6 +244,8 @@ jobs:
selfManagedInfra: ${{ inputs.selfManagedInfra }}
s3AccessKey: ${{ secrets.AWS_ACCESS_KEY_ID_S3PROXY }}
s3SecretKey: ${{ secrets.AWS_SECRET_ACCESS_KEY_S3PROXY }}
marketplaceImageVersion: ${{ inputs.marketplaceImageVersion }}
force: ${{ inputs.force }}

- name: Always terminate cluster
if: always()
Expand Down
1 change: 1 addition & 0 deletions cli/internal/cloudcmd/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ go_library(
"//internal/file",
"//internal/imagefetcher",
"//internal/maa",
"//internal/mpimage",
"//internal/role",
"//internal/state",
],
Expand Down
4 changes: 2 additions & 2 deletions cli/internal/cloudcmd/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func (a *Applier) terraformApplyVars(ctx context.Context, conf *config.Config) (
ctx,
conf.GetProvider(),
conf.GetAttestationConfig().GetVariant(),
conf.Image, conf.GetRegion(),
conf.Image, conf.GetRegion(), conf.UseMarketplaceImage(),
)
if err != nil {
return nil, fmt.Errorf("fetching image reference: %w", err)
Expand All @@ -141,7 +141,7 @@ func (a *Applier) terraformApplyVars(ctx context.Context, conf *config.Config) (
case cloudprovider.AWS:
return awsTerraformVars(conf, imageRef), nil
case cloudprovider.Azure:
return azureTerraformVars(conf, imageRef), nil
return azureTerraformVars(conf, imageRef)
case cloudprovider.GCP:
return gcpTerraformVars(conf, imageRef), nil
case cloudprovider.OpenStack:
Expand Down
2 changes: 1 addition & 1 deletion cli/internal/cloudcmd/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
type imageFetcher interface {
FetchReference(ctx context.Context,
provider cloudprovider.Provider, attestationVariant variant.Variant,
image, region string,
image, region string, useMarketplaceImage bool,
) (string, error)
}

Expand Down
2 changes: 1 addition & 1 deletion cli/internal/cloudcmd/clients_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ type stubImageFetcher struct {

func (f *stubImageFetcher) FetchReference(_ context.Context,
_ cloudprovider.Provider, _ variant.Variant,
_, _ string,
_, _ string, _ bool,
) (string, error) {
return f.reference, f.fetchReferenceErr
}
Expand Down
29 changes: 26 additions & 3 deletions cli/internal/cloudcmd/tfvars.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/mpimage"
"github.com/edgelesssys/constellation/v2/internal/role"
)

Expand Down Expand Up @@ -127,7 +128,7 @@ func normalizeAzureURIs(vars *terraform.AzureClusterVariables) *terraform.AzureC

// azureTerraformVars provides variables required to execute the Terraform scripts.
// It should be the only place to declare the Azure variables.
func azureTerraformVars(conf *config.Config, imageRef string) *terraform.AzureClusterVariables {
func azureTerraformVars(conf *config.Config, imageRef string) (*terraform.AzureClusterVariables, error) {
nodeGroups := make(map[string]terraform.AzureNodeGroup)
for groupName, group := range conf.NodeGroups {
zones := strings.Split(group.Zone, ",")
Expand All @@ -147,7 +148,6 @@ func azureTerraformVars(conf *config.Config, imageRef string) *terraform.AzureCl
Name: conf.Name,
NodeGroups: nodeGroups,
Location: conf.Provider.Azure.Location,
ImageID: imageRef,
CreateMAA: toPtr(conf.GetAttestationConfig().GetVariant().Equal(variant.AzureSEVSNP{})),
Debug: toPtr(conf.IsDebugCluster()),
ConfidentialVM: toPtr(conf.GetAttestationConfig().GetVariant().Equal(variant.AzureSEVSNP{})),
Expand All @@ -158,8 +158,31 @@ func azureTerraformVars(conf *config.Config, imageRef string) *terraform.AzureCl
InternalLoadBalancer: conf.InternalLoadBalancer,
}

if conf.UseMarketplaceImage() {
image, err := mpimage.NewFromURI(imageRef)
if err != nil {
return nil, fmt.Errorf("parsing marketplace image URI: %w", err)
}

azureImage, ok := image.(mpimage.AzureMarketplaceImage)
if !ok {
return nil, fmt.Errorf("expected Azure marketplace image, got %T", image)
}

// If a marketplace image is used, only the marketplace reference is required.
vars.MarketplaceImage = terraform.AzureMarketplaceImageVariables{
Publisher: azureImage.Publisher,
Product: azureImage.Offer,
Name: azureImage.SKU,
Version: azureImage.Version,
}
} else {
// If not, we need to specify the exact CommunityGalleries/.. image reference.
vars.ImageID = imageRef
}

vars = normalizeAzureURIs(vars)
return vars
return vars, nil
}

func azureTerraformIAMVars(conf *config.Config, oldVars terraform.AzureIAMVariables) *terraform.AzureIAMVariables {
Expand Down
4 changes: 2 additions & 2 deletions cli/internal/cmd/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@ func (a *applyCmd) runNodeImageUpgrade(cmd *cobra.Command, conf *config.Config)
provider := conf.GetProvider()
attestationVariant := conf.GetAttestationConfig().GetVariant()
region := conf.GetRegion()
imageReference, err := a.imageFetcher.FetchReference(cmd.Context(), provider, attestationVariant, conf.Image, region)
imageReference, err := a.imageFetcher.FetchReference(cmd.Context(), provider, attestationVariant, conf.Image, region, conf.UseMarketplaceImage())
if err != nil {
return fmt.Errorf("fetching image reference: %w", err)
}
Expand Down Expand Up @@ -846,6 +846,6 @@ type applier interface {
type imageFetcher interface {
FetchReference(ctx context.Context,
provider cloudprovider.Provider, attestationVariant variant.Variant,
image, region string,
image, region string, useMarketplaceImage bool,
) (string, error)
}
2 changes: 1 addition & 1 deletion cli/internal/cmd/upgradeapply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ type stubImageFetcher struct {

func (f *stubImageFetcher) FetchReference(_ context.Context,
_ cloudprovider.Provider, _ variant.Variant,
_, _ string,
_, _ string, _ bool,
) (string, error) {
return f.reference, f.fetchReferenceErr
}
14 changes: 14 additions & 0 deletions cli/internal/terraform/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ type AzureClusterVariables struct {
CustomEndpoint string `hcl:"custom_endpoint" cty:"custom_endpoint"`
// InternalLoadBalancer is true if an internal load balancer should be created.
InternalLoadBalancer bool `hcl:"internal_load_balancer" cty:"internal_load_balancer"`
// MarketplaceImage is the (optional) Azure Marketplace image to use.
MarketplaceImage AzureMarketplaceImageVariables `hcl:"marketplace_image" cty:"marketplace_image"`
}

// GetCreateMAA gets the CreateMAA variable.
Expand Down Expand Up @@ -250,6 +252,18 @@ type AzureIAMVariables struct {
ResourceGroup string `hcl:"resource_group_name" cty:"resource_group_name"`
}

// AzureMarketplaceImageVariables is a configuration for specifying an Azure Marketplace image.
type AzureMarketplaceImageVariables struct {
// Publisher is the publisher ID of the image.
Publisher string `hcl:"publisher" cty:"publisher"`
// Product is the product ID of the image.
Product string `hcl:"product" cty:"product"`
// Name is the name of the image.
Name string `hcl:"name" cty:"name"`
// Version is the version of the image.
Version string `hcl:"version" cty:"version"`
}

// String returns a string representation of the IAM-specific variables, formatted as Terraform variables.
func (v *AzureIAMVariables) String() string {
f := hclwrite.NewEmptyFile()
Expand Down
12 changes: 12 additions & 0 deletions cli/internal/terraform/variables_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ func TestAzureClusterVariables(t *testing.T) {
Debug: to.Ptr(true),
Location: "eu-central-1",
CustomEndpoint: "example.com",
MarketplaceImage: AzureMarketplaceImageVariables{
Publisher: "edgelesssys",
Product: "constellation",
Name: "constellation",
Version: "2.13.0",
},
}

// test that the variables are correctly rendered
Expand All @@ -216,6 +222,12 @@ node_groups = {
}
custom_endpoint = "example.com"
internal_load_balancer = false
marketplace_image = {
name = "constellation"
product = "constellation"
publisher = "edgelesssys"
version = "2.13.0"
}
`
got := vars.String()
assert.Equal(t, want, got)
Expand Down
27 changes: 27 additions & 0 deletions dev-docs/workflows/marketplace-images.md
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also add this to the user docs and add a "contact us" banner (similar to what we do in the marblerun docs). I don't have a strong opinion. What do you think @m1ghtym0?

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Using Marketplace Images in Constellation

This document explains the steps a user needs to take to run Constellation with dynamic billing via the cloud marketplaces.

## AWS

Marketplace Images on AWS are not available yet.

## Azure

On Azure, to use a marketplace image, ensure that the subscription has accepted the agreement to use marketplace images:

```bash
az vm image terms accept --publisher edgelesssystems --offer constellation --plan constellation
```

Then, set the VMs to use the marketplace image in the `constellation-conf.yaml` file:

```bash
yq eval -i ".provider.azure.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.

## GCP

Marketplace Images on GCP are not available yet.
4 changes: 4 additions & 0 deletions docs/docs/overview/license.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ You are free to use the Constellation binaries provided by Edgeless Systems to c
Enterprise Licenses don't have the above limitations and come with support and additional features. Find out more at the [product website](https://www.edgeless.systems/products/constellation/).

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

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/).
Loading
0