misc: skip message about community license with marketplace image

This commit is contained in:
Malte Poll 2024-03-01 17:06:02 +01:00
parent 1c8a7e4c22
commit f94c6ca0d4
12 changed files with 66 additions and 28 deletions

View File

@ -40,7 +40,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/kms/uri" "github.com/edgelesssys/constellation/v2/internal/kms/uri"
"github.com/edgelesssys/constellation/v2/internal/semver" "github.com/edgelesssys/constellation/v2/internal/semver"
"github.com/edgelesssys/constellation/v2/internal/versions" "github.com/edgelesssys/constellation/v2/internal/versions"
"github.com/samber/slog-multi" slogmulti "github.com/samber/slog-multi"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
@ -365,7 +365,7 @@ func (a *applyCmd) apply(
} }
// Check license // Check license
a.checkLicenseFile(cmd, conf.GetProvider()) a.checkLicenseFile(cmd, conf.GetProvider(), conf.UseMarketplaceImage())
// Now start actually running the apply command // Now start actually running the apply command

View File

@ -22,18 +22,22 @@ import (
// with the license server. If no license file is present or if errors // with the license server. If no license file is present or if errors
// occur during the check, the user is informed and the community license // occur during the check, the user is informed and the community license
// is used. It is a no-op in the open source version of Constellation. // is used. It is a no-op in the open source version of Constellation.
func (a *applyCmd) checkLicenseFile(cmd *cobra.Command, csp cloudprovider.Provider) { func (a *applyCmd) checkLicenseFile(cmd *cobra.Command, csp cloudprovider.Provider, useMarketplaceImage bool) {
var licenseID string var licenseID string
a.log.Debug("Running license check") a.log.Debug("Running license check")
readBytes, err := a.fileHandler.Read(constants.LicenseFilename) readBytes, err := a.fileHandler.Read(constants.LicenseFilename)
if errors.Is(err, fs.ErrNotExist) { switch {
cmd.Printf("Using community license.\n") case useMarketplaceImage:
cmd.Println("Using marketplace image billing.")
licenseID = license.MarketplaceLicense
case errors.Is(err, fs.ErrNotExist):
cmd.Println("Using community license.")
licenseID = license.CommunityLicense licenseID = license.CommunityLicense
} else if err != nil { case err != nil:
cmd.Printf("Error: %v\nContinuing with community license.\n", err) cmd.Printf("Error: %v\nContinuing with community license.\n", err)
licenseID = license.CommunityLicense licenseID = license.CommunityLicense
} else { default:
cmd.Printf("Constellation license found!\n") cmd.Printf("Constellation license found!\n")
licenseID, err = license.FromBytes(readBytes) licenseID, err = license.FromBytes(readBytes)
if err != nil { if err != nil {
@ -43,9 +47,11 @@ func (a *applyCmd) checkLicenseFile(cmd *cobra.Command, csp cloudprovider.Provid
} }
quota, err := a.applier.CheckLicense(cmd.Context(), csp, !a.flags.skipPhases.contains(skipInitPhase), licenseID) quota, err := a.applier.CheckLicense(cmd.Context(), csp, !a.flags.skipPhases.contains(skipInitPhase), licenseID)
if err != nil { if err != nil && !useMarketplaceImage {
cmd.Printf("Unable to contact license server.\n") cmd.Printf("Unable to contact license server.\n")
cmd.Printf("Please keep your vCPU quota in mind.\n") cmd.Printf("Please keep your vCPU quota in mind.\n")
} else if licenseID == license.MarketplaceLicense {
// Do nothing. Billing is handled by the marketplace.
} else if licenseID == license.CommunityLicense { } else if licenseID == license.CommunityLicense {
cmd.Printf("For details, see https://docs.edgeless.systems/constellation/overview/license\n") cmd.Printf("For details, see https://docs.edgeless.systems/constellation/overview/license\n")
} else { } else {

View File

@ -17,4 +17,4 @@ import (
// with the license server. If no license file is present or if errors // with the license server. If no license file is present or if errors
// occur during the check, the user is informed and the community license // occur during the check, the user is informed and the community license
// is used. It is a no-op in the open source version of Constellation. // is used. It is a no-op in the open source version of Constellation.
func (a *applyCmd) checkLicenseFile(*cobra.Command, cloudprovider.Provider) {} func (a *applyCmd) checkLicenseFile(*cobra.Command, cloudprovider.Provider, bool) {}

View File

@ -720,7 +720,8 @@ func (c *Config) DeployYawolLoadBalancer() bool {
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) || (c.Provider.GCP != nil && c.Provider.GCP.UseMarketplaceImage != nil && *c.Provider.GCP.UseMarketplaceImage) ||
(c.Provider.AWS != nil && c.Provider.AWS.UseMarketplaceImage != nil && *c.Provider.AWS.UseMarketplaceImage) (c.Provider.AWS != nil && c.Provider.AWS.UseMarketplaceImage != nil && *c.Provider.AWS.UseMarketplaceImage) ||
(c.Provider.OpenStack != nil && c.Provider.OpenStack.Cloud == "stackit")
} }
// Validate checks the config values and returns validation errors. // Validate checks the config values and returns validation errors.

View File

@ -131,6 +131,9 @@ func buildMarketplaceImage(payload marketplaceImagePayload) (string, error) {
case cloudprovider.AWS: case cloudprovider.AWS:
// For AWS, we use the AMI alias, which just needs the version and infers the rest transparently. // For AWS, we use the AMI alias, which just needs the version and infers the rest transparently.
return fmt.Sprintf("resolve:ssm:/aws/service/marketplace/prod-77ylkenlkgufs/%s", payload.imgInfo.Version), nil return fmt.Sprintf("resolve:ssm:/aws/service/marketplace/prod-77ylkenlkgufs/%s", payload.imgInfo.Version), nil
case cloudprovider.OpenStack:
// For OpenStack / STACKIT, we use the image reference directly.
return getReferenceFromImageInfo(payload.provider, payload.attestationVariant.String(), payload.imgInfo, payload.filters...)
default: default:
return "", fmt.Errorf("marketplace images are not supported for csp %s", payload.provider.String()) return "", fmt.Errorf("marketplace images are not supported for csp %s", payload.provider.String())
} }

View File

@ -13,6 +13,8 @@ type Action string
const ( const (
// CommunityLicense is used by everyone who has not bought an enterprise license. // CommunityLicense is used by everyone who has not bought an enterprise license.
CommunityLicense = "00000000-0000-0000-0000-000000000000" CommunityLicense = "00000000-0000-0000-0000-000000000000"
// MarketplaceLicense is used by everyone who uses a marketplace image.
MarketplaceLicense = "11111111-1111-1111-1111-111111111111"
// Init action denotes the initialization of a Constellation cluster. // Init action denotes the initialization of a Constellation cluster.
Init Action = "init" Init Action = "init"

View File

@ -58,6 +58,10 @@ Required:
- `$SEMANTIC_VERSION` is the semantic version of the image, e.g. `vX.Y.Z` or `vX.Y.Z-pre...`. - `$SEMANTIC_VERSION` is the semantic version of the image, e.g. `vX.Y.Z` or `vX.Y.Z-pre...`.
- `version` (String) Semantic version of the image. - `version` (String) Semantic version of the image.
Optional:
- `marketplace_image` (Boolean) Whether a marketplace image should be used.
<a id="nestedatt--attestation"></a> <a id="nestedatt--attestation"></a>
### Nested Schema for `attestation` ### Nested Schema for `attestation`

View File

@ -49,6 +49,10 @@ The Constellation OS image must be [replicated to the region](https://docs.edgel
<a id="nestedatt--image"></a> <a id="nestedatt--image"></a>
### Nested Schema for `image` ### Nested Schema for `image`
Optional:
- `marketplace_image` (Boolean) Whether a marketplace image should be used.
Read-Only: Read-Only:
- `reference` (String) CSP-specific unique reference to the image. The format differs per CSP. - `reference` (String) CSP-specific unique reference to the image. The format differs per CSP.

View File

@ -162,6 +162,10 @@ Required:
- `$SEMANTIC_VERSION` is the semantic version of the image, e.g. `vX.Y.Z` or `vX.Y.Z-pre...`. - `$SEMANTIC_VERSION` is the semantic version of the image, e.g. `vX.Y.Z` or `vX.Y.Z-pre...`.
- `version` (String) Semantic version of the image. - `version` (String) Semantic version of the image.
Optional:
- `marketplace_image` (Boolean) Whether a marketplace image should be used.
<a id="nestedatt--network_config"></a> <a id="nestedatt--network_config"></a>
### Nested Schema for `network_config` ### Nested Schema for `network_config`

View File

@ -447,28 +447,31 @@ func (r *ClusterResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
return return
} }
licenseID := plannedState.LicenseID.ValueString()
if licenseID == "" {
resp.Diagnostics.AddWarning("Constellation license ID not set.",
"Continuing with community license.")
}
if licenseID == license.CommunityLicense {
resp.Diagnostics.AddWarning("Using community license.",
"For details, see https://docs.edgeless.systems/constellation/overview/license")
}
// Validate during plan. Must be done in ModifyPlan to read provider data. // Validate during plan. Must be done in ModifyPlan to read provider data.
// See https://developer.hashicorp.com/terraform/plugin/framework/resources/configure#define-resource-configure-method. // See https://developer.hashicorp.com/terraform/plugin/framework/resources/configure#define-resource-configure-method.
_, diags := r.getMicroserviceVersion(&plannedState) _, diags := r.getMicroserviceVersion(&plannedState)
resp.Diagnostics.Append(diags...) resp.Diagnostics.Append(diags...)
_, _, diags = r.getImageVersion(ctx, &plannedState) var image imageAttribute
image, _, diags = r.getImageVersion(ctx, &plannedState)
resp.Diagnostics.Append(diags...) resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() { if resp.Diagnostics.HasError() {
return return
} }
licenseID := plannedState.LicenseID.ValueString()
switch {
case image.MarketplaceImage != nil && *image.MarketplaceImage:
// Marketplace images do not require a license.
case licenseID == "":
resp.Diagnostics.AddWarning("Constellation license ID not set.",
"Continuing with community license.")
case licenseID == license.CommunityLicense:
resp.Diagnostics.AddWarning("Using community license.",
"For details, see https://docs.edgeless.systems/constellation/overview/license")
}
// Checks running on updates to the resource. (i.e. state and plan != nil) // Checks running on updates to the resource. (i.e. state and plan != nil)
if !req.State.Raw.IsNull() { if !req.State.Raw.IsNull() {
// Read currentState supplied by Terraform runtime into the model // Read currentState supplied by Terraform runtime into the model
@ -759,9 +762,13 @@ func (r *ClusterResource) apply(ctx context.Context, data *ClusterResourceModel,
// parse license ID // parse license ID
licenseID := data.LicenseID.ValueString() licenseID := data.LicenseID.ValueString()
if licenseID == "" { switch {
case image.MarketplaceImage != nil && *image.MarketplaceImage:
licenseID = license.MarketplaceLicense
case licenseID == "":
licenseID = license.CommunityLicense licenseID = license.CommunityLicense
} }
// license ID can be base64-encoded // license ID can be base64-encoded
licenseIDFromB64, err := base64.StdEncoding.DecodeString(licenseID) licenseIDFromB64, err := base64.StdEncoding.DecodeString(licenseID)
if err == nil { if err == nil {

View File

@ -97,9 +97,10 @@ func TestViolatedImageConstraint(t *testing.T) {
} }
input, diags := basetypes.NewObjectValueFrom(context.Background(), map[string]attr.Type{ input, diags := basetypes.NewObjectValueFrom(context.Background(), map[string]attr.Type{
"version": basetypes.StringType{}, "version": basetypes.StringType{},
"reference": basetypes.StringType{}, "reference": basetypes.StringType{},
"short_path": basetypes.StringType{}, "short_path": basetypes.StringType{},
"marketplace_image": basetypes.BoolType{},
}, img) }, img)
require.Equal(t, 0, diags.ErrorsCount()) require.Equal(t, 0, diags.ErrorsCount())
_, _, diags2 := sut.getImageVersion(context.Background(), &ClusterResourceModel{ _, _, diags2 := sut.getImageVersion(context.Background(), &ClusterResourceModel{

View File

@ -229,13 +229,19 @@ func newImageAttributeSchema(t attributeType) schema.Attribute {
Computed: !isInput, Computed: !isInput,
Required: isInput, Required: isInput,
}, },
"marketplace_image": schema.BoolAttribute{
Description: "Whether a marketplace image should be used.",
MarkdownDescription: "Whether a marketplace image should be used.",
Optional: true,
},
}, },
} }
} }
// imageAttribute is the image attribute's data model. // imageAttribute is the image attribute's data model.
type imageAttribute struct { type imageAttribute struct {
Reference string `tfsdk:"reference"` Reference string `tfsdk:"reference"`
Version string `tfsdk:"version"` Version string `tfsdk:"version"`
ShortPath string `tfsdk:"short_path"` ShortPath string `tfsdk:"short_path"`
MarketplaceImage *bool `tfsdk:"marketplace_image"`
} }