Skip to content

Commit a8f5b6f

Browse files
committed
feat: NFD local feature discovery for aws placement groups
1 parent 8d9a5a7 commit a8f5b6f

File tree

7 files changed

+317
-0
lines changed

7 files changed

+317
-0
lines changed

pkg/handlers/aws/mutation/metapatch_handler.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/instancetype"
1717
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/network"
1818
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/placementgroup"
19+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/placementgroupnfd"
1920
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/region"
2021
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/securitygroups"
2122
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/tags"
@@ -39,6 +40,7 @@ func MetaPatchHandler(mgr manager.Manager) handlers.Named {
3940
securitygroups.NewControlPlanePatch(),
4041
volumes.NewControlPlanePatch(),
4142
placementgroup.NewControlPlanePatch(),
43+
placementgroupnfd.NewControlPlanePatch(),
4244
}
4345
patchHandlers = append(patchHandlers, genericmutation.MetaMutators(mgr)...)
4446
patchHandlers = append(patchHandlers, genericmutation.ControlPlaneMetaMutators()...)
@@ -60,6 +62,7 @@ func MetaWorkerPatchHandler(mgr manager.Manager) handlers.Named {
6062
securitygroups.NewWorkerPatch(),
6163
volumes.NewWorkerPatch(),
6264
placementgroup.NewWorkerPatch(),
65+
placementgroupnfd.NewWorkerPatch(),
6366
}
6467
patchHandlers = append(patchHandlers, genericmutation.WorkerMetaMutators()...)
6568

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
# This script is used to discover the placement group for the node.
5+
# The script will be placed in /etc/kubernetes/node-feature-discovery/source.d/
6+
# The script will be executed by the node feature discovery's local feature hook.
7+
8+
9+
# Fetch IMDSv2 token
10+
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
11+
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
12+
13+
# Get placement info with HTTP status check
14+
PARTITION_RESPONSE=$(curl -s -w "%{http_code}" -H "X-aws-ec2-metadata-token: $TOKEN" \
15+
http://169.254.169.254/latest/meta-data/placement/partition-number)
16+
17+
PG_RESPONSE=$(curl -s -w "%{http_code}" -H "X-aws-ec2-metadata-token: $TOKEN" \
18+
http://169.254.169.254/latest/meta-data/placement/group-name)
19+
20+
# Extract HTTP status codes and content
21+
PARTITION_HTTP_CODE="${PARTITION_RESPONSE: -3}" # last 3 characters are the HTTP status code
22+
PARTITION_CONTENT="${PARTITION_RESPONSE%???}" # remove the last 3 characters to get the content
23+
24+
# Only print features if HTTP 200 response
25+
if [ "$PARTITION_HTTP_CODE" = "200" ] && [ -n "$PARTITION_CONTENT" ]; then
26+
echo "feature.node.kubernetes.io/partition=${PARTITION_CONTENT}"
27+
fi
28+
29+
PG_HTTP_CODE="${PG_RESPONSE: -3}" # last 3 characters are the HTTP status code
30+
PG_CONTENT="${PG_RESPONSE%???}" # remove the last 3 characters to get the content
31+
32+
if [ "$PG_HTTP_CODE" = "200" ] && [ -n "$PG_CONTENT" ]; then
33+
echo "feature.node.kubernetes.io/aws-placement-group=${PG_CONTENT}"
34+
fi
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package placementgroupnfd
2+
3+
import (
4+
"context"
5+
6+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
7+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
8+
cabpkv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
9+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
10+
ctrl "sigs.k8s.io/controller-runtime"
11+
"sigs.k8s.io/controller-runtime/pkg/client"
12+
13+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
14+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation"
15+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches"
16+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors"
17+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables"
18+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/placementgroup"
19+
)
20+
21+
type controlPlanePatchHandler struct {
22+
variableName string
23+
variableFieldPath []string
24+
}
25+
26+
func NewControlPlanePatch() *controlPlanePatchHandler {
27+
return &controlPlanePatchHandler{
28+
variableName: v1alpha1.ClusterConfigVariableName,
29+
variableFieldPath: []string{
30+
v1alpha1.ControlPlaneConfigVariableName,
31+
v1alpha1.AWSVariableName,
32+
placementgroup.VariableName,
33+
},
34+
}
35+
}
36+
37+
func (h *controlPlanePatchHandler) Mutate(
38+
ctx context.Context,
39+
obj *unstructured.Unstructured,
40+
vars map[string]apiextensionsv1.JSON,
41+
holderRef runtimehooksv1.HolderReference,
42+
_ client.ObjectKey,
43+
_ mutation.ClusterGetter,
44+
) error {
45+
log := ctrl.LoggerFrom(ctx).WithValues(
46+
"holderRef", holderRef,
47+
)
48+
49+
placementGroupVar, err := variables.Get[v1alpha1.PlacementGroup](
50+
vars,
51+
h.variableName,
52+
h.variableFieldPath...,
53+
)
54+
if err != nil {
55+
if variables.IsNotFoundError(err) {
56+
log.V(5).Info("placement group variable for AWS controlplane not defined.")
57+
return nil
58+
}
59+
return err
60+
}
61+
62+
log = log.WithValues(
63+
"variableName",
64+
h.variableName,
65+
"variableFieldPath",
66+
h.variableFieldPath,
67+
"variableValue",
68+
placementGroupVar,
69+
)
70+
71+
return patches.MutateIfApplicable(
72+
obj,
73+
vars,
74+
&holderRef,
75+
selectors.ControlPlane(), log,
76+
func(obj *cabpkv1.KubeadmConfigTemplate) error {
77+
log.WithValues(
78+
"patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(),
79+
"patchedObjectName", client.ObjectKeyFromObject(obj),
80+
).Info("setting placement group for local node feature discovery in AWS controlplane KubeadmConfigTemplate")
81+
obj.Spec.Template.Spec.Files = append(obj.Spec.Template.Spec.Files, cabpkv1.File{
82+
Path: PlacementGroupDiscoveryScriptFileOnRemote,
83+
Content: string(PlacementgroupDiscoveryScript),
84+
Permissions: "0700",
85+
})
86+
return nil
87+
},
88+
)
89+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package placementgroupnfd
2+
3+
import (
4+
"context"
5+
6+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
7+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
8+
cabpkv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
9+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
10+
ctrl "sigs.k8s.io/controller-runtime"
11+
"sigs.k8s.io/controller-runtime/pkg/client"
12+
13+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
14+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation"
15+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches"
16+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors"
17+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables"
18+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/placementgroup"
19+
)
20+
21+
type workerPatchHandler struct {
22+
variableName string
23+
variableFieldPath []string
24+
}
25+
26+
func NewWorkerPatch() *workerPatchHandler {
27+
return &workerPatchHandler{
28+
variableName: v1alpha1.WorkerConfigVariableName,
29+
variableFieldPath: []string{
30+
v1alpha1.AWSVariableName,
31+
placementgroup.VariableName,
32+
},
33+
}
34+
}
35+
36+
func (h *workerPatchHandler) Mutate(
37+
ctx context.Context,
38+
obj *unstructured.Unstructured,
39+
vars map[string]apiextensionsv1.JSON,
40+
holderRef runtimehooksv1.HolderReference,
41+
_ client.ObjectKey,
42+
_ mutation.ClusterGetter,
43+
) error {
44+
log := ctrl.LoggerFrom(ctx).WithValues(
45+
"holderRef", holderRef,
46+
)
47+
48+
placementGroupVar, err := variables.Get[v1alpha1.PlacementGroup](
49+
vars,
50+
h.variableName,
51+
h.variableFieldPath...,
52+
)
53+
if err != nil {
54+
if variables.IsNotFoundError(err) {
55+
log.V(5).Info("placement group variable for AWS worker not defined.")
56+
return nil
57+
}
58+
return err
59+
}
60+
61+
log = log.WithValues(
62+
"variableName",
63+
h.variableName,
64+
"variableFieldPath",
65+
h.variableFieldPath,
66+
"variableValue",
67+
placementGroupVar,
68+
)
69+
70+
return patches.MutateIfApplicable(
71+
obj,
72+
vars,
73+
&holderRef,
74+
selectors.WorkersKubeadmConfigTemplateSelector(), log,
75+
func(obj *cabpkv1.KubeadmConfigTemplate) error {
76+
log.WithValues(
77+
"patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(),
78+
"patchedObjectName", client.ObjectKeyFromObject(obj),
79+
).Info("setting placement group for local node feature discovery in AWS workers KubeadmConfig template")
80+
obj.Spec.Template.Spec.Files = append(obj.Spec.Template.Spec.Files, cabpkv1.File{
81+
Path: PlacementGroupDiscoveryScriptFileOnRemote,
82+
Content: string(PlacementgroupDiscoveryScript),
83+
Permissions: "0700",
84+
})
85+
return nil
86+
},
87+
)
88+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package placementgroupnfd
2+
3+
import (
4+
_ "embed"
5+
)
6+
7+
var (
8+
//go:embed embedded/placementgroup_discovery.sh
9+
PlacementgroupDiscoveryScript []byte
10+
11+
PlacementGroupDiscoveryScriptFileOnRemote = "/etc/kubernetes/node-feature-discovery/source.d/placementgroup_discovery.sh"
12+
)

pkg/handlers/eks/mutation/metapatch_handler.go

Lines changed: 2 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/eks/mutation/instancetype"
1515
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/eks/mutation/network"
1616
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/eks/mutation/placementgroup"
17+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/eks/mutation/placementgroupnfd"
1718
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/eks/mutation/region"
1819
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/eks/mutation/securitygroups"
1920
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/eks/mutation/tags"
@@ -46,6 +47,7 @@ func MetaWorkerPatchHandler(mgr manager.Manager) handlers.Named {
4647
securitygroups.NewWorkerPatch(),
4748
volumes.NewWorkerPatch(),
4849
placementgroup.NewWorkerPatch(),
50+
placementgroupnfd.NewWorkerPatch(),
4951
tags.NewWorkerPatch(),
5052
}
5153
patchHandlers = append(patchHandlers, workerMetaMutators()...)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package placementgroupnfd
2+
3+
import (
4+
"context"
5+
6+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
7+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
8+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
9+
ctrl "sigs.k8s.io/controller-runtime"
10+
"sigs.k8s.io/controller-runtime/pkg/client"
11+
12+
eksbootstrapv1 "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/external/sigs.k8s.io/cluster-api-provider-aws/v2/bootstrap/eks/api/v1beta2"
13+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
14+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation"
15+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches"
16+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors"
17+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables"
18+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/placementgroup"
19+
awsplacementgroupnfd "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/placementgroupnfd"
20+
)
21+
22+
type workerPatchHandler struct {
23+
variableName string
24+
variableFieldPath []string
25+
}
26+
27+
func NewWorkerPatch() *workerPatchHandler {
28+
return &workerPatchHandler{
29+
variableName: v1alpha1.WorkerConfigVariableName,
30+
variableFieldPath: []string{
31+
v1alpha1.EKSVariableName,
32+
placementgroup.VariableName,
33+
},
34+
}
35+
}
36+
37+
func (h *workerPatchHandler) Mutate(
38+
ctx context.Context,
39+
obj *unstructured.Unstructured,
40+
vars map[string]apiextensionsv1.JSON,
41+
holderRef runtimehooksv1.HolderReference,
42+
_ client.ObjectKey,
43+
_ mutation.ClusterGetter,
44+
) error {
45+
log := ctrl.LoggerFrom(ctx).WithValues(
46+
"holderRef", holderRef,
47+
)
48+
49+
placementGroupVar, err := variables.Get[v1alpha1.PlacementGroup](
50+
vars,
51+
h.variableName,
52+
h.variableFieldPath...,
53+
)
54+
if err != nil {
55+
if variables.IsNotFoundError(err) {
56+
log.V(5).Info("placement group variable for EKS worker not defined.")
57+
return nil
58+
}
59+
return err
60+
}
61+
62+
log = log.WithValues(
63+
"variableName",
64+
h.variableName,
65+
"variableFieldPath",
66+
h.variableFieldPath,
67+
"variableValue",
68+
placementGroupVar,
69+
)
70+
71+
return patches.MutateIfApplicable(
72+
obj,
73+
vars,
74+
&holderRef,
75+
selectors.WorkersConfigTemplateSelector(eksbootstrapv1.GroupVersion.String(), "NodeadmConfigTemplate"), log,
76+
func(obj *eksbootstrapv1.NodeadmConfigTemplate) error {
77+
log.WithValues(
78+
"patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(),
79+
"patchedObjectName", client.ObjectKeyFromObject(obj),
80+
).Info("setting placement group for local node feature discovery in EKS workers NodeadmConfigTemplate")
81+
obj.Spec.Template.Spec.Files = append(obj.Spec.Template.Spec.Files, eksbootstrapv1.File{
82+
Path: awsplacementgroupnfd.PlacementGroupDiscoveryScriptFileOnRemote,
83+
Content: string(awsplacementgroupnfd.PlacementgroupDiscoveryScript),
84+
Permissions: "0700",
85+
})
86+
return nil
87+
},
88+
)
89+
}

0 commit comments

Comments
 (0)