diff --git a/main.go b/main.go index afec5a8985..057c3c215c 100644 --- a/main.go +++ b/main.go @@ -15,8 +15,9 @@ func main() { cmds.NewServerCommand(), cmds.NewAgentCommand(), cmds.NewEtcdSnapshotCommand(), - cmds.NewCertRotateCommand(), + cmds.NewCertCommand(), cmds.NewSecretsEncryptCommand(), + cmds.NewTokenCommand(), } if err := app.Run(configfilearg.MustParse(os.Args)); err != nil { diff --git a/pkg/cli/cmds/cert.go b/pkg/cli/cmds/cert.go index b49033912f..30a0646c0b 100644 --- a/pkg/cli/cmds/cert.go +++ b/pkg/cli/cmds/cert.go @@ -6,44 +6,66 @@ import ( "github.com/k3s-io/k3s/pkg/cli/cert" "github.com/k3s-io/k3s/pkg/cli/cmds" "github.com/k3s-io/k3s/pkg/configfilearg" + "github.com/k3s-io/k3s/pkg/version" "github.com/rancher/rke2/pkg/rke2" "github.com/urfave/cli" ) -var k3sCertFlags = K3SFlagSet{ - "config": copy, - "debug": copy, - "log": copy, - "alsologtostderr": copy, - "service": copy, - "data-dir": { - Usage: "(data) Folder to hold state", - Default: rke2Path, - }, -} - var certSubcommands = []cli.Command{ { Name: "rotate", - Usage: "Certificate Rotation", + Usage: "Rotate " + version.Program + " component certificates on disk", + SkipFlagParsing: false, + SkipArgReorder: true, + Action: Rotate, + Flags: cmds.CertRotateCommandFlags, + }, + { + Name: "rotate-ca", + Usage: "Write updated " + version.Program + " CA certificates to the datastore", SkipFlagParsing: false, SkipArgReorder: true, - Action: CertificateRotationRun, - Flags: cmds.CertCommandFlags, + Action: cert.RotateCA, + Flags: cmds.CertRotateCACommandFlags, }, } -var ( - k3sCertBase = mustCmdFromK3S(cmds.NewCertCommand(certSubcommands), k3sCertFlags) -) +func NewCertCommand() cli.Command { + k3sOpts := K3SFlagSet{} + subCommandOpts := map[string]K3SFlagSet{ + "rotate": { + "alsologtostderr": copy, + "config": copy, + "debug": copy, + "log": copy, + "service": copy, + "data-dir": { + Usage: "(data) Folder to hold state", + Default: rke2Path, + }, + }, + "rotate-ca": { + "server": copy, + "path": copy, + "force": copy, + }, + } -func NewCertRotateCommand() cli.Command { - cmd := k3sCertBase - configfilearg.DefaultParser.ValidFlags[cmd.Name] = cmd.Flags - return cmd + command := cmds.NewCertCommand(certSubcommands) + command.Usage = "Manage RKE2 certificates" + configfilearg.DefaultParser.ValidFlags[command.Name] = command.Flags + for i, subcommand := range command.Subcommands { + if s, ok := subCommandOpts[subcommand.Name]; ok { + k3sOpts.CopyInto(s) + command.Subcommands[i] = mustCmdFromK3S(subcommand, s) + } else { + command.Subcommands[i] = mustCmdFromK3S(subcommand, k3sOpts) + } + } + return mustCmdFromK3S(command, nil) } -func CertificateRotationRun(clx *cli.Context) error { +func Rotate(clx *cli.Context) error { dataDir := clx.String("data-dir") if dataDir == "" { dataDir = rke2Path @@ -51,5 +73,5 @@ func CertificateRotationRun(clx *cli.Context) error { if err := ioutil.WriteFile(rke2.ForceRestartFile(dataDir), []byte{}, 0600); err != nil { return err } - return cert.Run(clx) + return cert.Rotate(clx) } diff --git a/pkg/cli/cmds/cmds_test.go b/pkg/cli/cmds/cmds_test.go index a631558baa..8ec5840e31 100644 --- a/pkg/cli/cmds/cmds_test.go +++ b/pkg/cli/cmds/cmds_test.go @@ -1,15 +1,34 @@ package cmds -import "testing" +import ( + "testing" + + "github.com/urfave/cli" +) // Test_NewCommands confirms that all the top-level commands can be created // successfully without causing any panics in mustCmdFromK3S. Covering this // with a test allows us to catch K3s flag option mismatches in testing, // instead of not noticing until the main command crashes in functional tests. func Test_NewCommands(t *testing.T) { - NewServerCommand() - NewAgentCommand() - NewEtcdSnapshotCommand() - NewCertRotateCommand() - NewSecretsEncryptCommand() + app := cli.NewApp() + app.Name = "rke2" + app.Commands = []cli.Command{ + NewServerCommand(), + NewAgentCommand(), + NewEtcdSnapshotCommand(), + NewCertCommand(), + NewSecretsEncryptCommand(), + NewTokenCommand(), + } + + for _, command := range app.Commands { + t.Logf("Testing command: %s", command.Name) + app.Run([]string{app.Name, command.Name, "--help"}) + + for _, subcommand := range command.Subcommands { + t.Logf("Testing subcommand: %s %s", command.Name, subcommand.Name) + app.Run([]string{app.Name, command.Name, subcommand.Name, "--help"}) + } + } } diff --git a/pkg/cli/cmds/k3sopts.go b/pkg/cli/cmds/k3sopts.go index 6a93303f07..0d50835e75 100644 --- a/pkg/cli/cmds/k3sopts.go +++ b/pkg/cli/cmds/k3sopts.go @@ -104,6 +104,22 @@ func commandFromK3S(cmd cli.Command, flagOpts K3SFlagSet) (cli.Command, error) { strSliceFlag.Hidden = true } flag = strSliceFlag + } else if strSliceFlag, ok := flag.(*cli.StringSliceFlag); ok { + if opt.Usage != "" { + strSliceFlag.Usage = opt.Usage + } + if opt.Default != "" { + slice := &cli.StringSlice{} + parts := strings.Split(opt.Default, ",") + for _, val := range parts { + slice.Set(val) + } + strSliceFlag.Value = slice + } + if opt.Hide { + strSliceFlag.Hidden = true + } + flag = strSliceFlag } else if intFlag, ok := flag.(cli.IntFlag); ok { if opt.Usage != "" { intFlag.Usage = opt.Usage diff --git a/pkg/cli/cmds/token.go b/pkg/cli/cmds/token.go new file mode 100644 index 0000000000..9b20a86dde --- /dev/null +++ b/pkg/cli/cmds/token.go @@ -0,0 +1,41 @@ +package cmds + +import ( + "github.com/k3s-io/k3s/pkg/cli/cmds" + "github.com/k3s-io/k3s/pkg/cli/token" + "github.com/k3s-io/k3s/pkg/configfilearg" + "github.com/urfave/cli" +) + +func NewTokenCommand() cli.Command { + k3sOpts := K3SFlagSet{ + "kubeconfig": copy, + "data-dir": { + Usage: "(data) Folder to hold state", + Default: rke2Path, + }, + } + subCommandOpts := map[string]K3SFlagSet{ + "create": { + "description": copy, + "groups": copy, + "ttl": copy, + "usages": copy, + }, + "list": { + "output": copy, + }, + } + + command := cmds.NewTokenCommands(token.Create, token.Delete, token.Generate, token.List) + configfilearg.DefaultParser.ValidFlags[command.Name] = command.Flags + for i, subcommand := range command.Subcommands { + if s, ok := subCommandOpts[subcommand.Name]; ok { + k3sOpts.CopyInto(s) + command.Subcommands[i] = mustCmdFromK3S(subcommand, s) + } else { + command.Subcommands[i] = mustCmdFromK3S(subcommand, k3sOpts) + } + } + return mustCmdFromK3S(command, nil) +}