talos/internal/integration/api/update-hostname.go
Utku Ozdemir bb4abc0961
fix: regenerate kubelet certs when hostname changes
Clear the kubelet certificates and kubeconfig when hostname changes so that on next start, kubelet goes through the bootstrap process and new certificates are generated and the node is joined to the cluster with the new name.

Fixes siderolabs/talos#5834.

Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
2022-07-21 01:54:15 +02:00

151 lines
4.4 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//go:build integration_api
// +build integration_api
package api
import (
"context"
"fmt"
"strings"
"testing"
"time"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/talos-systems/talos/internal/integration/base"
machineapi "github.com/talos-systems/talos/pkg/machinery/api/machine"
"github.com/talos-systems/talos/pkg/machinery/client"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1/machine"
)
// UpdateHostnameSuite verifies UpdateHostname API.
type UpdateHostnameSuite struct {
base.K8sSuite
ctx context.Context //nolint:containedctx
ctxCancel context.CancelFunc
}
// SuiteName ...
func (suite *UpdateHostnameSuite) SuiteName() string {
return "api.UpdateHostnameSuite"
}
// SetupTest ...
func (suite *UpdateHostnameSuite) SetupTest() {
// make sure API calls have timeout
suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 5*time.Minute)
}
// TearDownTest ...
func (suite *UpdateHostnameSuite) TearDownTest() {
if suite.ctxCancel != nil {
suite.ctxCancel()
}
}
// TestUpdateHostname updates the hostname of a worker node,
// then asserts that the node re-joins the cluster with the new hostname.
// It reverts the change at the end of the test and asserts that the node is reported again as Ready.
func (suite *UpdateHostnameSuite) TestUpdateHostname() {
if testing.Short() {
suite.T().Skip("skipping in short mode")
}
if !suite.Capabilities().SupportsReboot {
suite.T().Skip("cluster doesn't support reboot")
}
nodeInternalIP := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker)
nodeCtx := client.WithNodes(suite.ctx, nodeInternalIP)
node, err := suite.GetK8sNodeByInternalIP(suite.ctx, nodeInternalIP)
suite.Require().NoError(err)
if strings.HasSuffix(node.Name, ".ec2.internal") {
suite.T().Skip("aws does not support hostname changes")
}
oldHostname := node.Name
newHostname := "test-update-hostname"
err = suite.updateHostname(nodeCtx, nodeInternalIP, newHostname)
suite.Require().NoError(err)
nodeReady := func(status corev1.ConditionStatus) bool {
return status == corev1.ConditionTrue
}
nodeNotReady := func(status corev1.ConditionStatus) bool {
return status != corev1.ConditionTrue
}
defer func() {
// revert the hostname back to the original one
err = suite.updateHostname(nodeCtx, nodeInternalIP, oldHostname)
suite.Require().NoError(err)
// expect node status to be Ready again
suite.Assert().NoError(suite.WaitForK8sNodeReadinessStatus(suite.ctx, oldHostname, nodeReady))
// Delete the node with the test hostname
err = suite.Clientset.CoreV1().Nodes().Delete(suite.ctx, newHostname, metav1.DeleteOptions{})
suite.Require().NoError(err)
// Reboot node for CNI bridge to be reconfigured: https://stackoverflow.com/questions/61373366
suite.AssertRebooted(
suite.ctx, nodeInternalIP, func(nodeCtx context.Context) error {
return base.IgnoreGRPCUnavailable(suite.Client.Reboot(nodeCtx))
}, 10*time.Minute,
)
}()
// expect node with old hostname to become NotReady
suite.Assert().NoError(suite.WaitForK8sNodeReadinessStatus(suite.ctx, oldHostname, nodeNotReady))
// expect node with new hostname to become Ready
suite.Assert().NoError(suite.WaitForK8sNodeReadinessStatus(suite.ctx, newHostname, nodeReady))
// Delete the node with the old hostname
err = suite.Clientset.CoreV1().Nodes().Delete(suite.ctx, oldHostname, metav1.DeleteOptions{})
suite.Require().NoError(err)
}
func (suite *UpdateHostnameSuite) updateHostname(nodeCtx context.Context, nodeIP string, newHostname string) error {
nodeConfig, err := suite.ReadConfigFromNode(nodeCtx)
if err != nil {
return err
}
nodeConfigRaw, ok := nodeConfig.Raw().(*v1alpha1.Config)
if !ok {
return fmt.Errorf("unexpected node config type %T", nodeConfig.Raw())
}
nodeConfigRaw.MachineConfig.MachineNetwork.NetworkHostname = newHostname
bytes, err := nodeConfigRaw.Bytes()
if err != nil {
return err
}
_, err = suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{
Data: bytes,
Mode: machineapi.ApplyConfigurationRequest_NO_REBOOT,
})
return err
}
func init() {
allSuites = append(allSuites, new(UpdateHostnameSuite))
}