@@ -22,9 +22,11 @@ import (
22
22
"fmt"
23
23
"os"
24
24
"os/signal"
25
+ "path/filepath"
25
26
"runtime"
26
27
"syscall"
27
28
29
+ "github.com/k0sproject/k0s/internal/pkg/file"
28
30
"github.com/k0sproject/k0s/internal/pkg/flags"
29
31
internallog "github.com/k0sproject/k0s/internal/pkg/log"
30
32
"github.com/k0sproject/k0s/internal/pkg/stringmap"
@@ -39,8 +41,11 @@ import (
39
41
"github.com/k0sproject/k0s/pkg/config"
40
42
"github.com/k0sproject/k0s/pkg/kubernetes"
41
43
"github.com/k0sproject/k0s/pkg/node"
44
+ "github.com/k0sproject/k0s/pkg/token"
42
45
43
46
apitypes "k8s.io/apimachinery/pkg/types"
47
+ "k8s.io/client-go/tools/clientcmd"
48
+ clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
44
49
45
50
"github.com/sirupsen/logrus"
46
51
"github.com/spf13/cobra"
@@ -83,8 +88,9 @@ func NewWorkerCmd() *cobra.Command {
83
88
c .TokenArg = args [0 ]
84
89
}
85
90
86
- if c .TokenArg != "" && c .TokenFile != "" {
87
- return 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]'" )
91
+ getBootstrapKubeconfig , err := kubeconfigGetterFromJoinToken (c .TokenFile , c .TokenArg )
92
+ if err != nil {
93
+ return err
88
94
}
89
95
90
96
nodeName , kubeletExtraArgs , err := GetNodeName (& c .WorkerOptions )
@@ -104,7 +110,14 @@ func NewWorkerCmd() *cobra.Command {
104
110
ctx , cancel := signal .NotifyContext (cmd .Context (), os .Interrupt , syscall .SIGINT , syscall .SIGTERM )
105
111
defer cancel ()
106
112
107
- return c .Start (ctx , nodeName , kubeletExtraArgs , nil )
113
+ // Check for legacy CA file (unused on worker-only nodes since 1.33)
114
+ if legacyCAFile := filepath .Join (c .K0sVars .CertRootDir , "ca.crt" ); file .Exists (legacyCAFile ) {
115
+ // Keep the file to allow interop between 1.32 and 1.33.
116
+ // TODO automatically delete this file in future releases.
117
+ logrus .Infof ("The file %s is no longer used and can safely be deleted" , legacyCAFile )
118
+ }
119
+
120
+ return c .Start (ctx , nodeName , kubeletExtraArgs , getBootstrapKubeconfig , nil )
108
121
},
109
122
}
110
123
@@ -136,10 +149,73 @@ func GetNodeName(opts *config.WorkerOptions) (apitypes.NodeName, stringmap.Strin
136
149
return nodeName , kubeletExtraArgs , nil
137
150
}
138
151
152
+ func kubeconfigGetterFromJoinToken (tokenFile , tokenArg string ) (clientcmd.KubeconfigGetter , error ) {
153
+ if tokenArg != "" {
154
+ if tokenFile != "" {
155
+ 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]'" )
156
+ }
157
+
158
+ kubeconfig , err := loadKubeconfigFromJoinToken (tokenArg )
159
+ if err != nil {
160
+ return nil , err
161
+ }
162
+
163
+ return func () (* clientcmdapi.Config , error ) {
164
+ return kubeconfig , nil
165
+ }, nil
166
+ }
167
+
168
+ if tokenFile == "" {
169
+ return nil , nil
170
+ }
171
+
172
+ return func () (* clientcmdapi.Config , error ) {
173
+ return loadKubeconfigFromTokenFile (tokenFile )
174
+ }, nil
175
+ }
176
+
177
+ func loadKubeconfigFromJoinToken (tokenData string ) (* clientcmdapi.Config , error ) {
178
+ decoded , err := token .DecodeJoinToken (tokenData )
179
+ if err != nil {
180
+ return nil , fmt .Errorf ("failed to decode join token: %w" , err )
181
+ }
182
+
183
+ kubeconfig , err := clientcmd .Load (decoded )
184
+ if err != nil {
185
+ return nil , fmt .Errorf ("failed to load kubeconfig from join token: %w" , err )
186
+ }
187
+
188
+ if tokenType := token .GetTokenType (kubeconfig ); tokenType != "kubelet-bootstrap" {
189
+ return nil , fmt .Errorf ("wrong token type %s, expected type: kubelet-bootstrap" , tokenType )
190
+ }
191
+
192
+ return kubeconfig , nil
193
+ }
194
+
195
+ func loadKubeconfigFromTokenFile (path string ) (* clientcmdapi.Config , error ) {
196
+ var problem string
197
+ tokenBytes , err := os .ReadFile (path )
198
+ if errors .Is (err , os .ErrNotExist ) {
199
+ problem = "not found"
200
+ } else if err != nil {
201
+ return nil , fmt .Errorf ("failed to read token file: %w" , err )
202
+ } else if len (tokenBytes ) == 0 {
203
+ problem = "is empty"
204
+ }
205
+ if problem != "" {
206
+ return nil , fmt .Errorf ("token file %q %s" +
207
+ `: obtain a new token via "k0s token create ..." and store it in the file` +
208
+ ` or reinstall this node via "k0s install --force ..." or "k0sctl apply --force ..."` ,
209
+ path , problem )
210
+ }
211
+
212
+ return loadKubeconfigFromJoinToken (string (tokenBytes ))
213
+ }
214
+
139
215
// Start starts the worker components based on the given [config.CLIOptions].
140
- func (c * Command ) Start (ctx context.Context , nodeName apitypes.NodeName , kubeletExtraArgs stringmap.StringMap , controller EmbeddingController ) error {
141
- if err := worker .BootstrapKubeletKubeconfig (ctx , c .K0sVars , nodeName , & c .WorkerOptions ); err != nil {
142
- return err
216
+ func (c * Command ) Start (ctx context.Context , nodeName apitypes.NodeName , kubeletExtraArgs stringmap.StringMap , getBootstrapKubeconfig clientcmd. KubeconfigGetter , controller EmbeddingController ) error {
217
+ if err := worker .BootstrapKubeletClientConfig (ctx , c .K0sVars , nodeName , & c .WorkerOptions , getBootstrapKubeconfig ); err != nil {
218
+ return fmt . Errorf ( "failed to bootstrap kubelet client configuration: %w" , err )
143
219
}
144
220
145
221
kubeletKubeconfigPath := c .K0sVars .KubeletAuthConfigPath
0 commit comments