Skip to content

Commit 7aaeb79

Browse files
authored
Merge pull request #212 from Vacant2333/userdata
feat: support custom userData
2 parents f92e2ed + bf695a6 commit 7aaeb79

File tree

4 files changed

+47
-7
lines changed

4 files changed

+47
-7
lines changed

charts/karpenter/crds/karpenter.k8s.alibabacloud_ecsnodeclasses.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,10 @@ spec:
367367
rule: self.all(k, k !='karpenter.sh/nodeclaim')
368368
- message: tag contains a restricted tag matching karpenter.k8s.alibabacloud/ecsnodeclass
369369
rule: self.all(k, k !='karpenter.k8s.alibabacloud/ecsnodeclass')
370+
userData:
371+
description: UserData to be applied to the provisioned nodes and executed
372+
before/after the node is registered.
373+
type: string
370374
vSwitchSelectionPolicy:
371375
default: cheapest
372376
description: VSwitchSelectionPolicy is the policy to select the vSwitch.

pkg/apis/v1alpha1/ecsnodeclass.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ type ECSNodeClassSpec struct {
9393
// +kubebuilder:validation:Pattern:="rg-[0-9a-z]+"
9494
// +optional
9595
ResourceGroupID string `json:"resourceGroupId,omitempty"`
96+
// UserData to be applied to the provisioned nodes and executed before/after the node is registered.
97+
// +optional
98+
UserData *string `json:"userData,omitempty"`
9699
}
97100

98101
// VSwitchSelectorTerm defines selection logic for a vSwitch used by Karpenter to launch nodes.

pkg/providers/ack/ack.go

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import (
4242
const defaultNodeLabel = "k8s.aliyun.com=true"
4343

4444
type Provider interface {
45-
GetNodeRegisterScript(context.Context, string, *karpv1.NodeClaim, *v1alpha1.KubeletConfiguration) (string, error)
45+
GetNodeRegisterScript(context.Context, string, *karpv1.NodeClaim, *v1alpha1.KubeletConfiguration, *string) (string, error)
4646
GetClusterCNI(context.Context) (string, error)
4747
LivenessProbe(*http.Request) error
4848
}
@@ -154,10 +154,11 @@ func (p *DefaultProvider) getTargetNodePoolID(ctx context.Context) (*string, err
154154
func (p *DefaultProvider) GetNodeRegisterScript(ctx context.Context,
155155
capacityType string,
156156
nodeClaim *karpv1.NodeClaim,
157-
kubeletCfg *v1alpha1.KubeletConfiguration) (string, error) {
157+
kubeletCfg *v1alpha1.KubeletConfiguration,
158+
userData *string) (string, error) {
158159
labels := lo.Assign(nodeClaim.Labels, map[string]string{karpv1.CapacityTypeLabelKey: capacityType})
159160
if cachedScript, ok := p.cache.Get(p.clusterID); ok {
160-
return p.resolveUserData(cachedScript.(string), labels, nodeClaim, kubeletCfg), nil
161+
return p.resolveUserData(cachedScript.(string), labels, nodeClaim, kubeletCfg, userData), nil
161162
}
162163

163164
nodepoolID, err := p.getTargetNodePoolID(ctx)
@@ -187,13 +188,24 @@ func (p *DefaultProvider) GetNodeRegisterScript(ctx context.Context,
187188
}
188189

189190
p.cache.SetDefault(p.clusterID, s)
190-
return p.resolveUserData(s, labels, nodeClaim, kubeletCfg), nil
191+
return p.resolveUserData(s, labels, nodeClaim, kubeletCfg, userData), nil
191192
}
192193

193-
func (p *DefaultProvider) resolveUserData(respStr string, labels map[string]string, nodeClaim *karpv1.NodeClaim, kubeletCfg *v1alpha1.KubeletConfiguration) string {
194+
func (p *DefaultProvider) resolveUserData(respStr string, labels map[string]string, nodeClaim *karpv1.NodeClaim,
195+
kubeletCfg *v1alpha1.KubeletConfiguration, userData *string) string {
196+
preUserData, postUserData := parseCustomUserData(userData)
197+
194198
var script bytes.Buffer
195199
// Add bash script header
196200
script.WriteString("#!/bin/bash\n\n")
201+
202+
// Insert preUserData if available
203+
if preUserData != "" {
204+
// Pre-userData: scripts to be executed before node registration
205+
script.WriteString("echo \"Executing preUserData...\"\n")
206+
script.WriteString(preUserData + "\n\n")
207+
}
208+
197209
// Clean up the input string
198210
cleanupStr := strings.ReplaceAll(respStr, "\r\n", "")
199211
script.WriteString(cleanupStr + " ")
@@ -203,7 +215,15 @@ func (p *DefaultProvider) resolveUserData(respStr string, labels map[string]stri
203215
cfg := convertNodeClassKubeletConfigToACKNodeConfig(kubeletCfg)
204216
script.WriteString(fmt.Sprintf("--node-config %s ", cfg))
205217
// Add taints
206-
script.WriteString(fmt.Sprintf("--taints %s", p.formatTaints(nodeClaim)))
218+
script.WriteString(fmt.Sprintf("--taints %s\n\n", p.formatTaints(nodeClaim)))
219+
220+
// Insert postUserData if available
221+
if postUserData != "" {
222+
// Post-userData: scripts to be executed after node registration
223+
script.WriteString("echo \"Executing postUserData...\"\n")
224+
script.WriteString(postUserData + "\n")
225+
}
226+
207227
// Encode to base64
208228
return base64.StdEncoding.EncodeToString(script.Bytes())
209229
}
@@ -252,3 +272,16 @@ func convertNodeClassKubeletConfigToACKNodeConfig(kubeletCfg *v1alpha1.KubeletCo
252272
}
253273
return base64.StdEncoding.EncodeToString(data)
254274
}
275+
276+
const userDataSeparator = "#===USERDATA_SEPARATOR==="
277+
278+
// By default, the UserData is executed after the node registration is completed.
279+
// If a user requires tasks to be executed both before and after node registration,
280+
// they must split the userdata into preUserData and postUserData using a SEPARATOR.
281+
func parseCustomUserData(userData *string) (string, string) {
282+
parts := strings.Split(tea.StringValue(userData), userDataSeparator)
283+
if len(parts) == 2 {
284+
return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
285+
}
286+
return "", tea.StringValue(userData)
287+
}

pkg/providers/instance/instance.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ func (p *DefaultProvider) getProvisioningGroup(ctx context.Context, nodeClass *v
540540
}
541541

542542
kubeletCfg := resolveKubeletConfiguration(nodeClass)
543-
userData, err := p.ackProvider.GetNodeRegisterScript(ctx, capacityType, nodeClaim, kubeletCfg)
543+
userData, err := p.ackProvider.GetNodeRegisterScript(ctx, capacityType, nodeClaim, kubeletCfg, nodeClass.Spec.UserData)
544544
if err != nil {
545545
log.FromContext(ctx).Error(err, "Failed to resolve user data for node")
546546
return nil, err

0 commit comments

Comments
 (0)