Move instanceType from CLI to config

This commit is contained in:
Nils Hanke 2022-08-31 17:35:33 +02:00 committed by Nils Hanke
parent 91d2c8ae73
commit 0aefe2c0ba
10 changed files with 269 additions and 217 deletions

View File

@ -43,6 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- cdbg now uses the Constellation config directly and does not require any extra config
- Azure CVMs are attested using SNP attestation
- Replaced kube-proxy with cilium
- VM instance types are now defined in the config, not via a CLI argument
### Deprecated
<!-- For soon-to-be removed features. -->

View File

@ -5,9 +5,7 @@ import (
"fmt"
"io/fs"
"github.com/edgelesssys/constellation/cli/internal/azure"
"github.com/edgelesssys/constellation/cli/internal/cloudcmd"
"github.com/edgelesssys/constellation/cli/internal/gcp"
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
@ -36,20 +34,6 @@ func NewCreateCmd() *cobra.Command {
must(cobra.MarkFlagRequired(cmd.Flags(), "control-plane-nodes"))
cmd.Flags().IntP("worker-nodes", "w", 0, "number of worker nodes (required)")
must(cobra.MarkFlagRequired(cmd.Flags(), "worker-nodes"))
cmd.Flags().StringP("instance-type", "t", "", "instance type of cluster nodes")
must(cmd.RegisterFlagCompletionFunc("instance-type", instanceTypeCompletion))
cmd.SetHelpTemplate(cmd.HelpTemplate() + fmt.Sprintf(`
Azure Confidential VM instance types:
%v
Azure Trusted Launch instance types:
%v
GCP instance types:
%v
`, formatInstanceTypes(azure.CVMInstanceTypes), formatInstanceTypes(azure.TrustedLaunchInstanceTypes), formatInstanceTypes(gcp.InstanceTypes)))
return cmd
}
@ -63,7 +47,7 @@ func runCreate(cmd *cobra.Command, args []string) error {
func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler, provider cloudprovider.Provider,
) (retErr error) {
flags, err := parseCreateFlags(cmd, provider)
flags, err := parseCreateFlags(cmd)
if err != nil {
return err
}
@ -85,11 +69,19 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
cmd.Println("Disabling Confidential VMs is insecure. Use only for evaluation purposes.")
}
var instanceType string
switch provider {
case cloudprovider.Azure:
instanceType = config.Provider.Azure.InstanceType
case cloudprovider.GCP:
instanceType = config.Provider.GCP.InstanceType
}
if !flags.yes {
// Ask user to confirm action.
cmd.Printf("The following Constellation cluster will be created:\n")
cmd.Printf("%d control-planes nodes of type %s will be created.\n", flags.controllerCount, flags.insType)
cmd.Printf("%d worker nodes of type %s will be created.\n", flags.workerCount, flags.insType)
cmd.Printf("%d control-planes nodes of type %s will be created.\n", flags.controllerCount, instanceType)
cmd.Printf("%d worker nodes of type %s will be created.\n", flags.workerCount, instanceType)
ok, err := askToConfirm(cmd, "Do you want to create this cluster?")
if err != nil {
return err
@ -100,7 +92,7 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
}
}
state, err := creator.Create(cmd.Context(), provider, config, flags.name, flags.insType, flags.controllerCount, flags.workerCount)
state, err := creator.Create(cmd.Context(), provider, config, flags.name, instanceType, flags.controllerCount, flags.workerCount)
if err != nil {
return err
}
@ -118,7 +110,7 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
}
// parseCreateFlags parses the flags of the create command.
func parseCreateFlags(cmd *cobra.Command, provider cloudprovider.Provider) (createFlags, error) {
func parseCreateFlags(cmd *cobra.Command) (createFlags, error) {
controllerCount, err := cmd.Flags().GetInt("control-plane-nodes")
if err != nil {
return createFlags{}, fmt.Errorf("parsing number of control-plane nodes: %w", err)
@ -135,17 +127,6 @@ func parseCreateFlags(cmd *cobra.Command, provider cloudprovider.Provider) (crea
return createFlags{}, fmt.Errorf("number of worker nodes must be at least %d", constants.MinWorkerCount)
}
insType, err := cmd.Flags().GetString("instance-type")
if err != nil {
return createFlags{}, fmt.Errorf("parsing instance type argument: %w", err)
}
if insType == "" {
insType = defaultInstanceType(provider)
}
if err := validInstanceTypeForProvider(cmd, insType, provider); err != nil {
return createFlags{}, err
}
name, err := cmd.Flags().GetString("name")
if err != nil {
return createFlags{}, fmt.Errorf("parsing name argument: %w", err)
@ -170,7 +151,6 @@ func parseCreateFlags(cmd *cobra.Command, provider cloudprovider.Provider) (crea
return createFlags{
controllerCount: controllerCount,
workerCount: workerCount,
insType: insType,
name: name,
configPath: configPath,
yes: yes,
@ -181,24 +161,11 @@ func parseCreateFlags(cmd *cobra.Command, provider cloudprovider.Provider) (crea
type createFlags struct {
controllerCount int
workerCount int
insType string
name string
configPath string
yes bool
}
// defaultInstanceType returns the default instance type for the given provider.
func defaultInstanceType(provider cloudprovider.Provider) string {
switch provider {
case cloudprovider.GCP:
return gcp.InstanceTypes[0]
case cloudprovider.Azure:
return azure.CVMInstanceTypes[0]
default:
return ""
}
}
// checkDirClean checks if files of a previous Constellation are left in the current working dir.
func checkDirClean(fileHandler file.Handler) error {
if _, err := fileHandler.Stat(constants.StateFilename); !errors.Is(err, fs.ErrNotExist) {
@ -242,20 +209,3 @@ func must(err error) {
panic(err)
}
}
func instanceTypeCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 1 {
return []string{}, cobra.ShellCompDirectiveError
}
switch args[0] {
case "gcp":
return gcp.InstanceTypes, cobra.ShellCompDirectiveNoFileComp
case "azure":
var azureInstanceTypes []string
azureInstanceTypes = append(azureInstanceTypes, azure.CVMInstanceTypes...)
azureInstanceTypes = append(azureInstanceTypes, azure.TrustedLaunchInstanceTypes...)
return azureInstanceTypes, cobra.ShellCompDirectiveNoFileComp
default:
return []string{}, cobra.ShellCompDirectiveError
}
}

View File

@ -7,8 +7,6 @@ import (
"strings"
"testing"
"github.com/edgelesssys/constellation/cli/internal/azure"
"github.com/edgelesssys/constellation/cli/internal/gcp"
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
@ -56,7 +54,6 @@ func TestCreate(t *testing.T) {
yesFlag bool
controllerCountFlag *int
workerCountFlag *int
insTypeFlag string
configFlag string
nameFlag string
stdin string
@ -136,15 +133,6 @@ func TestCreate(t *testing.T) {
controllerCountFlag: intPtr(3),
wantErr: true,
},
"flag invalid instance-type": {
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
creator: &stubCloudCreator{},
provider: cloudprovider.GCP,
controllerCountFlag: intPtr(1),
workerCountFlag: intPtr(1),
insTypeFlag: "invalid",
wantErr: true,
},
"old state in directory": {
setupFs: func(require *require.Assertions) afero.Fs {
fs := afero.NewMemMapFs()
@ -245,9 +233,6 @@ func TestCreate(t *testing.T) {
if tc.workerCountFlag != nil {
require.NoError(cmd.Flags().Set("worker-nodes", strconv.Itoa(*tc.workerCountFlag)))
}
if tc.insTypeFlag != "" {
require.NoError(cmd.Flags().Set("instance-type", tc.insTypeFlag))
}
fileHandler := file.NewHandler(tc.setupFs(require))
@ -353,47 +338,6 @@ func TestCreateCompletion(t *testing.T) {
}
}
func TestInstanceTypeCompletion(t *testing.T) {
testCases := map[string]struct {
args []string
wantResult []string
wantShellCD cobra.ShellCompDirective
}{
"azure": {
args: []string{"azure"},
wantResult: append(append([]string{}, azure.CVMInstanceTypes...), azure.TrustedLaunchInstanceTypes...),
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
},
"gcp": {
args: []string{"gcp"},
wantResult: gcp.InstanceTypes,
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
},
"empty args": {
args: []string{},
wantResult: []string{},
wantShellCD: cobra.ShellCompDirectiveError,
},
"unknown provider": {
args: []string{"foo"},
wantResult: []string{},
wantShellCD: cobra.ShellCompDirectiveError,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
cmd := &cobra.Command{}
result, shellCD := instanceTypeCompletion(cmd, tc.args, "")
assert.Equal(tc.wantResult, result)
assert.Equal(tc.wantShellCD, shellCD)
})
}
}
func intPtr(i int) *int {
return &i
}

View File

@ -1,7 +0,0 @@
package cmd
import "strings"
func formatInstanceTypes(types []string) string {
return " " + strings.Join(types, "\n ")
}

View File

@ -4,8 +4,6 @@ import (
"errors"
"fmt"
"github.com/edgelesssys/constellation/cli/internal/azure"
"github.com/edgelesssys/constellation/cli/internal/gcp"
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
"github.com/spf13/cobra"
)
@ -28,34 +26,3 @@ func isCloudProvider(arg int) cobra.PositionalArgs {
return nil
}
}
func validInstanceTypeForProvider(cmd *cobra.Command, insType string, provider cloudprovider.Provider) error {
switch provider {
case cloudprovider.GCP:
for _, instanceType := range gcp.InstanceTypes {
if insType == instanceType {
return nil
}
}
cmd.SetUsageTemplate("GCP instance types:\n" + formatInstanceTypes(gcp.InstanceTypes))
cmd.SilenceUsage = false
return fmt.Errorf("%s isn't a valid GCP instance type", insType)
case cloudprovider.Azure:
for _, instanceType := range azure.CVMInstanceTypes {
if insType == instanceType {
return nil
}
}
for _, instanceType := range azure.TrustedLaunchInstanceTypes {
if insType == instanceType {
return nil
}
}
cmd.SetUsageTemplate("Azure CVM instance types:\n" + formatInstanceTypes(azure.CVMInstanceTypes) +
"\n\nAzure Trusted Launch instance types:\n" + formatInstanceTypes(azure.TrustedLaunchInstanceTypes))
cmd.SilenceUsage = false
return fmt.Errorf("%s isn't a valid Azure instance type", insType)
default:
return fmt.Errorf("%s isn't a valid cloud platform", provider)
}
}

View File

@ -11,6 +11,7 @@ import (
"regexp"
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/internal/config/instancetypes"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/versions"
@ -146,6 +147,9 @@ type AzureConfig struct {
// Machine image used to create Constellation nodes.
Image string `yaml:"image" validate:"required"`
// description: |
// Virtual machine instance type to use for Constellation nodes.
InstanceType string `yaml:"instanceType" validate:"azure_instance_type"`
// description: |
// Type of a node's state disk. The type influences boot time and I/O performance. See: https://docs.microsoft.com/en-us/azure/virtual-machines/disks-types#disk-type-comparison
StateDiskType string `yaml:"stateDiskType" validate:"oneof=Premium_LRS Premium_ZRS Standard_LRS StandardSSD_LRS StandardSSD_ZRS"`
// description: |
@ -192,6 +196,9 @@ type GCPConfig struct {
// Machine image used to create Constellation nodes.
Image string `yaml:"image" validate:"required"`
// description: |
// Virtual machine instance type to use for Constellation nodes.
InstanceType string `yaml:"instanceType" validate:"gcp_instance_type"`
// description: |
// Type of a node's state disk. The type influences boot time and I/O performance. See: https://cloud.google.com/compute/docs/disks#disk-types
StateDiskType string `yaml:"stateDiskType" validate:"oneof=pd-standard pd-balanced pd-ssd"`
// description: |
@ -260,6 +267,7 @@ func Default() *Config {
UserAssignedIdentity: "",
ResourceGroup: "",
Image: DefaultImageAzure,
InstanceType: "Standard_DC4as_v5",
StateDiskType: "Premium_LRS",
Measurements: copyPCRMap(azurePCRs),
EnforcedMeasurements: []uint32{8, 9, 11, 12},
@ -272,6 +280,7 @@ func Default() *Config {
Region: "",
Zone: "",
Image: DefaultImageGCP,
InstanceType: "n2d-standard-4",
StateDiskType: "pd-ssd",
ServiceAccountKeyPath: "serviceAccountKey.json",
Measurements: copyPCRMap(gcpPCRs),
@ -290,6 +299,21 @@ func validateK8sVersion(fl validator.FieldLevel) bool {
return versions.IsSupportedK8sVersion(fl.Field().String())
}
func validateAzureInstanceType(fl validator.FieldLevel) bool {
azureConfig := fl.Parent().Interface().(AzureConfig)
var acceptNonCVM bool
if azureConfig.ConfidentialVM != nil {
// This is the inverse of the config value (acceptNonCVMs is true if confidentialVM is false).
// We could make the validator the other way around, but this should be an explicit bypass rather than checking if CVMs are "allowed".
acceptNonCVM = !*azureConfig.ConfidentialVM
}
return validInstanceTypeForProvider(fl.Field().String(), acceptNonCVM, cloudprovider.Azure)
}
func validateGCPInstanceType(fl validator.FieldLevel) bool {
return validInstanceTypeForProvider(fl.Field().String(), false, cloudprovider.GCP)
}
// Validate checks the config values and returns validation error messages.
// The function only returns an error if the validation itself fails.
func (c *Config) Validate() ([]string, error) {
@ -299,11 +323,30 @@ func (c *Config) Validate() ([]string, error) {
return nil, err
}
// Register Azure & GCP InstanceType validation error types
if err := validate.RegisterTranslation("azure_instance_type", trans, c.registerTranslateAzureInstanceTypeError, translateAzureInstanceTypeError); err != nil {
return nil, err
}
if err := validate.RegisterTranslation("gcp_instance_type", trans, registerTranslateGCPInstanceTypeError, translateGCPInstanceTypeError); err != nil {
return nil, err
}
// register custom validator with label supported_k8s_version to validate version based on available versionConfigs.
if err := validate.RegisterValidation("supported_k8s_version", validateK8sVersion); err != nil {
return nil, err
}
// register custom validator with label azure_instance_type to validate version based on available versionConfigs.
if err := validate.RegisterValidation("azure_instance_type", validateAzureInstanceType); err != nil {
return nil, err
}
// register custom validator with label azure_instance_type to validate version based on available versionConfigs.
if err := validate.RegisterValidation("gcp_instance_type", validateGCPInstanceType); err != nil {
return nil, err
}
err := validate.Struct(c)
if err == nil {
return nil, nil
@ -321,6 +364,32 @@ func (c *Config) Validate() ([]string, error) {
return msgs, nil
}
// Validation translation functions for Azure & GCP instance type error functions.
func (c *Config) registerTranslateAzureInstanceTypeError(ut ut.Translator) error {
// Suggest trusted launch VMs if confidential VMs have been specifically disabled
if c.Provider.Azure != nil && c.Provider.Azure.ConfidentialVM != nil && !*c.Provider.Azure.ConfidentialVM {
return ut.Add("azure_instance_type", fmt.Sprintf("{0} must be one of %v", instancetypes.AzureTrustedLaunchInstanceTypes), true)
}
// Otherwise suggest CVMs
return ut.Add("azure_instance_type", fmt.Sprintf("{0} must be one of %v", instancetypes.AzureCVMInstanceTypes), true)
}
func translateAzureInstanceTypeError(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("azure_instance_type", fe.Field())
return t
}
func registerTranslateGCPInstanceTypeError(ut ut.Translator) error {
return ut.Add("gcp_instance_type", fmt.Sprintf("{0} must be one of %v", instancetypes.GCPInstanceTypes), true)
}
func translateGCPInstanceTypeError(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("gcp_instance_type", fe.Field())
return t
}
// HasProvider checks whether the config contains the provider.
func (c *Config) HasProvider(provider cloudprovider.Provider) bool {
switch provider {
@ -430,3 +499,32 @@ func copyPCRMap(m map[uint32][]byte) map[uint32][]byte {
res.CopyFrom(m)
return res
}
func validInstanceTypeForProvider(insType string, acceptNonCVM bool, provider cloudprovider.Provider) bool {
switch provider {
case cloudprovider.GCP:
for _, instanceType := range instancetypes.GCPInstanceTypes {
if insType == instanceType {
return true
}
}
return false
case cloudprovider.Azure:
if acceptNonCVM {
for _, instanceType := range instancetypes.AzureTrustedLaunchInstanceTypes {
if insType == instanceType {
return true
}
}
} else {
for _, instanceType := range instancetypes.AzureCVMInstanceTypes {
if insType == instanceType {
return true
}
}
}
return false
default:
return false
}
}

View File

@ -199,7 +199,7 @@ func init() {
FieldName: "azure",
},
}
AzureConfigDoc.Fields = make([]encoder.Doc, 14)
AzureConfigDoc.Fields = make([]encoder.Doc, 15)
AzureConfigDoc.Fields[0].Name = "subscription"
AzureConfigDoc.Fields[0].Type = "string"
AzureConfigDoc.Fields[0].Note = ""
@ -220,56 +220,61 @@ func init() {
AzureConfigDoc.Fields[3].Note = ""
AzureConfigDoc.Fields[3].Description = "Machine image used to create Constellation nodes."
AzureConfigDoc.Fields[3].Comments[encoder.LineComment] = "Machine image used to create Constellation nodes."
AzureConfigDoc.Fields[4].Name = "stateDiskType"
AzureConfigDoc.Fields[4].Name = "instanceType"
AzureConfigDoc.Fields[4].Type = "string"
AzureConfigDoc.Fields[4].Note = ""
AzureConfigDoc.Fields[4].Description = "Type of a node's state disk. The type influences boot time and I/O performance. See: https://docs.microsoft.com/en-us/azure/virtual-machines/disks-types#disk-type-comparison"
AzureConfigDoc.Fields[4].Comments[encoder.LineComment] = "Type of a node's state disk. The type influences boot time and I/O performance. See: https://docs.microsoft.com/en-us/azure/virtual-machines/disks-types#disk-type-comparison"
AzureConfigDoc.Fields[5].Name = "userAssignedIdentity"
AzureConfigDoc.Fields[4].Description = "Virtual machine instance type to use for Constellation nodes."
AzureConfigDoc.Fields[4].Comments[encoder.LineComment] = "Virtual machine instance type to use for Constellation nodes."
AzureConfigDoc.Fields[5].Name = "stateDiskType"
AzureConfigDoc.Fields[5].Type = "string"
AzureConfigDoc.Fields[5].Note = ""
AzureConfigDoc.Fields[5].Description = "Authorize spawned VMs to access Azure API. See: https://docs.edgeless.systems/constellation/getting-started/install#authorization"
AzureConfigDoc.Fields[5].Comments[encoder.LineComment] = "Authorize spawned VMs to access Azure API. See: https://docs.edgeless.systems/constellation/getting-started/install#authorization"
AzureConfigDoc.Fields[6].Name = "resourceGroup"
AzureConfigDoc.Fields[5].Description = "Type of a node's state disk. The type influences boot time and I/O performance. See: https://docs.microsoft.com/en-us/azure/virtual-machines/disks-types#disk-type-comparison"
AzureConfigDoc.Fields[5].Comments[encoder.LineComment] = "Type of a node's state disk. The type influences boot time and I/O performance. See: https://docs.microsoft.com/en-us/azure/virtual-machines/disks-types#disk-type-comparison"
AzureConfigDoc.Fields[6].Name = "userAssignedIdentity"
AzureConfigDoc.Fields[6].Type = "string"
AzureConfigDoc.Fields[6].Note = ""
AzureConfigDoc.Fields[6].Description = "Resource group to use."
AzureConfigDoc.Fields[6].Comments[encoder.LineComment] = "Resource group to use."
AzureConfigDoc.Fields[7].Name = "appClientID"
AzureConfigDoc.Fields[6].Description = "Authorize spawned VMs to access Azure API. See: https://docs.edgeless.systems/constellation/getting-started/install#authorization"
AzureConfigDoc.Fields[6].Comments[encoder.LineComment] = "Authorize spawned VMs to access Azure API. See: https://docs.edgeless.systems/constellation/getting-started/install#authorization"
AzureConfigDoc.Fields[7].Name = "resourceGroup"
AzureConfigDoc.Fields[7].Type = "string"
AzureConfigDoc.Fields[7].Note = ""
AzureConfigDoc.Fields[7].Description = "Application client ID of the Active Directory app registration."
AzureConfigDoc.Fields[7].Comments[encoder.LineComment] = "Application client ID of the Active Directory app registration."
AzureConfigDoc.Fields[8].Name = "clientSecretValue"
AzureConfigDoc.Fields[7].Description = "Resource group to use."
AzureConfigDoc.Fields[7].Comments[encoder.LineComment] = "Resource group to use."
AzureConfigDoc.Fields[8].Name = "appClientID"
AzureConfigDoc.Fields[8].Type = "string"
AzureConfigDoc.Fields[8].Note = ""
AzureConfigDoc.Fields[8].Description = "Client secret value of the Active Directory app registration credentials."
AzureConfigDoc.Fields[8].Comments[encoder.LineComment] = "Client secret value of the Active Directory app registration credentials."
AzureConfigDoc.Fields[9].Name = "measurements"
AzureConfigDoc.Fields[9].Type = "Measurements"
AzureConfigDoc.Fields[8].Description = "Application client ID of the Active Directory app registration."
AzureConfigDoc.Fields[8].Comments[encoder.LineComment] = "Application client ID of the Active Directory app registration."
AzureConfigDoc.Fields[9].Name = "clientSecretValue"
AzureConfigDoc.Fields[9].Type = "string"
AzureConfigDoc.Fields[9].Note = ""
AzureConfigDoc.Fields[9].Description = "Expected confidential VM measurements."
AzureConfigDoc.Fields[9].Comments[encoder.LineComment] = "Expected confidential VM measurements."
AzureConfigDoc.Fields[10].Name = "enforcedMeasurements"
AzureConfigDoc.Fields[10].Type = "[]uint32"
AzureConfigDoc.Fields[9].Description = "Client secret value of the Active Directory app registration credentials."
AzureConfigDoc.Fields[9].Comments[encoder.LineComment] = "Client secret value of the Active Directory app registration credentials."
AzureConfigDoc.Fields[10].Name = "measurements"
AzureConfigDoc.Fields[10].Type = "Measurements"
AzureConfigDoc.Fields[10].Note = ""
AzureConfigDoc.Fields[10].Description = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning."
AzureConfigDoc.Fields[10].Comments[encoder.LineComment] = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning."
AzureConfigDoc.Fields[11].Name = "idKeyDigest"
AzureConfigDoc.Fields[11].Type = "string"
AzureConfigDoc.Fields[10].Description = "Expected confidential VM measurements."
AzureConfigDoc.Fields[10].Comments[encoder.LineComment] = "Expected confidential VM measurements."
AzureConfigDoc.Fields[11].Name = "enforcedMeasurements"
AzureConfigDoc.Fields[11].Type = "[]uint32"
AzureConfigDoc.Fields[11].Note = ""
AzureConfigDoc.Fields[11].Description = "Expected value for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf"
AzureConfigDoc.Fields[11].Comments[encoder.LineComment] = "Expected value for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf"
AzureConfigDoc.Fields[12].Name = "enforceIdKeyDigest"
AzureConfigDoc.Fields[12].Type = "bool"
AzureConfigDoc.Fields[11].Description = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning."
AzureConfigDoc.Fields[11].Comments[encoder.LineComment] = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning."
AzureConfigDoc.Fields[12].Name = "idKeyDigest"
AzureConfigDoc.Fields[12].Type = "string"
AzureConfigDoc.Fields[12].Note = ""
AzureConfigDoc.Fields[12].Description = "Enforce the specified idKeyDigest value during remote attestation."
AzureConfigDoc.Fields[12].Comments[encoder.LineComment] = "Enforce the specified idKeyDigest value during remote attestation."
AzureConfigDoc.Fields[13].Name = "confidentialVM"
AzureConfigDoc.Fields[12].Description = "Expected value for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf"
AzureConfigDoc.Fields[12].Comments[encoder.LineComment] = "Expected value for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf"
AzureConfigDoc.Fields[13].Name = "enforceIdKeyDigest"
AzureConfigDoc.Fields[13].Type = "bool"
AzureConfigDoc.Fields[13].Note = ""
AzureConfigDoc.Fields[13].Description = "Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview"
AzureConfigDoc.Fields[13].Comments[encoder.LineComment] = "Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview"
AzureConfigDoc.Fields[13].Description = "Enforce the specified idKeyDigest value during remote attestation."
AzureConfigDoc.Fields[13].Comments[encoder.LineComment] = "Enforce the specified idKeyDigest value during remote attestation."
AzureConfigDoc.Fields[14].Name = "confidentialVM"
AzureConfigDoc.Fields[14].Type = "bool"
AzureConfigDoc.Fields[14].Note = ""
AzureConfigDoc.Fields[14].Description = "Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview"
AzureConfigDoc.Fields[14].Comments[encoder.LineComment] = "Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview"
GCPConfigDoc.Type = "GCPConfig"
GCPConfigDoc.Comments[encoder.LineComment] = "GCPConfig are GCP specific configuration values used by the CLI."
@ -280,7 +285,7 @@ func init() {
FieldName: "gcp",
},
}
GCPConfigDoc.Fields = make([]encoder.Doc, 8)
GCPConfigDoc.Fields = make([]encoder.Doc, 9)
GCPConfigDoc.Fields[0].Name = "project"
GCPConfigDoc.Fields[0].Type = "string"
GCPConfigDoc.Fields[0].Note = ""
@ -301,26 +306,31 @@ func init() {
GCPConfigDoc.Fields[3].Note = ""
GCPConfigDoc.Fields[3].Description = "Machine image used to create Constellation nodes."
GCPConfigDoc.Fields[3].Comments[encoder.LineComment] = "Machine image used to create Constellation nodes."
GCPConfigDoc.Fields[4].Name = "stateDiskType"
GCPConfigDoc.Fields[4].Name = "instanceType"
GCPConfigDoc.Fields[4].Type = "string"
GCPConfigDoc.Fields[4].Note = ""
GCPConfigDoc.Fields[4].Description = "Type of a node's state disk. The type influences boot time and I/O performance. See: https://cloud.google.com/compute/docs/disks#disk-types"
GCPConfigDoc.Fields[4].Comments[encoder.LineComment] = "Type of a node's state disk. The type influences boot time and I/O performance. See: https://cloud.google.com/compute/docs/disks#disk-types"
GCPConfigDoc.Fields[5].Name = "serviceAccountKeyPath"
GCPConfigDoc.Fields[4].Description = "Virtual machine instance type to use for Constellation nodes."
GCPConfigDoc.Fields[4].Comments[encoder.LineComment] = "Virtual machine instance type to use for Constellation nodes."
GCPConfigDoc.Fields[5].Name = "stateDiskType"
GCPConfigDoc.Fields[5].Type = "string"
GCPConfigDoc.Fields[5].Note = ""
GCPConfigDoc.Fields[5].Description = "Path of service account key file. For needed service account roles, see https://constellation-docs.edgeless.systems/constellation/getting-started/install#authorization"
GCPConfigDoc.Fields[5].Comments[encoder.LineComment] = "Path of service account key file. For needed service account roles, see https://constellation-docs.edgeless.systems/constellation/getting-started/install#authorization"
GCPConfigDoc.Fields[6].Name = "measurements"
GCPConfigDoc.Fields[6].Type = "Measurements"
GCPConfigDoc.Fields[5].Description = "Type of a node's state disk. The type influences boot time and I/O performance. See: https://cloud.google.com/compute/docs/disks#disk-types"
GCPConfigDoc.Fields[5].Comments[encoder.LineComment] = "Type of a node's state disk. The type influences boot time and I/O performance. See: https://cloud.google.com/compute/docs/disks#disk-types"
GCPConfigDoc.Fields[6].Name = "serviceAccountKeyPath"
GCPConfigDoc.Fields[6].Type = "string"
GCPConfigDoc.Fields[6].Note = ""
GCPConfigDoc.Fields[6].Description = "Expected confidential VM measurements."
GCPConfigDoc.Fields[6].Comments[encoder.LineComment] = "Expected confidential VM measurements."
GCPConfigDoc.Fields[7].Name = "enforcedMeasurements"
GCPConfigDoc.Fields[7].Type = "[]uint32"
GCPConfigDoc.Fields[6].Description = "Path of service account key file. For needed service account roles, see https://constellation-docs.edgeless.systems/constellation/getting-started/install#authorization"
GCPConfigDoc.Fields[6].Comments[encoder.LineComment] = "Path of service account key file. For needed service account roles, see https://constellation-docs.edgeless.systems/constellation/getting-started/install#authorization"
GCPConfigDoc.Fields[7].Name = "measurements"
GCPConfigDoc.Fields[7].Type = "Measurements"
GCPConfigDoc.Fields[7].Note = ""
GCPConfigDoc.Fields[7].Description = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning."
GCPConfigDoc.Fields[7].Comments[encoder.LineComment] = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning."
GCPConfigDoc.Fields[7].Description = "Expected confidential VM measurements."
GCPConfigDoc.Fields[7].Comments[encoder.LineComment] = "Expected confidential VM measurements."
GCPConfigDoc.Fields[8].Name = "enforcedMeasurements"
GCPConfigDoc.Fields[8].Type = "[]uint32"
GCPConfigDoc.Fields[8].Note = ""
GCPConfigDoc.Fields[8].Description = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning."
GCPConfigDoc.Fields[8].Comments[encoder.LineComment] = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning."
QEMUConfigDoc.Type = "QEMUConfig"
QEMUConfigDoc.Comments[encoder.LineComment] = ""

View File

@ -5,6 +5,7 @@ import (
"testing"
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/internal/config/instancetypes"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero"
@ -379,3 +380,91 @@ func TestConfig_IsImageDebug(t *testing.T) {
})
}
}
func TestValidInstanceTypeForProvider(t *testing.T) {
testCases := map[string]struct {
provider cloudprovider.Provider
instanceTypes []string
nonCVMsAllowed bool
expectedResult bool
}{
"empty all": {
provider: cloudprovider.Unknown,
instanceTypes: []string{},
expectedResult: false,
},
"empty azure only CVMs": {
provider: cloudprovider.Azure,
instanceTypes: []string{},
expectedResult: false,
},
"empty azure with non-CVMs": {
provider: cloudprovider.Azure,
instanceTypes: []string{},
nonCVMsAllowed: true,
expectedResult: false,
},
"empty gcp": {
provider: cloudprovider.GCP,
instanceTypes: []string{},
expectedResult: false,
},
"azure only CVMs": {
provider: cloudprovider.Azure,
instanceTypes: instancetypes.AzureCVMInstanceTypes,
expectedResult: true,
},
"azure CVMs but CVMs disabled": {
provider: cloudprovider.Azure,
instanceTypes: instancetypes.AzureCVMInstanceTypes,
nonCVMsAllowed: true,
expectedResult: false,
},
"azure trusted launch VMs with CVMs enabled": {
provider: cloudprovider.Azure,
instanceTypes: instancetypes.AzureTrustedLaunchInstanceTypes,
expectedResult: false,
},
"azure trusted launch VMs with CVMs disbled": {
provider: cloudprovider.Azure,
instanceTypes: instancetypes.AzureTrustedLaunchInstanceTypes,
nonCVMsAllowed: true,
expectedResult: true,
},
"gcp": {
provider: cloudprovider.GCP,
instanceTypes: instancetypes.GCPInstanceTypes,
expectedResult: true,
},
"put gcp when azure is set": {
provider: cloudprovider.Azure,
instanceTypes: instancetypes.GCPInstanceTypes,
expectedResult: false,
},
"put gcp when azure is set with CVMs disabled": {
provider: cloudprovider.Azure,
instanceTypes: instancetypes.GCPInstanceTypes,
nonCVMsAllowed: true,
expectedResult: false,
},
"put azure when gcp is set": {
provider: cloudprovider.GCP,
instanceTypes: instancetypes.AzureCVMInstanceTypes,
expectedResult: false,
},
"put azure when gcp is set with CVMs disabled": {
provider: cloudprovider.GCP,
instanceTypes: instancetypes.AzureTrustedLaunchInstanceTypes,
nonCVMsAllowed: true,
expectedResult: false,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
for _, instanceType := range tc.instanceTypes {
assert.Equal(tc.expectedResult, validInstanceTypeForProvider(instanceType, tc.nonCVMsAllowed, tc.provider), instanceType)
}
})
}
}

View File

@ -1,7 +1,7 @@
package azure
package instancetypes
// CVMInstanceTypes are valid Azure CVM instance types.
var CVMInstanceTypes = []string{
// AzureCVMInstanceTypes are valid Azure CVM instance types.
var AzureCVMInstanceTypes = []string{
// CVMs (3rd Generation EPYC 7763v processors)
// DCasv5-series
"Standard_DC4as_v5",
@ -39,8 +39,8 @@ var CVMInstanceTypes = []string{
"Standard_EC96ads_v5",
}
// TrustedLaunchInstanceTypes are valid Azure Trusted Launch instance types.
var TrustedLaunchInstanceTypes = []string{
// AzureTrustedLaunchInstanceTypes are valid Azure Trusted Launch instance types.
var AzureTrustedLaunchInstanceTypes = []string{
// Trusted Launch (2nd Generation AMD EPYC 7452 or 3rd Generation EPYC 7763v processors)
// Dav4-series
"Standard_D4a_v4",

View File

@ -1,7 +1,7 @@
package gcp
package instancetypes
// InstanceTypes are valid GCP instance types.
var InstanceTypes = []string{
// GCPInstanceTypes are valid GCP instance types.
var GCPInstanceTypes = []string{
"n2d-standard-4",
"n2d-standard-8",
"n2d-standard-16",