@@ -4,14 +4,15 @@ import (
44 "bytes"
55 "context"
66 "io"
7+ "io/fs"
78 "os"
89 "os/exec"
910 "path/filepath"
1011 "strconv"
1112 "strings"
1213
1314 "github.com/pkg/errors"
14- "github.com/rancher/wrangler/pkg/resolvehome"
15+ "github.com/rancher/wrangler/v3/ pkg/resolvehome"
1516 "github.com/sirupsen/logrus"
1617 "github.com/spf13/pflag"
1718 "github.com/urfave/cli"
@@ -29,6 +30,10 @@ var criDefaultConfigPath = "/etc/crictl.yaml"
2930
3031// main entrypoint for the k8e multicall binary
3132func main () {
33+ if findDebug (os .Args ) {
34+ logrus .SetLevel (logrus .DebugLevel )
35+ }
36+
3237 dataDir := findDataDir (os .Args )
3338
3439 // Handle direct invocation via symlink alias (multicall binary behavior)
@@ -51,33 +56,32 @@ func main() {
5156 cmds .NewCRICTL (externalCLIAction ("crictl" , dataDir )),
5257 cmds .NewCtrCommand (externalCLIAction ("ctr" , dataDir )),
5358 cmds .NewCheckConfigCommand (externalCLIAction ("check-config" , dataDir )),
54- cmds .NewInitOSConfigCommand (externalCLIAction ("init-os-config" , dataDir )),
5559 cmds .NewTokenCommands (
5660 tokenCommand ,
5761 tokenCommand ,
5862 tokenCommand ,
5963 tokenCommand ,
6064 tokenCommand ,
6165 ),
62- cmds .NewEtcdSnapshotCommand (etcdsnapshotCommand ,
63- cmds .NewEtcdSnapshotSubcommands (
64- etcdsnapshotCommand ,
65- etcdsnapshotCommand ,
66- etcdsnapshotCommand ,
67- etcdsnapshotCommand ),
66+ cmds .NewEtcdSnapshotCommands (
67+ etcdsnapshotCommand ,
68+ etcdsnapshotCommand ,
69+ etcdsnapshotCommand ,
70+ etcdsnapshotCommand ,
6871 ),
69- cmds .NewSecretsEncryptCommand ( secretsencryptCommand ,
70- cmds . NewSecretsEncryptSubcommands (
71- secretsencryptCommand ,
72- secretsencryptCommand ,
73- secretsencryptCommand ,
74- secretsencryptCommand ,
75- secretsencryptCommand ,
76- secretsencryptCommand ) ,
72+ cmds .NewSecretsEncryptCommands (
73+ secretsencryptCommand ,
74+ secretsencryptCommand ,
75+ secretsencryptCommand ,
76+ secretsencryptCommand ,
77+ secretsencryptCommand ,
78+ secretsencryptCommand ,
79+ secretsencryptCommand ,
7780 ),
7881 cmds .NewCertCommands (
7982 certCommand ,
8083 certCommand ,
84+ certCommand ,
8185 ),
8286 cmds .NewCompletionCommand (internalCLIAction (version .Program + "-completion" , dataDir , os .Args )),
8387 }
@@ -87,11 +91,32 @@ func main() {
8791 }
8892}
8993
90- // findDataDir reads data-dir settings from the CLI args and config file.
94+ // findDebug reads debug settings from the environment, CLI args, and config file.
95+ func findDebug (args []string ) bool {
96+ debug , _ := strconv .ParseBool (os .Getenv (version .ProgramUpper + "_DEBUG" ))
97+ if debug {
98+ return debug
99+ }
100+ fs := pflag .NewFlagSet ("debug-set" , pflag .ContinueOnError )
101+ fs .ParseErrorsWhitelist .UnknownFlags = true
102+ fs .SetOutput (io .Discard )
103+ fs .BoolVarP (& debug , "debug" , "" , false , "(logging) Turn on debug logs" )
104+ fs .Parse (args )
105+ if debug {
106+ return debug
107+ }
108+ debug , _ = strconv .ParseBool (configfilearg .MustFindString (args , "debug" ))
109+ return debug
110+ }
111+
112+ // findDataDir reads data-dir settings from the environment, CLI args, and config file.
91113// If not found, the default will be used, which varies depending on whether
92114// k8e is being run as root or not.
93115func findDataDir (args []string ) string {
94- var dataDir string
116+ dataDir := os .Getenv (version .ProgramUpper + "_DATA_DIR" )
117+ if dataDir != "" {
118+ return dataDir
119+ }
95120 fs := pflag .NewFlagSet ("data-dir-set" , pflag .ContinueOnError )
96121 fs .ParseErrorsWhitelist .UnknownFlags = true
97122 fs .SetOutput (io .Discard )
@@ -161,7 +186,7 @@ func externalCLI(cli, dataDir string, args []string) error {
161186 return stageAndRun (dataDir , cli , append ([]string {cli }, args ... ), false )
162187}
163188
164- // internalCLIAction returns a function that will call a k8e internal command, be used as the Action of a cli.Command.
189+ // internalCLIAction returns a function that will call a K8e internal command, be used as the Action of a cli.Command.
165190func internalCLIAction (cmd , dataDir string , args []string ) func (ctx * cli.Context ) error {
166191 return func (ctx * cli.Context ) error {
167192 // We don't want the Info logs seen when printing the autocomplete script
@@ -185,16 +210,24 @@ func stageAndRun(dataDir, cmd string, args []string, calledAsInternal bool) erro
185210 }
186211 logrus .Debugf ("Asset dir %s" , dir )
187212
188- var pathEnv string
213+ pathList := []string {
214+ filepath .Clean (filepath .Join (dir , ".." , "cni" )),
215+ filepath .Join (dir , "bin" ),
216+ }
189217 if findPreferBundledBin (args ) {
190- pathEnv = filepath .Join (dir , "bin" ) + string (os .PathListSeparator ) + filepath .Join (dir , "bin/aux" ) + string (os .PathListSeparator ) + os .Getenv ("PATH" )
218+ pathList = append (
219+ pathList ,
220+ filepath .Join (dir , "bin" , "aux" ),
221+ os .Getenv ("PATH" ),
222+ )
191223 } else {
192- pathEnv = filepath .Join (dir , "bin" ) + string (os .PathListSeparator ) + os .Getenv ("PATH" ) + string (os .PathListSeparator ) + filepath .Join (dir , "bin/aux" )
193- }
194- if err := os .Setenv ("PATH" , pathEnv ); err != nil {
195- return err
224+ pathList = append (
225+ pathList ,
226+ os .Getenv ("PATH" ),
227+ filepath .Join (dir , "bin" , "aux" ),
228+ )
196229 }
197- if err := os .Setenv (version . ProgramUpper + "_DATA_DIR " , dir ); err != nil {
230+ if err := os .Setenv ("PATH " , strings . Join ( pathList , string ( os . PathListSeparator )) ); err != nil {
198231 return err
199232 }
200233
@@ -269,6 +302,53 @@ func extract(dataDir string) (string, error) {
269302 return "" , err
270303 }
271304
305+ // Create a stable CNI bin dir and place it first in the path so that users have a
306+ // consistent location to drop their own CNI plugin binaries.
307+ cniPath := filepath .Join (dataDir , "data" , "cni" )
308+ cniBin := filepath .Join (dir , "bin" , "cni" )
309+ if err := os .MkdirAll (cniPath , 0755 ); err != nil {
310+ return "" , err
311+ }
312+ // Create symlink that points at the cni multicall binary itself
313+ logrus .Debugf ("Creating symlink %s -> %s" , filepath .Join (cniPath , "cni" ), cniBin )
314+ os .Remove (filepath .Join (cniPath , "cni" ))
315+ if err := os .Symlink (cniBin , filepath .Join (cniPath , "cni" )); err != nil {
316+ return "" , err
317+ }
318+
319+ // Find symlinks that point to the cni multicall binary, and clone them in the stable CNI bin dir.
320+ // Non-symlink plugins in the stable CNI bin dir will not be overwritten, to allow users to replace our
321+ // CNI plugins with their own versions if they want. Note that the cni multicall binary itself is always
322+ // symlinked into the stable bin dir and should not be replaced.
323+ ents , err := os .ReadDir (filepath .Join (tempDest , "bin" ))
324+ if err != nil {
325+ return "" , err
326+ }
327+ for _ , ent := range ents {
328+ if info , err := ent .Info (); err == nil && info .Mode ()& fs .ModeSymlink != 0 {
329+ if target , err := os .Readlink (filepath .Join (tempDest , "bin" , ent .Name ())); err == nil && target == "cni" {
330+ src := filepath .Join (cniPath , ent .Name ())
331+ // Check if plugin already exists in stable CNI bin dir
332+ if info , err := os .Lstat (src ); err == nil {
333+ if info .Mode ()& fs .ModeSymlink != 0 {
334+ // Exists and is a symlink, remove it so we can create a new symlink for the new bin.
335+ os .Remove (src )
336+ } else {
337+ // Not a symlink, leave it alone
338+ logrus .Debugf ("Not replacing non-symlink CNI plugin %s with mode %O" , src , info .Mode ())
339+ continue
340+ }
341+ }
342+ logrus .Debugf ("Creating symlink %s -> %s" , src , cniBin )
343+ if err := os .Symlink (cniBin , src ); err != nil {
344+ return "" , err
345+ }
346+ }
347+ }
348+ }
349+
350+ // Rotate 'current' symlink into 'previous', and create a new 'current' that points
351+ // at the new directory.
272352 currentSymLink := filepath .Join (dataDir , "data" , "current" )
273353 previousSymLink := filepath .Join (dataDir , "data" , "previous" )
274354 if _ , err := os .Lstat (currentSymLink ); err == nil {
@@ -279,7 +359,14 @@ func extract(dataDir string) (string, error) {
279359 if err := os .Symlink (dir , currentSymLink ); err != nil {
280360 return "" , err
281361 }
282- return dir , os .Rename (tempDest , dir )
362+
363+ // Rename the new directory into place after updating symlinks, so that the k8e binary check at the start
364+ // of this function only succeeds if everything else has been completed successfully.
365+ if err := os .Rename (tempDest , dir ); err != nil {
366+ return "" , err
367+ }
368+
369+ return dir , nil
283370}
284371
285372// findCriConfig returns the path to crictl.yaml
0 commit comments