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

Add support for setting kubelet root directory #5186

Merged
merged 3 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions cmd/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Flags:
--k0s-cloud-provider-update-frequency duration the frequency of k0s-cloud-provider node updates (default 2m0s)
--kube-controller-manager-extra-args string extra args for kube-controller-manager
--kubelet-extra-args string extra args for kubelet
--kubelet-root-dir string Kubelet root directory for k0s
--labels strings Node labels, list of key=value pairs
-l, --logging stringToString Logging Levels for the different components (default [containerd=info,etcd=info,konnectivity-server=1,kube-apiserver=1,kube-controller-manager=1,kube-scheduler=1,kubelet=1])
--no-taints disable default taints for controller node
Expand Down
1 change: 1 addition & 0 deletions cmd/install/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Flags:
--k0s-cloud-provider-update-frequency duration the frequency of k0s-cloud-provider node updates (default 2m0s)
--kube-controller-manager-extra-args string extra args for kube-controller-manager
--kubelet-extra-args string extra args for kubelet
--kubelet-root-dir string Kubelet root directory for k0s
--labels strings Node labels, list of key=value pairs
-l, --logging stringToString Logging Levels for the different components (default [containerd=info,etcd=info,konnectivity-server=1,kube-apiserver=1,kube-controller-manager=1,kube-scheduler=1,kubelet=1])
--no-taints disable default taints for controller node
Expand Down
2 changes: 1 addition & 1 deletion cmd/install/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func cmdFlagsToArgs(cmd *cobra.Command) ([]string, error) {
switch f.Name {
case "env", "force":
return
case "data-dir", "token-file", "config":
case "data-dir", "kubelet-root-dir", "token-file", "config":
if absVal, err := filepath.Abs(val); err != nil {
err = fmt.Errorf("failed to convert --%s=%s to an absolute path: %w", f.Name, val, err)
errs = append(errs, err)
Expand Down
4 changes: 2 additions & 2 deletions inttest/kubeletcertrotate/kubeletcertrotate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (s *kubeletCertRotateSuite) SetupTest() {
s.Require().NoError(err)

// Start the workers using the join token
s.Require().NoError(s.RunWorkersWithToken(workerJoinToken))
s.Require().NoError(s.RunWorkersWithToken(workerJoinToken, "--kubelet-root-dir=/var/lib/kubelet"))

client, err := s.KubeClient(s.ControllerNode(0))
s.Require().NoError(err)
Expand All @@ -74,7 +74,7 @@ func (s *kubeletCertRotateSuite) SetupTest() {
workerSSH, err := s.SSH(s.Context(), s.WorkerNode(0))
s.Require().NoError(err)
s.T().Log("waiting to see kubelet rotating the client cert before triggering Plan creation")
_, err = workerSSH.ExecWithOutput(s.Context(), "inotifywait --no-dereference /var/lib/k0s/kubelet/pki/kubelet-client-current.pem")
_, err = workerSSH.ExecWithOutput(s.Context(), "inotifywait --no-dereference /var/lib/kubelet/pki/kubelet-client-current.pem")
s.Require().NoError(err)
output, err := workerSSH.ExecWithOutput(s.Context(), "k0s status -ojson")
s.Require().NoError(err)
Expand Down
6 changes: 4 additions & 2 deletions inttest/reset/reset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (s *suite) TestReset() {

if !s.Run("k0s gets up", func() {
s.Require().NoError(s.InitController(0, "--disable-components=konnectivity-server,metrics-server"))
s.Require().NoError(s.RunWorkers())
s.Require().NoError(s.RunWorkers("--kubelet-root-dir=/var/lib/kubelet"))

kc, err := s.KubeClient(s.ControllerNode(0))
s.Require().NoError(err)
Expand All @@ -59,6 +59,7 @@ func (s *suite) TestReset() {

s.NoError(ssh.Exec(ctx, "test -d /var/lib/k0s", common.SSHStreams{}), "/var/lib/k0s is not a directory")
s.NoError(ssh.Exec(ctx, "test -d /run/k0s", common.SSHStreams{}), "/run/k0s is not a directory")
s.NoError(ssh.Exec(ctx, "test -d /var/lib/kubelet", common.SSHStreams{}), "/var/lib/kubelet is not a directory")

s.NoError(ssh.Exec(ctx, "pidof containerd-shim-runc-v2 >&2", common.SSHStreams{}), "Expected some running containerd shims")
}) {
Expand Down Expand Up @@ -90,7 +91,7 @@ func (s *suite) TestReset() {
defer ssh.Disconnect()

streams, flushStreams := common.TestLogStreams(s.T(), "reset")
err = ssh.Exec(ctx, "k0s reset --debug", streams)
err = ssh.Exec(ctx, "k0s reset --debug --kubelet-root-dir=/var/lib/kubelet", streams)
flushStreams()
s.NoError(err, "k0s reset didn't exit cleanly")

Expand All @@ -104,6 +105,7 @@ func (s *suite) TestReset() {

// /var/lib/k0s is a mount point in the Docker container and can't be deleted, so it must be empty
s.NoError(ssh.Exec(ctx, `x="$(ls -A /var/lib/k0s)" && echo "$x" >&2 && [ -z "$x" ]`, common.SSHStreams{}), "/var/lib/k0s is not empty")
s.NoError(ssh.Exec(ctx, "! test -e /var/lib/kubelet", common.SSHStreams{}), "/var/lib/kubelet still exists")
s.NoError(ssh.Exec(ctx, "! test -e /run/k0s", common.SSHStreams{}), "/run/k0s still exists")
s.NoError(ssh.Exec(ctx, "! pidof containerd-shim-runc-v2 >&2", common.SSHStreams{}), "Expected no running containerd shims")
})
Expand Down
5 changes: 3 additions & 2 deletions pkg/cleanup/cleanup.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ func NewConfig(debug bool, k0sVars *config.CfgVars, criSocketFlag string) (*Conf
},
&services{},
&directories{
dataDir: k0sVars.DataDir,
runDir: k0sVars.RunDir,
dataDir: k0sVars.DataDir,
kubeletRootDir: k0sVars.KubeletRootDir,
runDir: k0sVars.RunDir,
},
&cni{},
}
Expand Down
12 changes: 9 additions & 3 deletions pkg/cleanup/directories.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ import (
)

type directories struct {
dataDir string
runDir string
dataDir string
kubeletRootDir string
runDir string
}

// Name returns the name of the step
Expand Down Expand Up @@ -70,7 +71,7 @@ func (d *directories) Run() error {
dataDirMounted = true
continue
}
if isUnderPath(v.Path, filepath.Join(d.dataDir, "kubelet")) || isUnderPath(v.Path, d.dataDir) {
if isUnderPath(v.Path, d.kubeletRootDir) || isUnderPath(v.Path, d.dataDir) {
logrus.Debugf("%v is mounted! attempting to unmount...", v.Path)
if err = mounter.Unmount(v.Path); err != nil {
// if we fail to unmount, try lazy unmount so
Expand All @@ -84,6 +85,11 @@ func (d *directories) Run() error {
}
}

logrus.Debugf("removing kubelet root dir (%s)", d.kubeletRootDir)
if err := os.RemoveAll(d.kubeletRootDir); err != nil {
return fmt.Errorf("failed to delete k0s kubelet root direcotory: %w", err)
}

if dataDirMounted {
logrus.Debugf("removing the contents of mounted data-dir (%s)", d.dataDir)
} else {
Expand Down
10 changes: 4 additions & 6 deletions pkg/component/worker/kubelet.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ type Kubelet struct {
ExtraArgs string
DualStackEnabled bool

rootDir string
configPath string
supervisor supervisor.Supervisor
}
Expand All @@ -84,10 +83,9 @@ func (k *Kubelet) Init(_ context.Context) error {
}
}

k.rootDir = filepath.Join(k.K0sVars.DataDir, "kubelet")
err := dir.Init(k.rootDir, constant.DataDirMode)
err := dir.Init(k.K0sVars.KubeletRootDir, constant.DataDirMode)
if err != nil {
return fmt.Errorf("failed to create %s: %w", k.rootDir, err)
return fmt.Errorf("failed to create %s: %w", k.K0sVars.KubeletRootDir, err)
}

runDir := filepath.Join(k.K0sVars.RunDir, "kubelet")
Expand Down Expand Up @@ -133,12 +131,12 @@ func (k *Kubelet) Start(ctx context.Context) error {

logrus.Info("Starting kubelet")
args := stringmap.StringMap{
"--root-dir": k.rootDir,
"--root-dir": k.K0sVars.KubeletRootDir,
"--config": k.configPath,
"--kubeconfig": k.Kubeconfig,
"--v": k.LogLevel,
"--runtime-cgroups": "/system.slice/containerd.service",
"--cert-dir": filepath.Join(k.rootDir, "pki"),
"--cert-dir": filepath.Join(k.K0sVars.KubeletRootDir, "pki"),
}

if len(k.Labels) > 0 {
Expand Down
2 changes: 1 addition & 1 deletion pkg/component/worker/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func BootstrapKubeletKubeconfig(ctx context.Context, k0sVars *config.CfgVars, wo
return fmt.Errorf("wrong token type %s, expected type: kubelet-bootstrap", tokenType)
}

certDir := filepath.Join(k0sVars.DataDir, "kubelet", "pki")
certDir := filepath.Join(k0sVars.KubeletRootDir, "pki")
if err := dir.Init(certDir, constant.DataDirMode); err != nil {
return fmt.Errorf("failed to initialize kubelet certificate directory: %w", err)
}
Expand Down
20 changes: 20 additions & 0 deletions pkg/config/cfgvars.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type CfgVars struct {
BinDir string // location for all pki related binaries
CertRootDir string // CertRootDir defines the root location for all pki related artifacts
DataDir string // Data directory containing k0s state
KubeletRootDir string // Root directory for kubelet
EtcdCertDir string // EtcdCertDir contains etcd certificates
EtcdDataDir string // EtcdDataDir contains etcd state
KineSocketPath string // The unix socket path for kine
Expand Down Expand Up @@ -105,6 +106,12 @@ func WithCommand(cmd command) CfgVarOption {
c.DataDir = f
}

if f, err := flags.GetString("kubelet-root-dir"); err == nil && f != "" {
if f, err := filepath.Abs(f); err == nil {
c.KubeletRootDir = f
}
}

if f, err := flags.GetString("config"); err == nil && f != "" {
c.StartupConfigPath = f
}
Expand Down Expand Up @@ -137,6 +144,7 @@ func DefaultCfgVars() *CfgVars {
// NewCfgVars returns a new CfgVars struct.
func NewCfgVars(cobraCmd command, dirs ...string) (*CfgVars, error) {
var dataDir string
var kubeletRootDir string

if len(dirs) > 0 {
dataDir = dirs[0]
Expand All @@ -146,6 +154,9 @@ func NewCfgVars(cobraCmd command, dirs ...string) (*CfgVars, error) {
if val, err := cobraCmd.Flags().GetString("data-dir"); err == nil && val != "" {
dataDir = val
}
if val, err := cobraCmd.Flags().GetString("kubelet-root-dir"); err == nil && val != "" {
kubeletRootDir = val
}
}

if dataDir == "" {
Expand All @@ -158,6 +169,14 @@ func NewCfgVars(cobraCmd command, dirs ...string) (*CfgVars, error) {
return nil, fmt.Errorf("invalid datadir: %w", err)
}

if kubeletRootDir == "" {
kubeletRootDir = filepath.Join(dataDir, "kubelet")
}
kubeletRootDir, err = filepath.Abs(kubeletRootDir)
if err != nil {
return nil, fmt.Errorf("invalid kubeletRootDir: %w", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filepath.Abs can only fail if the process working directory can't be determined, so the wrapped error message is kinda misleading.

Suggested change
return nil, fmt.Errorf("invalid kubeletRootDir: %w", err)
return err

}

var runDir string
if os.Geteuid() == 0 {
runDir = "/run/k0s"
Expand All @@ -180,6 +199,7 @@ func NewCfgVars(cobraCmd command, dirs ...string) (*CfgVars, error) {
OCIBundleDir: filepath.Join(dataDir, "images"),
CertRootDir: certDir,
DataDir: dataDir,
KubeletRootDir: kubeletRootDir,
EtcdCertDir: filepath.Join(certDir, "etcd"),
EtcdDataDir: filepath.Join(dataDir, "etcd"),
KineSocketPath: filepath.Join(runDir, constant.KineSocket),
Expand Down
45 changes: 45 additions & 0 deletions pkg/config/cfgvars_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package config
import (
"bytes"
"io"
"path/filepath"
"reflect"
"testing"

Expand Down Expand Up @@ -62,6 +63,7 @@ func TestWithCommand(t *testing.T) {
fakeFlags := &FakeFlagSet{
values: map[string]any{
"data-dir": "/path/to/data",
"kubelet-root-dir": "/path/to/kubelet",
"config": "/path/to/config",
"status-socket": "/path/to/socket",
"enable-dynamic-config": true,
Expand All @@ -79,8 +81,12 @@ func TestWithCommand(t *testing.T) {
c := &CfgVars{}
WithCommand(fakeCmd)(c)

dir, err := filepath.Abs("/path/to/kubelet")
assert.NoError(t, err)

assert.Same(t, in, c.stdin)
assert.Equal(t, "/path/to/data", c.DataDir)
assert.Equal(t, dir, c.KubeletRootDir)
assert.Equal(t, "/path/to/config", c.StartupConfigPath)
assert.Equal(t, "/path/to/socket", c.StatusSocketPath)
assert.True(t, c.EnableDynamicConfig)
Expand Down Expand Up @@ -149,6 +155,45 @@ func TestNewCfgVars_DataDir(t *testing.T) {
}
}

func TestNewCfgVars_KubeletRootDir(t *testing.T) {
tests := []struct {
name string
fakeCmd command
dirs []string
expected *CfgVars
}{
{
name: "default kubelet root dir",
fakeCmd: &FakeCommand{flagSet: &FakeFlagSet{}},
expected: &CfgVars{KubeletRootDir: filepath.Join(constant.DataDirDefault, "kubelet")},
},
{
name: "default kubelet root dir when datadir set",
fakeCmd: &FakeCommand{
flagSet: &FakeFlagSet{values: map[string]any{"data-dir": "/path/to/data"}},
},
expected: &CfgVars{KubeletRootDir: "/path/to/data/kubelet"},
},
{
name: "custom kubelet root dir",
fakeCmd: &FakeCommand{
flagSet: &FakeFlagSet{values: map[string]any{"kubelet-root-dir": "/path/to/kubelet"}},
},
expected: &CfgVars{KubeletRootDir: "/path/to/kubelet"},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c, err := NewCfgVars(tt.fakeCmd, tt.dirs...)
assert.NoError(t, err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert.NoError(t, err)
require.NoError(t, err)

expected, err := filepath.Abs(tt.expected.KubeletRootDir)
assert.NoError(t, err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert.NoError(t, err)
require.NoError(t, err)

assert.Equal(t, expected, c.KubeletRootDir)
})
}
}

func TestNodeConfig_Default(t *testing.T) {
oldDefaultPath := defaultConfigPath
defer func() { defaultConfigPath = oldDefaultPath }()
Expand Down
1 change: 1 addition & 0 deletions pkg/config/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ func GetPersistentFlagSet() *pflag.FlagSet {
flagset.BoolVarP(&Debug, "debug", "d", false, "Debug logging (default: false)")
flagset.BoolVarP(&Verbose, "verbose", "v", false, "Verbose logging (default: false)")
flagset.String("data-dir", constant.DataDirDefault, "Data Directory for k0s. DO NOT CHANGE for an existing setup, things will break!")
flagset.String("kubelet-root-dir", "", "Kubelet root directory for k0s")
flagset.StringVar(&StatusSocket, "status-socket", "", "Full file path to the socket file. (default: <rundir>/status.sock)")
flagset.StringVar(&DebugListenOn, "debugListenOn", ":6060", "Http listenOn for Debug pprof handler")
return flagset
Expand Down
Loading