1
0
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:
Fangge Jin
2025-09-26 07:26:38 -04:00
parent 567aa4add2
commit bf77b3a834
7 changed files with 192 additions and 0 deletions

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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")

View File

@@ -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"`
}

View File

@@ -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
}

View File

@@ -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)
}
})
}
}

View File

@@ -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
}