Skip to content

Commit 716b3d0

Browse files
committed
Add --start and --token-env flags to install command
Signed-off-by: s3rj1k <[email protected]>
1 parent 98921ee commit 716b3d0

File tree

10 files changed

+154
-9
lines changed

10 files changed

+154
-9
lines changed

cmd/controller/controller.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,31 @@ func (c *command) start(ctx context.Context, flags *config.ControllerOptions, de
182182

183183
var joinClient *token.JoinClient
184184

185-
if (c.TokenArg != "" || c.TokenFile != "") && c.needToJoin(nodeConfig) {
185+
if (c.TokenArg != "" || c.TokenFile != "" || c.TokenEnv != "") && c.needToJoin(nodeConfig) {
186+
tokenSources := 0
187+
if c.TokenArg != "" {
188+
tokenSources++
189+
}
190+
if c.TokenFile != "" {
191+
tokenSources++
192+
}
193+
if c.TokenEnv != "" {
194+
tokenSources++
195+
}
196+
197+
if tokenSources > 1 {
198+
return errors.New("you can only pass one token source: either as a CLI argument 'k0s controller [token]', via '--token-file [path]', or via '--token-env [var]'")
199+
}
200+
186201
var tokenData string
187202
if c.TokenArg != "" {
188203
tokenData = c.TokenArg
204+
} else if c.TokenEnv != "" {
205+
tokenValue := os.Getenv(c.TokenEnv)
206+
if tokenValue == "" {
207+
return fmt.Errorf("environment variable %q is not set or is empty", c.TokenEnv)
208+
}
209+
tokenData = tokenValue
189210
} else {
190211
data, err := os.ReadFile(c.TokenFile)
191212
if err != nil {

cmd/controller/controller_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ Flags:
7575
--single enable single node (implies --enable-worker, default false)
7676
--status-socket string Full file path to the socket file. (default: <rundir>/status.sock)
7777
--taints strings Node taints, list of key=value:effect strings
78+
--token-env string Environment variable name containing the join-token.
7879
--token-file string Path to the file containing join-token.
7980
-v, --verbose Verbose logging (default true)
8081
`, out.String())

cmd/install/controller.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,21 @@ With the controller subcommand you can setup a single node cluster by running:
4949
return fmt.Errorf("invalid node config: %w", errors.Join(errs...))
5050
}
5151

52+
// Convert --token-env to --token-file
53+
tokenFilePath, err := handleTokenEnv(cmd, k0sVars.DataDir)
54+
if err != nil {
55+
return err
56+
}
57+
5258
flagsAndVals, err := cmdFlagsToArgs(cmd)
5359
if err != nil {
5460
return err
5561
}
5662

63+
if tokenFilePath != "" {
64+
flagsAndVals = append(flagsAndVals, "--token-file="+tokenFilePath)
65+
}
66+
5767
systemUsers := nodeConfig.Spec.Install.SystemUsers
5868
homeDir := k0sVars.DataDir
5969
if err := install.EnsureControllerUsers(systemUsers, homeDir); err != nil {
@@ -65,6 +75,12 @@ With the controller subcommand you can setup a single node cluster by running:
6575
return fmt.Errorf("failed to install controller service: %w", err)
6676
}
6777

78+
if installFlags.start {
79+
if err := startInstalledService(installFlags.force); err != nil {
80+
return fmt.Errorf("failed to start controller service: %w", err)
81+
}
82+
}
83+
6884
return nil
6985
},
7086
}

cmd/install/controller_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,15 @@ Flags:
6868
--single enable single node (implies --enable-worker, default false)
6969
--status-socket string Full file path to the socket file. (default: <rundir>/status.sock)
7070
--taints strings Node taints, list of key=value:effect strings
71+
--token-env string Environment variable name containing the join-token.
7172
--token-file string Path to the file containing join-token.
7273
7374
Global Flags:
7475
-d, --debug Debug logging (implies verbose logging)
7576
--debugListenOn string Http listenOn for Debug pprof handler (default ":6060")
7677
-e, --env stringArray set environment variable
7778
--force force init script creation
79+
--start start the service immediately after installation
7880
-v, --verbose Verbose logging
7981
`, out.String())
8082
}

cmd/install/install.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ package install
66
import (
77
"github.com/k0sproject/k0s/cmd/internal"
88
"github.com/k0sproject/k0s/pkg/config"
9+
"github.com/k0sproject/k0s/pkg/install"
10+
911
"github.com/spf13/cobra"
1012
"github.com/spf13/pflag"
1113
)
1214

1315
type installFlags struct {
1416
force bool
1517
envVars []string
18+
start bool
1619
}
1720

1821
func NewInstallCmd() *cobra.Command {
@@ -38,9 +41,15 @@ func NewInstallCmd() *cobra.Command {
3841
})
3942
pflags.BoolVar(&installFlags.force, "force", false, "force init script creation")
4043
pflags.StringArrayVarP(&installFlags.envVars, "env", "e", nil, "set environment variable")
44+
pflags.BoolVar(&installFlags.start, "start", false, "start the service immediately after installation")
4145

4246
cmd.AddCommand(installWorkerCmd(&installFlags))
4347
addPlatformSpecificCommands(cmd, &installFlags)
4448

4549
return cmd
4650
}
51+
52+
// startInstalledService starts (or restarts with force) the installed k0s service.
53+
func startInstalledService(force bool) error {
54+
return install.StartInstalledService(force)
55+
}

cmd/install/util.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package install
66
import (
77
"errors"
88
"fmt"
9+
"os"
910
"path/filepath"
1011
"strings"
1112

@@ -24,7 +25,7 @@ func cmdFlagsToArgs(cmd *cobra.Command) ([]string, error) {
2425
flagsAndVals = append(flagsAndVals, fmt.Sprintf(`--%s=%s`, f.Name, strings.Trim(val, "[]")))
2526
default:
2627
switch f.Name {
27-
case "env", "force":
28+
case "env", "force", "start", "token-env":
2829
return
2930
case "data-dir", "kubelet-root-dir", "token-file", "config":
3031
if absVal, err := filepath.Abs(val); err != nil {
@@ -44,3 +45,28 @@ func cmdFlagsToArgs(cmd *cobra.Command) ([]string, error) {
4445

4546
return flagsAndVals, nil
4647
}
48+
49+
// handleTokenEnv converts --token-env to a token file and returns its path.
50+
func handleTokenEnv(cmd *cobra.Command, dataDir string) (string, error) {
51+
tokenEnvFlag := cmd.Flags().Lookup("token-env")
52+
if tokenEnvFlag == nil || !tokenEnvFlag.Changed {
53+
return "", nil
54+
}
55+
56+
envVarName := tokenEnvFlag.Value.String()
57+
tokenValue := os.Getenv(envVarName)
58+
if tokenValue == "" {
59+
return "", fmt.Errorf("environment variable %q is not set or is empty", envVarName)
60+
}
61+
62+
tokenFilePath := filepath.Join(dataDir, ".token")
63+
if err := os.MkdirAll(dataDir, 0755); err != nil {
64+
return "", fmt.Errorf("failed to create data directory: %w", err)
65+
}
66+
67+
if err := os.WriteFile(tokenFilePath, []byte(tokenValue), 0600); err != nil {
68+
return "", fmt.Errorf("failed to write token file: %w", err)
69+
}
70+
71+
return tokenFilePath, nil
72+
}

cmd/install/worker.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,37 @@ All default values of worker command will be passed to the service stub unless o
2727
return errors.New("this command must be run as root")
2828
}
2929

30+
k0sVars, err := config.NewCfgVars(cmd)
31+
if err != nil {
32+
return fmt.Errorf("failed to initialize configuration variables: %w", err)
33+
}
34+
35+
// Convert --token-env to --token-file
36+
tokenFilePath, err := handleTokenEnv(cmd, k0sVars.DataDir)
37+
if err != nil {
38+
return err
39+
}
40+
3041
flagsAndVals, err := cmdFlagsToArgs(cmd)
3142
if err != nil {
3243
return err
3344
}
3445

46+
if tokenFilePath != "" {
47+
flagsAndVals = append(flagsAndVals, "--token-file="+tokenFilePath)
48+
}
49+
3550
args := append([]string{"worker"}, flagsAndVals...)
3651
if err := install.InstallService(args, installFlags.envVars, installFlags.force); err != nil {
3752
return fmt.Errorf("failed to install worker service: %w", err)
3853
}
3954

55+
if installFlags.start {
56+
if err := startInstalledService(installFlags.force); err != nil {
57+
return fmt.Errorf("failed to start worker service: %w", err)
58+
}
59+
}
60+
4061
return nil
4162
},
4263
}

cmd/worker/worker.go

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func NewWorkerCmd() *cobra.Command {
8080
c.TokenArg = args[0]
8181
}
8282

83-
getBootstrapKubeconfig, err := kubeconfigGetterFromJoinToken(c.TokenFile, c.TokenArg)
83+
getBootstrapKubeconfig, err := kubeconfigGetterFromJoinToken(c.TokenFile, c.TokenEnv, c.TokenArg)
8484
if err != nil {
8585
return err
8686
}
@@ -150,12 +150,23 @@ func GetNodeName(opts *config.WorkerOptions) (apitypes.NodeName, stringmap.Strin
150150
return nodeName, kubeletExtraArgs, nil
151151
}
152152

153-
func kubeconfigGetterFromJoinToken(tokenFile, tokenArg string) (clientcmd.KubeconfigGetter, error) {
153+
func kubeconfigGetterFromJoinToken(tokenFile, tokenEnv, tokenArg string) (clientcmd.KubeconfigGetter, error) {
154+
tokenSources := 0
154155
if tokenArg != "" {
155-
if tokenFile != "" {
156-
return nil, errors.New("you can only pass one token argument either as a CLI argument 'k0s worker [token]' or as a flag 'k0s worker --token-file [path]'")
157-
}
156+
tokenSources++
157+
}
158+
if tokenFile != "" {
159+
tokenSources++
160+
}
161+
if tokenEnv != "" {
162+
tokenSources++
163+
}
158164

165+
if tokenSources > 1 {
166+
return nil, errors.New("you can only pass one token source: either as a CLI argument 'k0s worker [token]', via '--token-file [path]', or via '--token-env [var]'")
167+
}
168+
169+
if tokenArg != "" {
159170
kubeconfig, err := loadKubeconfigFromJoinToken(tokenArg)
160171
if err != nil {
161172
return nil, err
@@ -166,12 +177,28 @@ func kubeconfigGetterFromJoinToken(tokenFile, tokenArg string) (clientcmd.Kubeco
166177
}, nil
167178
}
168179

180+
if tokenEnv != "" {
181+
tokenValue := os.Getenv(tokenEnv)
182+
if tokenValue == "" {
183+
return nil, fmt.Errorf("environment variable %q is not set or is empty", tokenEnv)
184+
}
185+
186+
kubeconfig, err := loadKubeconfigFromJoinToken(tokenValue)
187+
if err != nil {
188+
return nil, err
189+
}
190+
191+
return func() (*clientcmdapi.Config, error) {
192+
return kubeconfig, nil
193+
}, nil
194+
}
195+
169196
if tokenFile == "" {
170197
return nil, nil
171198
}
172199

173200
return func() (*clientcmdapi.Config, error) {
174-
return loadKubeconfigFromTokenFile(tokenFile)
201+
return loadKubeconfigFromToken(tokenFile)
175202
}, nil
176203
}
177204

@@ -193,7 +220,7 @@ func loadKubeconfigFromJoinToken(tokenData string) (*clientcmdapi.Config, error)
193220
return kubeconfig, nil
194221
}
195222

196-
func loadKubeconfigFromTokenFile(path string) (*clientcmdapi.Config, error) {
223+
func loadKubeconfigFromToken(path string) (*clientcmdapi.Config, error) {
197224
var problem string
198225
tokenBytes, err := os.ReadFile(path)
199226
if errors.Is(err, os.ErrNotExist) {

pkg/config/cli.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ type WorkerOptions struct {
7070
Labels map[string]string
7171
Taints []string
7272
TokenFile string
73+
TokenEnv string
7374
TokenArg string
7475
WorkerProfile string
7576
IPTablesMode string
@@ -251,6 +252,7 @@ func GetWorkerFlags() *pflag.FlagSet {
251252
flagset.StringVar(&workerOpts.WorkerProfile, "profile", defaultWorkerProfile, "worker profile to use on the node")
252253
flagset.BoolVar(&workerOpts.CloudProvider, "enable-cloud-provider", false, "Whether or not to enable cloud provider support in kubelet")
253254
flagset.StringVar(&workerOpts.TokenFile, "token-file", "", "Path to the file containing join-token.")
255+
flagset.StringVar(&workerOpts.TokenEnv, "token-env", "", "Environment variable name containing the join-token.")
254256
flagset.VarP((*logLevelsFlag)(&workerOpts.LogLevels), "logging", "l", "Logging Levels for the different components")
255257
flagset.Var((*cliflag.ConfigurationMap)(&workerOpts.Labels), "labels", "Node labels, list of key=value pairs")
256258
flagset.StringSliceVarP(&workerOpts.Taints, "taints", "", []string{}, "Node taints, list of key=value:effect strings")

pkg/install/service.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package install
55

66
import (
77
"errors"
8+
"fmt"
89
"runtime"
910

1011
"github.com/kardianos/service"
@@ -122,3 +123,22 @@ func GetServiceConfig(role string) *service.Config {
122123
Description: k0sDescription,
123124
}
124125
}
126+
127+
// StartInstalledService starts (or restarts with force) the installed k0s service.
128+
func StartInstalledService(force bool) error {
129+
svc, err := InstalledService()
130+
if err != nil {
131+
return err
132+
}
133+
status, _ := svc.Status()
134+
if status == service.StatusRunning {
135+
if force {
136+
if err := svc.Restart(); err != nil {
137+
return fmt.Errorf("failed to restart service: %w", err)
138+
}
139+
return nil
140+
}
141+
return errors.New("already running")
142+
}
143+
return svc.Start()
144+
}

0 commit comments

Comments
 (0)