Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce a feature to propagate the machine name to user data and as label to the node #919

Merged
merged 7 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions pkg/util/provider/machinecontroller/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ func (c *controller) reconcileClusterMachine(ctx context.Context, machine *v1alp
return retry, err
}

retry, err = c.syncMachineNameToNode(ctx, machine)
if err != nil {
return retry, err
}

retry, err = c.syncMachineNodeTemplates(ctx, machine)
if err != nil {
return retry, err
Expand Down Expand Up @@ -355,8 +360,10 @@ func (c *controller) triggerCreationFlow(ctx context.Context, createMachineReque

// we should avoid mutating Secret, since it goes all the way into the Informer's store
secretCopy := createMachineRequest.Secret.DeepCopy()
err := c.addBootstrapTokenToUserData(ctx, machine, secretCopy)
if err != nil {
if err := c.addBootstrapTokenToUserData(ctx, machine, secretCopy); err != nil {
return machineutils.ShortRetry, err
}
if err := c.addMachineNameToUserData(machine, secretCopy); err != nil {
return machineutils.ShortRetry, err
}
createMachineRequest.Secret = secretCopy
Expand Down
42 changes: 37 additions & 5 deletions pkg/util/provider/machinecontroller/machine_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,38 @@ func mergeDataMaps(in map[string][]byte, maps ...map[string][]byte) map[string][
return out
}

// syncMachineNameToNode syncs the machine name on the corresponding node object
// by adding a machine name label to its metadata.
func (c *controller) syncMachineNameToNode(ctx context.Context, machine *v1alpha1.Machine) (machineutils.RetryPeriod, error) {
node, err := c.nodeLister.Get(getNodeName(machine))
if err != nil {
if apierrors.IsNotFound(err) {
// Don't return error so that other steps can be executed.
return machineutils.LongRetry, nil
}
klog.Errorf("Error occurred while trying to fetch node object - err: %s", err)
return machineutils.ShortRetry, err
}

if node.Labels[machineutils.MachineLabelKey] == machine.Name {
return machineutils.LongRetry, nil
}

if node.Labels == nil {
node.Labels = make(map[string]string)
}
node.Labels[machineutils.MachineLabelKey] = machine.Name

if _, err := c.targetCoreClient.CoreV1().Nodes().Update(ctx, node, metav1.UpdateOptions{}); err != nil {
if apierrors.IsConflict(err) {
return machineutils.ConflictRetry, err
}
return machineutils.ShortRetry, err
}

return machineutils.LongRetry, nil
}

// syncMachineNodeTemplate syncs nodeTemplates between machine and corresponding node-object.
// It ensures, that any nodeTemplate element available on Machine should be available on node-object.
// Although there could be more elements already available on node-object which will not be touched.
Expand All @@ -241,12 +273,12 @@ func (c *controller) syncMachineNodeTemplates(ctx context.Context, machine *v1al
lastAppliedALT v1alpha1.NodeTemplateSpec
)

node, err := c.nodeLister.Get(machine.Labels[v1alpha1.NodeLabelKey])
if err != nil && apierrors.IsNotFound(err) {
// Dont return error so that other steps can be executed.
return machineutils.LongRetry, nil
}
node, err := c.nodeLister.Get(getNodeName(machine))
if err != nil {
if apierrors.IsNotFound(err) {
// Don't return error so that other steps can be executed.
return machineutils.LongRetry, nil
}
klog.Errorf("Error occurred while trying to fetch node object - err: %s", err)
return machineutils.LongRetry, err
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,7 @@
/*
(c) 2017 SAP SE or an SAP affiliate company. All rights reserved.
// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors
//
// SPDX-License-Identifier: Apache-2.0

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Modifications Copyright (c) 2017 SAP SE or an SAP affiliate company. All rights reserved.
*/

// Package controller is used to provide the core functionalities of machine-controller-manager
package controller

import (
Expand All @@ -29,19 +14,27 @@ import (
"strings"
"time"

"github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
bootstraptokenapi "k8s.io/cluster-bootstrap/token/api"
bootstraptokenutil "k8s.io/cluster-bootstrap/token/util"
"k8s.io/klog/v2"

"github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
)

const placeholder = "<<BOOTSTRAP_TOKEN>>"
const (
bootstrapTokenPlaceholder = "<<BOOTSTRAP_TOKEN>>"
machineNamePlaceholder = "<<MACHINE_NAME>>"
)

// urlEncodedPlaceholder is a placeholder that can for instance occur in ignition userdata format
var urlEncodedPlaceholder = url.QueryEscape(placeholder)
var (
// urlEncodedBootstrapTokenPlaceholder is a bootstrapTokenPlaceholder that can for instance occur in ignition userdata format
urlEncodedBootstrapTokenPlaceholder = url.QueryEscape(bootstrapTokenPlaceholder)
// urlEncodedMachineNamePlaceholder is a machineNamePlaceholder that can for instance occur in ignition userdata format
urlEncodedMachineNamePlaceholder = url.QueryEscape(machineNamePlaceholder)
)

func (c *controller) addBootstrapTokenToUserData(ctx context.Context, machine *v1alpha1.Machine, secret *corev1.Secret) error {
var (
Expand All @@ -52,7 +45,7 @@ func (c *controller) addBootstrapTokenToUserData(ctx context.Context, machine *v

if userDataB, exists = secret.Data["userData"]; !exists {
// If userData key is not founds
return fmt.Errorf("Userdata field not found in secret for machine %q", machine.Name)
return fmt.Errorf("userdata field not found in secret for machine %q", machine.Name)
}
userDataS = string(userDataB)

Expand All @@ -66,12 +59,12 @@ func (c *controller) addBootstrapTokenToUserData(ctx context.Context, machine *v
string(bootstrapTokenSecret.Data[bootstraptokenapi.BootstrapTokenSecretKey]),
)

if strings.Contains(userDataS, placeholder) {
klog.V(4).Infof("replacing placeholder %s with %s in user-data!", placeholder, token)
userDataS = strings.ReplaceAll(userDataS, placeholder, token)
} else if strings.Contains(userDataS, urlEncodedPlaceholder) {
klog.V(4).Infof("replacing url encoded placeholder %s with %s in user-data!", urlEncodedPlaceholder, url.QueryEscape(token))
userDataS = strings.ReplaceAll(userDataS, urlEncodedPlaceholder, url.QueryEscape(token))
if strings.Contains(userDataS, bootstrapTokenPlaceholder) {
klog.V(4).Infof("replacing placeholder %s with %s in user-data!", bootstrapTokenPlaceholder, token)
userDataS = strings.ReplaceAll(userDataS, bootstrapTokenPlaceholder, token)
} else if strings.Contains(userDataS, urlEncodedBootstrapTokenPlaceholder) {
klog.V(4).Infof("replacing url encoded placeholder %s with %s in user-data!", urlEncodedBootstrapTokenPlaceholder, url.QueryEscape(token))
userDataS = strings.ReplaceAll(userDataS, urlEncodedBootstrapTokenPlaceholder, url.QueryEscape(token))
} else {
klog.Warningf("no bootstrap token placeholder found in user-data, nothing to replace! Without bootstrap token , node won't join.")
}
Expand All @@ -81,6 +74,34 @@ func (c *controller) addBootstrapTokenToUserData(ctx context.Context, machine *v
return nil
}

func (c *controller) addMachineNameToUserData(machine *v1alpha1.Machine, secret *corev1.Secret) error {
var (
userDataB []byte
userDataS string
exists bool
)

if userDataB, exists = secret.Data["userData"]; !exists {
// If userData key is not founds
return fmt.Errorf("userdata field not found in secret for machine %q", machine.Name)
}
userDataS = string(userDataB)

if strings.Contains(userDataS, machineNamePlaceholder) {
klog.V(4).Infof("replacing placeholder %s with %s in user-data!", machineNamePlaceholder, machine.Name)
userDataS = strings.ReplaceAll(userDataS, machineNamePlaceholder, machine.Name)
} else if strings.Contains(userDataS, urlEncodedMachineNamePlaceholder) {
klog.V(4).Infof("replacing url encoded placeholder %s with %s in user-data!", urlEncodedMachineNamePlaceholder, url.QueryEscape(machine.Name))
userDataS = strings.ReplaceAll(userDataS, urlEncodedMachineNamePlaceholder, url.QueryEscape(machine.Name))
} else {
klog.Info("no machine name placeholder found in user-data, nothing to replace! Without machine name , node won't join if node-agent-authorizer is enabled.")
}

secret.Data["userData"] = []byte(userDataS)

return nil
}

func (c *controller) getBootstrapTokenOrCreateIfNotExist(ctx context.Context, machine *v1alpha1.Machine) (secret *corev1.Secret, err error) {
tokenID, secretName := getTokenIDAndSecretName(machine.Name)

Expand All @@ -92,7 +113,7 @@ func (c *controller) getBootstrapTokenOrCreateIfNotExist(ctx context.Context, ma
return nil, err
}
data := map[string][]byte{
bootstraptokenapi.BootstrapTokenDescriptionKey: []byte("A bootstrap token generated by MachineControllManager."),
bootstraptokenapi.BootstrapTokenDescriptionKey: []byte("A bootstrap token generated by MachineControllerManager."),
bootstraptokenapi.BootstrapTokenIDKey: []byte(tokenID),
bootstraptokenapi.BootstrapTokenSecretKey: []byte(bootstrapTokenSecretKey),
bootstraptokenapi.BootstrapTokenExpirationKey: []byte(metav1.Now().Add(c.getEffectiveCreationTimeout(machine).Duration).Format(time.RFC3339)),
Expand Down Expand Up @@ -132,9 +153,9 @@ func (c *controller) deleteBootstrapToken(ctx context.Context, machineName strin
// The set of allowed characters can be specified. Returns error if there was a problem during the random generation.
func generateRandomStringFromCharset(n int, allowedCharacters string) (string, error) {
output := make([]byte, n)
max := new(big.Int).SetInt64(int64(len(allowedCharacters)))
maximum := new(big.Int).SetInt64(int64(len(allowedCharacters)))
for i := range output {
randomCharacter, err := rand.Int(rand.Reader, max)
randomCharacter, err := rand.Int(rand.Reader, maximum)
if err != nil {
return "", err
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/util/provider/machineutils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ const (
// TaintNodeCriticalComponentsNotReady is the name of a gardener taint
// indicating that a node is not yet ready to have user workload scheduled
TaintNodeCriticalComponentsNotReady = "node.gardener.cloud/critical-components-not-ready"

// MachineLabelKey defines the labels which contains the name of the machine of a node
MachineLabelKey = "node.gardener.cloud/machine-name"
)

// RetryPeriod is an alias for specifying the retry period
Expand Down