Skip to content

Commit bf77b3a

Browse files
committed
aws: Add support for AMD SEV-SNP VMs
Signed-off-by: Fangge Jin <[email protected]>
1 parent 567aa4a commit bf77b3a

File tree

7 files changed

+192
-0
lines changed

7 files changed

+192
-0
lines changed

pkg/asset/machines/aws/awsmachines.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,16 @@ func GenerateMachines(clusterID string, in *MachineInput) ([]*asset.RuntimeFile,
139139
)
140140
}
141141

142+
if mpool.CPUOptions != nil {
143+
cpuOptions := capa.CPUOptions{}
144+
145+
if mpool.CPUOptions.ConfidentialCompute != nil {
146+
cpuOptions.ConfidentialCompute = capa.AWSConfidentialComputePolicy(*mpool.CPUOptions.ConfidentialCompute)
147+
}
148+
149+
awsMachine.Spec.CPUOptions = cpuOptions
150+
}
151+
142152
result = append(result, &asset.RuntimeFile{
143153
File: asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", awsMachine.Name)},
144154
Object: awsMachine,

pkg/asset/machines/aws/machines.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"k8s.io/apimachinery/pkg/runtime"
1212
"k8s.io/apimachinery/pkg/util/sets"
1313
"k8s.io/utils/pointer"
14+
"k8s.io/utils/ptr"
1415

1516
v1 "github.com/openshift/api/config/v1"
1617
machinev1 "github.com/openshift/api/machine/v1"
@@ -35,6 +36,7 @@ type machineProviderInput struct {
3536
userTags map[string]string
3637
publicSubnet bool
3738
securityGroupIDs []string
39+
cpuOptions *awstypes.CPUOptions
3840
}
3941

4042
// Machines returns a list of machines for a machinepool.
@@ -77,6 +79,7 @@ func Machines(clusterID string, region string, subnets aws.SubnetsByZone, pool *
7779
userTags: userTags,
7880
publicSubnet: publicSubnet,
7981
securityGroupIDs: pool.Platform.AWS.AdditionalSecurityGroupIDs,
82+
cpuOptions: mpool.CPUOptions,
8083
})
8184
if err != nil {
8285
return nil, nil, errors.Wrap(err, "failed to create provider")
@@ -291,6 +294,16 @@ func provider(in *machineProviderInput) (*machineapi.AWSMachineProviderConfig, e
291294
config.MetadataServiceOptions.Authentication = machineapi.MetadataServiceAuthentication(in.imds.Authentication)
292295
}
293296

297+
if in.cpuOptions != nil {
298+
cpuOptions := machineapi.CPUOptions{}
299+
300+
if in.cpuOptions.ConfidentialCompute != nil {
301+
cpuOptions.ConfidentialCompute = ptr.To(machineapi.AWSConfidentialComputePolicy(*in.cpuOptions.ConfidentialCompute))
302+
}
303+
304+
config.CPUOptions = &cpuOptions
305+
}
306+
294307
return config, nil
295308
}
296309

pkg/asset/machines/aws/machinesets.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ func MachineSets(in *MachineSetInput) ([]*machineapi.MachineSet, error) {
102102
userTags: in.InstallConfigPlatformAWS.UserTags,
103103
publicSubnet: publicSubnet,
104104
securityGroupIDs: in.Pool.Platform.AWS.AdditionalSecurityGroupIDs,
105+
cpuOptions: mpool.CPUOptions,
105106
})
106107
if err != nil {
107108
return nil, errors.Wrap(err, "failed to create provider")

pkg/types/aws/machinepool.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ type MachinePool struct {
4848
// +kubebuilder:validation:MaxItems=10
4949
// +optional
5050
AdditionalSecurityGroupIDs []string `json:"additionalSecurityGroupIDs,omitempty"`
51+
52+
// CPUOptions defines CPU-related settings for the instance, including the confidential computing policy.
53+
// When omitted, this means no opinion and the AWS platform is left to choose a reasonable default.
54+
// More info:
55+
// https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CpuOptionsRequest.html,
56+
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/cpu-options-supported-instances-values.html
57+
// +optional
58+
CPUOptions *CPUOptions `json:"cpuOptions,omitempty,omitzero"`
5159
}
5260

5361
// Set sets the values from `required` to `a`.
@@ -96,6 +104,10 @@ func (a *MachinePool) Set(required *MachinePool) {
96104
if len(required.AdditionalSecurityGroupIDs) > 0 {
97105
a.AdditionalSecurityGroupIDs = required.AdditionalSecurityGroupIDs
98106
}
107+
108+
if required.CPUOptions != nil {
109+
a.CPUOptions = required.CPUOptions
110+
}
99111
}
100112

101113
// EC2RootVolume defines the storage for an ec2 instance.
@@ -135,3 +147,34 @@ type EC2Metadata struct {
135147
// +optional
136148
Authentication string `json:"authentication,omitempty"`
137149
}
150+
151+
// ConfidentialComputePolicy represents the confidential compute configuration for the instance.
152+
// +kubebuilder:validation:Enum=Disabled;AMDEncryptedVirtualizationNestedPaging
153+
type ConfidentialComputePolicy string
154+
155+
const (
156+
// ConfidentialComputePolicyDisabled disables confidential computing for the instance.
157+
ConfidentialComputePolicyDisabled ConfidentialComputePolicy = "Disabled"
158+
// ConfidentialComputePolicySEVSNP enables AMD SEV-SNP as the confidential computing technology for the instance.
159+
ConfidentialComputePolicySEVSNP ConfidentialComputePolicy = "AMDEncryptedVirtualizationNestedPaging"
160+
)
161+
162+
// CPUOptions defines CPU-related settings for the instance, including the confidential computing policy.
163+
// If provided, it must not be empty — at least one field must be set.
164+
// +kubebuilder:validation:MinProperties=1
165+
type CPUOptions struct {
166+
// ConfidentialCompute specifies whether confidential computing should be enabled for the instance,
167+
// and, if so, which confidential computing technology to use.
168+
// Valid values are: Disabled, AMDEncryptedVirtualizationNestedPaging and omitted.
169+
// When set to Disabled, confidential computing will be disabled for the instance.
170+
// When set to AMDEncryptedVirtualizationNestedPaging, AMD SEV-SNP will be used as the confidential computing technology for the instance.
171+
// In this case, ensure the following conditions are met:
172+
// 1) The selected instance type supports AMD SEV-SNP.
173+
// 2) The selected AWS region supports AMD SEV-SNP.
174+
// 3) The selected AMI supports AMD SEV-SNP.
175+
// More details can be checked at https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/sev-snp.html
176+
// When omitted, this means no opinion and the AWS platform is left to choose a reasonable default,
177+
// which is subject to change without notice. The current default is Disabled.
178+
// +optional
179+
ConfidentialCompute *ConfidentialComputePolicy `json:"confidentialCompute,omitempty"`
180+
}

pkg/types/aws/validation/machinepool.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ var (
2727
}()
2828

2929
validMetadataAuthValues = sets.NewString("Required", "Optional")
30+
31+
validConfidentialComputePolicy = []aws.ConfidentialComputePolicy{
32+
aws.ConfidentialComputePolicyDisabled,
33+
aws.ConfidentialComputePolicySEVSNP,
34+
}
3035
)
3136

3237
// AWS has a limit of 16 security groups. See:
@@ -53,6 +58,7 @@ func ValidateMachinePool(platform *aws.Platform, p *aws.MachinePool, fldPath *fi
5358
}
5459

5560
allErrs = append(allErrs, validateSecurityGroups(platform, p, fldPath)...)
61+
allErrs = append(allErrs, ValidateCPUOptions(p, fldPath)...)
5662

5763
return allErrs
5864
}
@@ -133,3 +139,41 @@ func ValidateMachinePoolArchitecture(pool *types.MachinePool, fldPath *field.Pat
133139
}
134140
return allErrs
135141
}
142+
143+
// ValidateCPUOptions checks that valid CPU options are set for a machine pool.
144+
func ValidateCPUOptions(p *aws.MachinePool, fldPath *field.Path) field.ErrorList {
145+
if p.CPUOptions == nil {
146+
return nil
147+
}
148+
149+
allErrs := field.ErrorList{}
150+
151+
if *p.CPUOptions == (aws.CPUOptions{}) {
152+
allErrs = append(
153+
allErrs,
154+
field.Invalid(
155+
fldPath.Child("cpuOptions"),
156+
"{}",
157+
"At least one field must be set if cpuOptions is provided",
158+
),
159+
)
160+
}
161+
162+
if p.CPUOptions.ConfidentialCompute != nil {
163+
switch *p.CPUOptions.ConfidentialCompute {
164+
case aws.ConfidentialComputePolicyDisabled, aws.ConfidentialComputePolicySEVSNP:
165+
// Valid values
166+
default:
167+
allErrs = append(
168+
allErrs,
169+
field.NotSupported(
170+
fldPath.Child("confidentialCompute"),
171+
p.CPUOptions.ConfidentialCompute,
172+
validConfidentialComputePolicy,
173+
),
174+
)
175+
}
176+
}
177+
178+
return allErrs
179+
}

pkg/types/aws/validation/machinepool_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/stretchr/testify/assert"
88
"k8s.io/apimachinery/pkg/util/validation/field"
9+
"k8s.io/utils/ptr"
910

1011
"github.com/openshift/installer/pkg/types/aws"
1112
)
@@ -250,3 +251,57 @@ func Test_validateAMIID(t *testing.T) {
250251
})
251252
}
252253
}
254+
255+
func Test_validateCPUOptions(t *testing.T) {
256+
cases := []struct {
257+
name string
258+
pool *aws.MachinePool
259+
err string
260+
}{{
261+
name: "confidential compute policy set to AMD SEV-SNP",
262+
pool: &aws.MachinePool{
263+
CPUOptions: &aws.CPUOptions{
264+
ConfidentialCompute: ptr.To(aws.ConfidentialComputePolicySEVSNP),
265+
},
266+
},
267+
}, {
268+
name: "confidential compute disabled",
269+
pool: &aws.MachinePool{
270+
CPUOptions: &aws.CPUOptions{
271+
ConfidentialCompute: ptr.To(aws.ConfidentialComputePolicyDisabled),
272+
},
273+
},
274+
}, {
275+
name: "empty confidential compute policy",
276+
pool: &aws.MachinePool{
277+
CPUOptions: &aws.CPUOptions{
278+
ConfidentialCompute: ptr.To(aws.ConfidentialComputePolicy("")),
279+
},
280+
},
281+
err: `^test-path.confidentialCompute: Unsupported value: "": supported values: "Disabled", "AMDEncryptedVirtualizationNestedPaging"$`,
282+
}, {
283+
name: "invalid confidential compute policy",
284+
pool: &aws.MachinePool{
285+
CPUOptions: &aws.CPUOptions{
286+
ConfidentialCompute: ptr.To(aws.ConfidentialComputePolicy("invalid")),
287+
},
288+
},
289+
err: `^test-path.confidentialCompute: Unsupported value: "invalid": supported values: "Disabled", "AMDEncryptedVirtualizationNestedPaging"$`,
290+
}, {
291+
name: "empty cpu options",
292+
pool: &aws.MachinePool{
293+
CPUOptions: &aws.CPUOptions{},
294+
},
295+
err: `^test-path.cpuOptions: Invalid value: "{}": At least one field must be set if cpuOptions is provided$`,
296+
}}
297+
for _, tc := range cases {
298+
t.Run(tc.name, func(t *testing.T) {
299+
err := ValidateCPUOptions(tc.pool, field.NewPath("test-path")).ToAggregate()
300+
if tc.err == "" {
301+
assert.NoError(t, err)
302+
} else {
303+
assert.Regexp(t, tc.err, err)
304+
}
305+
})
306+
}
307+
}

pkg/types/aws/zz_generated.deepcopy.go

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)