mirror of
https://github.com/openshift/installer.git
synced 2026-02-05 06:46:36 +01:00
aws: Add support for AMD SEV-SNP VMs
Signed-off-by: Fangge Jin <fjin@redhat.com>
This commit is contained in:
@@ -139,6 +139,16 @@ func GenerateMachines(clusterID string, in *MachineInput) ([]*asset.RuntimeFile,
|
||||
)
|
||||
}
|
||||
|
||||
if mpool.CPUOptions != nil {
|
||||
cpuOptions := capa.CPUOptions{}
|
||||
|
||||
if mpool.CPUOptions.ConfidentialCompute != nil {
|
||||
cpuOptions.ConfidentialCompute = capa.AWSConfidentialComputePolicy(*mpool.CPUOptions.ConfidentialCompute)
|
||||
}
|
||||
|
||||
awsMachine.Spec.CPUOptions = cpuOptions
|
||||
}
|
||||
|
||||
result = append(result, &asset.RuntimeFile{
|
||||
File: asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", awsMachine.Name)},
|
||||
Object: awsMachine,
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/utils/pointer"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
v1 "github.com/openshift/api/config/v1"
|
||||
machinev1 "github.com/openshift/api/machine/v1"
|
||||
@@ -35,6 +36,7 @@ type machineProviderInput struct {
|
||||
userTags map[string]string
|
||||
publicSubnet bool
|
||||
securityGroupIDs []string
|
||||
cpuOptions *awstypes.CPUOptions
|
||||
}
|
||||
|
||||
// Machines returns a list of machines for a machinepool.
|
||||
@@ -77,6 +79,7 @@ func Machines(clusterID string, region string, subnets aws.SubnetsByZone, pool *
|
||||
userTags: userTags,
|
||||
publicSubnet: publicSubnet,
|
||||
securityGroupIDs: pool.Platform.AWS.AdditionalSecurityGroupIDs,
|
||||
cpuOptions: mpool.CPUOptions,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to create provider")
|
||||
@@ -291,6 +294,16 @@ func provider(in *machineProviderInput) (*machineapi.AWSMachineProviderConfig, e
|
||||
config.MetadataServiceOptions.Authentication = machineapi.MetadataServiceAuthentication(in.imds.Authentication)
|
||||
}
|
||||
|
||||
if in.cpuOptions != nil {
|
||||
cpuOptions := machineapi.CPUOptions{}
|
||||
|
||||
if in.cpuOptions.ConfidentialCompute != nil {
|
||||
cpuOptions.ConfidentialCompute = ptr.To(machineapi.AWSConfidentialComputePolicy(*in.cpuOptions.ConfidentialCompute))
|
||||
}
|
||||
|
||||
config.CPUOptions = &cpuOptions
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ func MachineSets(in *MachineSetInput) ([]*machineapi.MachineSet, error) {
|
||||
userTags: in.InstallConfigPlatformAWS.UserTags,
|
||||
publicSubnet: publicSubnet,
|
||||
securityGroupIDs: in.Pool.Platform.AWS.AdditionalSecurityGroupIDs,
|
||||
cpuOptions: mpool.CPUOptions,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create provider")
|
||||
|
||||
@@ -48,6 +48,14 @@ type MachinePool struct {
|
||||
// +kubebuilder:validation:MaxItems=10
|
||||
// +optional
|
||||
AdditionalSecurityGroupIDs []string `json:"additionalSecurityGroupIDs,omitempty"`
|
||||
|
||||
// CPUOptions defines CPU-related settings for the instance, including the confidential computing policy.
|
||||
// When omitted, this means no opinion and the AWS platform is left to choose a reasonable default.
|
||||
// More info:
|
||||
// https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CpuOptionsRequest.html,
|
||||
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/cpu-options-supported-instances-values.html
|
||||
// +optional
|
||||
CPUOptions *CPUOptions `json:"cpuOptions,omitempty,omitzero"`
|
||||
}
|
||||
|
||||
// Set sets the values from `required` to `a`.
|
||||
@@ -96,6 +104,10 @@ func (a *MachinePool) Set(required *MachinePool) {
|
||||
if len(required.AdditionalSecurityGroupIDs) > 0 {
|
||||
a.AdditionalSecurityGroupIDs = required.AdditionalSecurityGroupIDs
|
||||
}
|
||||
|
||||
if required.CPUOptions != nil {
|
||||
a.CPUOptions = required.CPUOptions
|
||||
}
|
||||
}
|
||||
|
||||
// EC2RootVolume defines the storage for an ec2 instance.
|
||||
@@ -135,3 +147,34 @@ type EC2Metadata struct {
|
||||
// +optional
|
||||
Authentication string `json:"authentication,omitempty"`
|
||||
}
|
||||
|
||||
// ConfidentialComputePolicy represents the confidential compute configuration for the instance.
|
||||
// +kubebuilder:validation:Enum=Disabled;AMDEncryptedVirtualizationNestedPaging
|
||||
type ConfidentialComputePolicy string
|
||||
|
||||
const (
|
||||
// ConfidentialComputePolicyDisabled disables confidential computing for the instance.
|
||||
ConfidentialComputePolicyDisabled ConfidentialComputePolicy = "Disabled"
|
||||
// ConfidentialComputePolicySEVSNP enables AMD SEV-SNP as the confidential computing technology for the instance.
|
||||
ConfidentialComputePolicySEVSNP ConfidentialComputePolicy = "AMDEncryptedVirtualizationNestedPaging"
|
||||
)
|
||||
|
||||
// CPUOptions defines CPU-related settings for the instance, including the confidential computing policy.
|
||||
// If provided, it must not be empty — at least one field must be set.
|
||||
// +kubebuilder:validation:MinProperties=1
|
||||
type CPUOptions struct {
|
||||
// ConfidentialCompute specifies whether confidential computing should be enabled for the instance,
|
||||
// and, if so, which confidential computing technology to use.
|
||||
// Valid values are: Disabled, AMDEncryptedVirtualizationNestedPaging and omitted.
|
||||
// When set to Disabled, confidential computing will be disabled for the instance.
|
||||
// When set to AMDEncryptedVirtualizationNestedPaging, AMD SEV-SNP will be used as the confidential computing technology for the instance.
|
||||
// In this case, ensure the following conditions are met:
|
||||
// 1) The selected instance type supports AMD SEV-SNP.
|
||||
// 2) The selected AWS region supports AMD SEV-SNP.
|
||||
// 3) The selected AMI supports AMD SEV-SNP.
|
||||
// More details can be checked at https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/sev-snp.html
|
||||
// When omitted, this means no opinion and the AWS platform is left to choose a reasonable default,
|
||||
// which is subject to change without notice. The current default is Disabled.
|
||||
// +optional
|
||||
ConfidentialCompute *ConfidentialComputePolicy `json:"confidentialCompute,omitempty"`
|
||||
}
|
||||
|
||||
@@ -27,6 +27,11 @@ var (
|
||||
}()
|
||||
|
||||
validMetadataAuthValues = sets.NewString("Required", "Optional")
|
||||
|
||||
validConfidentialComputePolicy = []aws.ConfidentialComputePolicy{
|
||||
aws.ConfidentialComputePolicyDisabled,
|
||||
aws.ConfidentialComputePolicySEVSNP,
|
||||
}
|
||||
)
|
||||
|
||||
// AWS has a limit of 16 security groups. See:
|
||||
@@ -53,6 +58,7 @@ func ValidateMachinePool(platform *aws.Platform, p *aws.MachinePool, fldPath *fi
|
||||
}
|
||||
|
||||
allErrs = append(allErrs, validateSecurityGroups(platform, p, fldPath)...)
|
||||
allErrs = append(allErrs, ValidateCPUOptions(p, fldPath)...)
|
||||
|
||||
return allErrs
|
||||
}
|
||||
@@ -133,3 +139,41 @@ func ValidateMachinePoolArchitecture(pool *types.MachinePool, fldPath *field.Pat
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateCPUOptions checks that valid CPU options are set for a machine pool.
|
||||
func ValidateCPUOptions(p *aws.MachinePool, fldPath *field.Path) field.ErrorList {
|
||||
if p.CPUOptions == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if *p.CPUOptions == (aws.CPUOptions{}) {
|
||||
allErrs = append(
|
||||
allErrs,
|
||||
field.Invalid(
|
||||
fldPath.Child("cpuOptions"),
|
||||
"{}",
|
||||
"At least one field must be set if cpuOptions is provided",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
if p.CPUOptions.ConfidentialCompute != nil {
|
||||
switch *p.CPUOptions.ConfidentialCompute {
|
||||
case aws.ConfidentialComputePolicyDisabled, aws.ConfidentialComputePolicySEVSNP:
|
||||
// Valid values
|
||||
default:
|
||||
allErrs = append(
|
||||
allErrs,
|
||||
field.NotSupported(
|
||||
fldPath.Child("confidentialCompute"),
|
||||
p.CPUOptions.ConfidentialCompute,
|
||||
validConfidentialComputePolicy,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
"github.com/openshift/installer/pkg/types/aws"
|
||||
)
|
||||
@@ -250,3 +251,57 @@ func Test_validateAMIID(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_validateCPUOptions(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
pool *aws.MachinePool
|
||||
err string
|
||||
}{{
|
||||
name: "confidential compute policy set to AMD SEV-SNP",
|
||||
pool: &aws.MachinePool{
|
||||
CPUOptions: &aws.CPUOptions{
|
||||
ConfidentialCompute: ptr.To(aws.ConfidentialComputePolicySEVSNP),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
name: "confidential compute disabled",
|
||||
pool: &aws.MachinePool{
|
||||
CPUOptions: &aws.CPUOptions{
|
||||
ConfidentialCompute: ptr.To(aws.ConfidentialComputePolicyDisabled),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
name: "empty confidential compute policy",
|
||||
pool: &aws.MachinePool{
|
||||
CPUOptions: &aws.CPUOptions{
|
||||
ConfidentialCompute: ptr.To(aws.ConfidentialComputePolicy("")),
|
||||
},
|
||||
},
|
||||
err: `^test-path.confidentialCompute: Unsupported value: "": supported values: "Disabled", "AMDEncryptedVirtualizationNestedPaging"$`,
|
||||
}, {
|
||||
name: "invalid confidential compute policy",
|
||||
pool: &aws.MachinePool{
|
||||
CPUOptions: &aws.CPUOptions{
|
||||
ConfidentialCompute: ptr.To(aws.ConfidentialComputePolicy("invalid")),
|
||||
},
|
||||
},
|
||||
err: `^test-path.confidentialCompute: Unsupported value: "invalid": supported values: "Disabled", "AMDEncryptedVirtualizationNestedPaging"$`,
|
||||
}, {
|
||||
name: "empty cpu options",
|
||||
pool: &aws.MachinePool{
|
||||
CPUOptions: &aws.CPUOptions{},
|
||||
},
|
||||
err: `^test-path.cpuOptions: Invalid value: "{}": At least one field must be set if cpuOptions is provided$`,
|
||||
}}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := ValidateCPUOptions(tc.pool, field.NewPath("test-path")).ToAggregate()
|
||||
if tc.err == "" {
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
assert.Regexp(t, tc.err, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,27 @@
|
||||
|
||||
package aws
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CPUOptions) DeepCopyInto(out *CPUOptions) {
|
||||
*out = *in
|
||||
if in.ConfidentialCompute != nil {
|
||||
in, out := &in.ConfidentialCompute, &out.ConfidentialCompute
|
||||
*out = new(ConfidentialComputePolicy)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CPUOptions.
|
||||
func (in *CPUOptions) DeepCopy() *CPUOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CPUOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *EC2Metadata) DeepCopyInto(out *EC2Metadata) {
|
||||
*out = *in
|
||||
@@ -52,6 +73,11 @@ func (in *MachinePool) DeepCopyInto(out *MachinePool) {
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.CPUOptions != nil {
|
||||
in, out := &in.CPUOptions, &out.CPUOptions
|
||||
*out = new(CPUOptions)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user