Skip to content

Commit 02d0626

Browse files
authored
feat: Support AWS PlacementGroups (#1219)
This allows for specifying PlacementGroup name allowing for different strategies (configured in the pre-created PlacementGroup), including `Partition` which provides increased resilience to underlying hardware failure.
1 parent 999e36d commit 02d0626

11 files changed

+473
-0
lines changed

api/v1alpha1/aws_node_types.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,22 @@ type AWSGenericNodeSpec struct {
3838

3939
// +kubebuilder:validation:Optional
4040
AdditionalSecurityGroups AdditionalSecurityGroup `json:"additionalSecurityGroups,omitempty"`
41+
42+
// PlacementGroup specifies the placement group in which to launch the instance.
43+
// +kubebuilder:validation:Optional
44+
PlacementGroup *PlacementGroup `json:"placementGroupName,omitempty"`
4145
}
4246

4347
type AdditionalSecurityGroup []SecurityGroup
4448

49+
type PlacementGroup struct {
50+
// Name is the name of the placement group.
51+
// +kubebuilder:validation:Required
52+
// +kubebuilder:validation:MinLength=1
53+
// +kubebuilder:validation:MaxLength=255
54+
Name string `json:"name"`
55+
}
56+
4557
type SecurityGroup struct {
4658
// ID is the id of the security group
4759
// +kubebuilder:validation:Optional

api/v1alpha1/crds/caren.nutanix.com_awsclusterconfigs.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,17 @@ spec:
390390
instanceType:
391391
default: m5.xlarge
392392
type: string
393+
placementGroupName:
394+
description: PlacementGroup specifies the placement group in which to launch the instance.
395+
properties:
396+
name:
397+
description: Name is the name of the placement group.
398+
maxLength: 255
399+
minLength: 1
400+
type: string
401+
required:
402+
- name
403+
type: object
393404
type: object
394405
nodeRegistration:
395406
default: {}

api/v1alpha1/crds/caren.nutanix.com_awsworkernodeconfigs.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,18 @@ spec:
8989
default: m5.2xlarge
9090
description: The AWS instance type to use for the cluster Machines.
9191
type: string
92+
placementGroupName:
93+
description: PlacementGroup specifies the placement group in which
94+
to launch the instance.
95+
properties:
96+
name:
97+
description: Name is the name of the placement group.
98+
maxLength: 255
99+
minLength: 1
100+
type: string
101+
required:
102+
- name
103+
type: object
92104
type: object
93105
nodeRegistration:
94106
default: {}

api/v1alpha1/zz_generated.deepcopy.go

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

pkg/handlers/aws/mutation/metapatch_handler.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/iaminstanceprofile"
1515
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/instancetype"
1616
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/network"
17+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/placementgroup"
1718
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/region"
1819
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/securitygroups"
1920
genericmutation "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation"
@@ -30,6 +31,7 @@ func MetaPatchHandler(mgr manager.Manager) handlers.Named {
3031
instancetype.NewControlPlanePatch(),
3132
ami.NewControlPlanePatch(),
3233
securitygroups.NewControlPlanePatch(),
34+
placementgroup.NewControlPlanePatch(),
3335
}
3436
patchHandlers = append(patchHandlers, genericmutation.MetaMutators(mgr)...)
3537
patchHandlers = append(patchHandlers, genericmutation.ControlPlaneMetaMutators()...)
@@ -48,6 +50,7 @@ func MetaWorkerPatchHandler(mgr manager.Manager) handlers.Named {
4850
instancetype.NewWorkerPatch(),
4951
ami.NewWorkerPatch(),
5052
securitygroups.NewWorkerPatch(),
53+
placementgroup.NewWorkerPatch(),
5154
}
5255
patchHandlers = append(patchHandlers, genericmutation.WorkerMetaMutators()...)
5356

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright 2023 Nutanix. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package placementgroup
5+
6+
import (
7+
"context"
8+
9+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
10+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
11+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
12+
ctrl "sigs.k8s.io/controller-runtime"
13+
"sigs.k8s.io/controller-runtime/pkg/client"
14+
15+
capav1 "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
16+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
17+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation"
18+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches"
19+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors"
20+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables"
21+
)
22+
23+
const (
24+
// VariableName is the external patch variable name.
25+
VariableName = "placementGroup"
26+
)
27+
28+
type awsPlacementGroupControlPlanePatchHandler struct {
29+
variableName string
30+
variableFieldPath []string
31+
}
32+
33+
func NewControlPlanePatch() *awsPlacementGroupControlPlanePatchHandler {
34+
return newAWSPlacementGroupControlPlanePatchHandler(
35+
v1alpha1.ClusterConfigVariableName,
36+
v1alpha1.ControlPlaneConfigVariableName,
37+
v1alpha1.AWSVariableName,
38+
VariableName,
39+
)
40+
}
41+
42+
func newAWSPlacementGroupControlPlanePatchHandler(
43+
variableName string,
44+
variableFieldPath ...string,
45+
) *awsPlacementGroupControlPlanePatchHandler {
46+
return &awsPlacementGroupControlPlanePatchHandler{
47+
variableName: variableName,
48+
variableFieldPath: variableFieldPath,
49+
}
50+
}
51+
52+
func (h *awsPlacementGroupControlPlanePatchHandler) Mutate(
53+
ctx context.Context,
54+
obj *unstructured.Unstructured,
55+
vars map[string]apiextensionsv1.JSON,
56+
holderRef runtimehooksv1.HolderReference,
57+
_ client.ObjectKey,
58+
_ mutation.ClusterGetter,
59+
) error {
60+
log := ctrl.LoggerFrom(ctx).WithValues(
61+
"holderRef", holderRef,
62+
)
63+
64+
placementGroupVar, err := variables.Get[v1alpha1.PlacementGroup](
65+
vars,
66+
h.variableName,
67+
h.variableFieldPath...,
68+
)
69+
if err != nil {
70+
if variables.IsNotFoundError(err) {
71+
log.V(5).Info("AWS placement group variable for control-plane not defined")
72+
return nil
73+
}
74+
return err
75+
}
76+
77+
log = log.WithValues(
78+
"variableName",
79+
h.variableName,
80+
"variableFieldPath",
81+
h.variableFieldPath,
82+
"variableValue",
83+
placementGroupVar,
84+
)
85+
86+
return patches.MutateIfApplicable(
87+
obj,
88+
vars,
89+
&holderRef,
90+
selectors.InfrastructureControlPlaneMachines(
91+
"v1beta2",
92+
"AWSMachineTemplate",
93+
),
94+
log,
95+
func(obj *capav1.AWSMachineTemplate) error {
96+
log.WithValues(
97+
"patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(),
98+
"patchedObjectName", client.ObjectKeyFromObject(obj),
99+
).Info("setting placement group in control plane AWSMachineTemplate spec")
100+
101+
obj.Spec.Template.Spec.PlacementGroupName = placementGroupVar.Name
102+
103+
return nil
104+
},
105+
)
106+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright 2023 Nutanix. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package placementgroup
5+
6+
import (
7+
. "github.com/onsi/ginkgo/v2"
8+
"github.com/onsi/gomega"
9+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
10+
11+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
12+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation"
13+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest"
14+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/internal/test/request"
15+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/test/helpers"
16+
)
17+
18+
var _ = Describe("Generate PlacementGroup patches for ControlPlane", func() {
19+
patchGenerator := func() mutation.GeneratePatches {
20+
return mutation.NewMetaGeneratePatchesHandler(
21+
"",
22+
helpers.TestEnv.Client,
23+
NewControlPlanePatch(),
24+
).(mutation.GeneratePatches)
25+
}
26+
27+
testDefs := []capitest.PatchTestDef{
28+
{
29+
Name: "unset variable",
30+
},
31+
{
32+
Name: "placement group for controlplane set",
33+
Vars: []runtimehooksv1.Variable{
34+
capitest.VariableWithValue(
35+
v1alpha1.ClusterConfigVariableName,
36+
v1alpha1.PlacementGroup{Name: "pg-1234"},
37+
v1alpha1.ControlPlaneConfigVariableName,
38+
v1alpha1.AWSVariableName,
39+
VariableName,
40+
),
41+
},
42+
RequestItem: request.NewCPAWSMachineTemplateRequestItem("1234"),
43+
ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{
44+
Operation: "add",
45+
Path: "/spec/template/spec/placementGroupName",
46+
ValueMatcher: gomega.Equal("pg-1234"),
47+
}},
48+
},
49+
}
50+
51+
// create test node for each case
52+
for _, tt := range testDefs {
53+
It(tt.Name, func() {
54+
capitest.AssertGeneratePatches(
55+
GinkgoT(),
56+
patchGenerator,
57+
&tt,
58+
)
59+
})
60+
}
61+
})
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2023 Nutanix. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package placementgroup
5+
6+
import (
7+
"testing"
8+
9+
. "github.com/onsi/ginkgo/v2"
10+
. "github.com/onsi/gomega"
11+
)
12+
13+
func TestInstanceTypePatch(t *testing.T) {
14+
RegisterFailHandler(Fail)
15+
RunSpecs(t, "PlacementGroup patches for ControlPlane and Workers suite")
16+
}

0 commit comments

Comments
 (0)