From 0babc3965329d99231bcfad25c2cc74e10c78e4e Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 4 Mar 2020 00:37:18 +0300 Subject: [PATCH] feat: split `osctl` commands into Talos API and cluster management This keeps backwards compatibility with `osctl` CLI binary with the exception of `osctl config generate` which was renamed to `osctl gen config` to avoid confusion with other `osctl config` commands which operate on client config, not Talos server config. Command implementation and helpers were split into subpackages for cleaner code and more visible boundaries. The resulting binary still combines commands from both sections into a single binary. Signed-off-by: Andrey Smirnov --- Dockerfile | 9 +- cmd/osctl/cmd/completion.go | 4 +- cmd/osctl/cmd/config.go | 302 ------------------ cmd/osctl/cmd/mgmt/cluster.go | 13 + cmd/osctl/cmd/mgmt/cluster/cluster.go | 38 +++ .../{cluster.go => mgmt/cluster/create.go} | 191 +++-------- cmd/osctl/cmd/mgmt/cluster/destroy.go | 45 +++ cmd/osctl/cmd/mgmt/cluster/show.go | 99 ++++++ cmd/osctl/cmd/mgmt/config.go | 167 ++++++++++ .../{ => mgmt}/firecracker_launch_linux.go | 4 +- cmd/osctl/cmd/{ => mgmt}/gen.go | 34 +- .../cmd/{ => mgmt}/loadbalancer_launch.go | 6 +- cmd/osctl/cmd/mgmt/root.go | 14 + cmd/osctl/cmd/{ => mgmt}/validate.go | 8 +- cmd/osctl/cmd/root.go | 92 +----- cmd/osctl/cmd/talos/config.go | 172 ++++++++++ cmd/osctl/cmd/{ => talos}/containers.go | 9 +- cmd/osctl/cmd/{ => talos}/copy.go | 6 +- cmd/osctl/cmd/{ => talos}/dmesg.go | 6 +- cmd/osctl/cmd/{ => talos}/interfaces.go | 9 +- cmd/osctl/cmd/{ => talos}/kubeconfig.go | 6 +- cmd/osctl/cmd/{ => talos}/list.go | 6 +- cmd/osctl/cmd/{ => talos}/logs.go | 9 +- cmd/osctl/cmd/{ => talos}/memory.go | 9 +- cmd/osctl/cmd/{ => talos}/mounts.go | 9 +- cmd/osctl/cmd/{ => talos}/processes.go | 11 +- cmd/osctl/cmd/{ => talos}/read.go | 6 +- cmd/osctl/cmd/{ => talos}/reboot.go | 4 +- cmd/osctl/cmd/{ => talos}/reset.go | 4 +- cmd/osctl/cmd/{ => talos}/restart.go | 4 +- cmd/osctl/cmd/talos/root.go | 81 +++++ cmd/osctl/cmd/{ => talos}/routes.go | 9 +- cmd/osctl/cmd/{ => talos}/service.go | 17 +- cmd/osctl/cmd/{ => talos}/shutdown.go | 4 +- cmd/osctl/cmd/{ => talos}/stats.go | 9 +- cmd/osctl/cmd/{ => talos}/time.go | 9 +- cmd/osctl/cmd/{ => talos}/upgrade.go | 9 +- cmd/osctl/cmd/{ => talos}/version.go | 9 +- cmd/osctl/main.go | 4 +- cmd/osctl/pkg/{ => mgmt}/helpers/artifacts.go | 0 .../pkg/{ => mgmt}/helpers/helpers_test.go | 0 cmd/osctl/pkg/mgmt/helpers/image.go | 25 ++ cmd/osctl/pkg/{ => talos}/helpers/archive.go | 0 cmd/osctl/pkg/{ => talos}/helpers/checks.go | 0 cmd/osctl/pkg/talos/helpers/helpers_test.go | 14 + cmd/osctl/pkg/{ => talos}/helpers/peer.go | 0 docs/osctl/osctl_config.md | 1 - docs/osctl/osctl_gen.md | 1 + ...config_generate.md => osctl_gen_config.md} | 12 +- internal/integration/cli/validate.go | 2 +- internal/integration/provision/upgrade.go | 2 +- pkg/cli/cli.go | 6 + {cmd/osctl/pkg/helpers => pkg/cli}/context.go | 6 +- .../pkg/helpers/cli.go => pkg/cli/output.go | 2 +- {cmd/osctl/pkg/helpers => pkg/cli}/should.go | 2 +- 55 files changed, 863 insertions(+), 657 deletions(-) delete mode 100644 cmd/osctl/cmd/config.go create mode 100644 cmd/osctl/cmd/mgmt/cluster.go create mode 100644 cmd/osctl/cmd/mgmt/cluster/cluster.go rename cmd/osctl/cmd/{cluster.go => mgmt/cluster/create.go} (54%) create mode 100644 cmd/osctl/cmd/mgmt/cluster/destroy.go create mode 100644 cmd/osctl/cmd/mgmt/cluster/show.go create mode 100644 cmd/osctl/cmd/mgmt/config.go rename cmd/osctl/cmd/{ => mgmt}/firecracker_launch_linux.go (92%) rename cmd/osctl/cmd/{ => mgmt}/gen.go (90%) rename cmd/osctl/cmd/{ => mgmt}/loadbalancer_launch.go (94%) create mode 100644 cmd/osctl/cmd/mgmt/root.go rename cmd/osctl/cmd/{ => mgmt}/validate.go (88%) create mode 100644 cmd/osctl/cmd/talos/config.go rename cmd/osctl/cmd/{ => talos}/containers.go (93%) rename cmd/osctl/cmd/{ => talos}/copy.go (95%) rename cmd/osctl/cmd/{ => talos}/dmesg.go (94%) rename cmd/osctl/cmd/{ => talos}/interfaces.go (90%) rename cmd/osctl/cmd/{ => talos}/kubeconfig.go (94%) rename cmd/osctl/cmd/{ => talos}/list.go (97%) rename cmd/osctl/cmd/{ => talos}/logs.go (96%) rename cmd/osctl/cmd/{ => talos}/memory.go (97%) rename cmd/osctl/cmd/{ => talos}/mounts.go (91%) rename cmd/osctl/cmd/{ => talos}/processes.go (96%) rename cmd/osctl/cmd/{ => talos}/read.go (92%) rename cmd/osctl/cmd/{ => talos}/reboot.go (94%) rename cmd/osctl/cmd/{ => talos}/reset.go (96%) rename cmd/osctl/cmd/{ => talos}/restart.go (96%) create mode 100644 cmd/osctl/cmd/talos/root.go rename cmd/osctl/cmd/{ => talos}/routes.go (90%) rename cmd/osctl/cmd/{ => talos}/service.go (95%) rename cmd/osctl/cmd/{ => talos}/shutdown.go (94%) rename cmd/osctl/cmd/{ => talos}/stats.go (93%) rename cmd/osctl/cmd/{ => talos}/time.go (92%) rename cmd/osctl/cmd/{ => talos}/upgrade.go (90%) rename cmd/osctl/cmd/{ => talos}/version.go (90%) rename cmd/osctl/pkg/{ => mgmt}/helpers/artifacts.go (100%) rename cmd/osctl/pkg/{ => mgmt}/helpers/helpers_test.go (100%) create mode 100644 cmd/osctl/pkg/mgmt/helpers/image.go rename cmd/osctl/pkg/{ => talos}/helpers/archive.go (100%) rename cmd/osctl/pkg/{ => talos}/helpers/checks.go (100%) create mode 100644 cmd/osctl/pkg/talos/helpers/helpers_test.go rename cmd/osctl/pkg/{ => talos}/helpers/peer.go (100%) rename docs/osctl/{osctl_config_generate.md => osctl_gen_config.md} (78%) create mode 100644 pkg/cli/cli.go rename {cmd/osctl/pkg/helpers => pkg/cli}/context.go (83%) rename cmd/osctl/pkg/helpers/cli.go => pkg/cli/output.go (97%) rename {cmd/osctl/pkg/helpers => pkg/cli}/should.go (96%) diff --git a/Dockerfile b/Dockerfile index d6a063ba9..9b16d6882 100644 --- a/Dockerfile +++ b/Dockerfile @@ -222,8 +222,9 @@ ARG SHA ARG TAG ARG ARTIFACTS ARG VERSION_PKG="github.com/talos-systems/talos/pkg/version" +ARG MGMT_HELPERS_PKG="github.com/talos-systems/talos/cmd/osctl/pkg/mgmt/helpers" WORKDIR /src/cmd/osctl -RUN --mount=type=cache,target=/.cache/go-build GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X ${VERSION_PKG}.Name=Client -X ${VERSION_PKG}.SHA=${SHA} -X ${VERSION_PKG}.Tag=${TAG} -X github.com/talos-systems/talos/cmd/osctl/pkg/helpers.ArtifactsPath=${ARTIFACTS}" -o /osctl-linux-amd64 +RUN --mount=type=cache,target=/.cache/go-build GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X ${VERSION_PKG}.Name=Client -X ${VERSION_PKG}.SHA=${SHA} -X ${VERSION_PKG}.Tag=${TAG} -X ${MGMT_HELPERS_PKG}.ArtifactsPath=${ARTIFACTS}" -o /osctl-linux-amd64 RUN chmod +x /osctl-linux-amd64 FROM scratch AS osctl-linux @@ -234,8 +235,9 @@ ARG SHA ARG TAG ARG ARTIFACTS ARG VERSION_PKG="github.com/talos-systems/talos/pkg/version" +ARG MGMT_HELPERS_PKG="github.com/talos-systems/talos/cmd/osctl/pkg/mgmt/helpers" WORKDIR /src/cmd/osctl -RUN --mount=type=cache,target=/.cache/go-build GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w -X ${VERSION_PKG}.Name=Client -X ${VERSION_PKG}.SHA=${SHA} -X ${VERSION_PKG}.Tag=${TAG} -X github.com/talos-systems/talos/cmd/osctl/pkg/helpers.ArtifactsPath=${ARTIFACTS}" -o /osctl-darwin-amd64 +RUN --mount=type=cache,target=/.cache/go-build GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w -X ${VERSION_PKG}.Name=Client -X ${VERSION_PKG}.SHA=${SHA} -X ${VERSION_PKG}.Tag=${TAG} -X ${MGMT_HELPERS_PKG}.ArtifactsPath=${ARTIFACTS}" -o /osctl-darwin-amd64 RUN chmod +x /osctl-darwin-amd64 FROM scratch AS osctl-darwin @@ -419,9 +421,10 @@ FROM base AS integration-test-provision-linux-build ARG SHA ARG TAG ARG VERSION_PKG="github.com/talos-systems/talos/pkg/version" +ARG MGMT_HELPERS_PKG="github.com/talos-systems/talos/cmd/osctl/pkg/mgmt/helpers" ARG ARTIFACTS RUN --mount=type=cache,target=/.cache/go-build GOOS=linux GOARCH=amd64 go test -c \ - -ldflags "-s -w -X ${VERSION_PKG}.Name=Client -X ${VERSION_PKG}.SHA=${SHA} -X ${VERSION_PKG}.Tag=${TAG} -X github.com/talos-systems/talos/cmd/osctl/pkg/helpers.ArtifactsPath=${ARTIFACTS}" \ + -ldflags "-s -w -X ${VERSION_PKG}.Name=Client -X ${VERSION_PKG}.SHA=${SHA} -X ${VERSION_PKG}.Tag=${TAG} -X ${MGMT_HELPERS_PKG}.ArtifactsPath=${ARTIFACTS}" \ -tags integration,integration_provision \ ./internal/integration diff --git a/cmd/osctl/cmd/completion.go b/cmd/osctl/cmd/completion.go index b9275d254..d3afef81e 100644 --- a/cmd/osctl/cmd/completion.go +++ b/cmd/osctl/cmd/completion.go @@ -10,7 +10,7 @@ import ( "github.com/spf13/cobra" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/pkg/cli" ) // completionCmd represents the completion command @@ -52,7 +52,7 @@ osctl completion zsh > "${fpath[1]}/_osctl"`, Args: cobra.ExactValidArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if len(args) != 1 { - helpers.Should(cmd.Usage()) + cli.Should(cmd.Usage()) os.Exit(1) } diff --git a/cmd/osctl/cmd/config.go b/cmd/osctl/cmd/config.go deleted file mode 100644 index a29098dc6..000000000 --- a/cmd/osctl/cmd/config.go +++ /dev/null @@ -1,302 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -package cmd - -import ( - "encoding/base64" - "fmt" - "io/ioutil" - "net/url" - "os" - "path/filepath" - "strings" - - "github.com/spf13/cobra" - "gopkg.in/yaml.v2" - - clientconfig "github.com/talos-systems/talos/cmd/osctl/pkg/client/config" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" - "github.com/talos-systems/talos/pkg/config" - "github.com/talos-systems/talos/pkg/config/machine" - "github.com/talos-systems/talos/pkg/config/types/v1alpha1/generate" - "github.com/talos-systems/talos/pkg/constants" -) - -var ( - configVersion string - dnsDomain string - kubernetesVersion string - installDisk string - installImage string - outputDir string -) - -// configCmd represents the config command. -var configCmd = &cobra.Command{ - Use: "config", - Short: "Manage the client configuration", - Long: ``, -} - -// configEndpointCmd represents the config endpoint command. -var configEndpointCmd = &cobra.Command{ - Use: "endpoint ...", - Aliases: []string{"endpoints"}, - Short: "Set the endpoint(s) for the current context", - Long: ``, - Args: cobra.MinimumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - c, err := clientconfig.Open(talosconfig) - if err != nil { - return fmt.Errorf("error reading config: %w", err) - } - if c.Context == "" { - return fmt.Errorf("no context is set") - } - - c.Contexts[c.Context].Endpoints = args - if err := c.Save(talosconfig); err != nil { - return fmt.Errorf("error writing config: %w", err) - } - - return nil - }, -} - -// configNodeCmd represents the config node command. -var configNodeCmd = &cobra.Command{ - Use: "node ...", - Aliases: []string{"nodes"}, - Short: "Set the node(s) for the current context", - Long: ``, - Args: cobra.ArbitraryArgs, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := clientconfig.Open(talosconfig) - if err != nil { - return fmt.Errorf("error reading config: %w", err) - } - if c.Context == "" { - return fmt.Errorf("no context is set") - } - - c.Contexts[c.Context].Nodes = args - if err := c.Save(talosconfig); err != nil { - return fmt.Errorf("error writing config: %w", err) - } - - return nil - }, -} - -// configContextCmd represents the configc context command. -var configContextCmd = &cobra.Command{ - Use: "context ", - Short: "Set the current context", - Long: ``, - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - context := args[0] - - c, err := clientconfig.Open(talosconfig) - if err != nil { - return fmt.Errorf("error reading config: %w", err) - } - - c.Context = context - - if err := c.Save(talosconfig); err != nil { - return fmt.Errorf("error writing config: %s", err) - } - - return nil - }, -} - -// configAddCmd represents the config add command. -var configAddCmd = &cobra.Command{ - Use: "add ", - Short: "Add a new context", - Long: ``, - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - context := args[0] - c, err := clientconfig.Open(talosconfig) - if err != nil { - return fmt.Errorf("error reading config: %w", err) - } - - caBytes, err := ioutil.ReadFile(ca) - if err != nil { - return fmt.Errorf("error reading CA: %w", err) - } - - crtBytes, err := ioutil.ReadFile(crt) - if err != nil { - return fmt.Errorf("error reading certificate: %w", err) - } - - keyBytes, err := ioutil.ReadFile(key) - if err != nil { - return fmt.Errorf("error reading key: %w", err) - } - - newContext := &clientconfig.Context{ - CA: base64.StdEncoding.EncodeToString(caBytes), - Crt: base64.StdEncoding.EncodeToString(crtBytes), - Key: base64.StdEncoding.EncodeToString(keyBytes), - } - - if c.Contexts == nil { - c.Contexts = map[string]*clientconfig.Context{} - } - - c.Contexts[context] = newContext - if err := c.Save(talosconfig); err != nil { - return fmt.Errorf("error writing config: %w", err) - } - - return nil - }, -} - -// configGenerateCmd represents the config generate command. -var configGenerateCmd = &cobra.Command{ - Use: "generate https://", - Short: "Generate a set of configuration files", - Long: ``, - Args: cobra.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - // Validate url input to ensure it has https:// scheme before we attempt to gen - u, err := url.Parse(args[1]) - if err != nil { - return fmt.Errorf("failed to parse load balancer IP or DNS name: %w", err) - } - if u.Scheme == "" { - return fmt.Errorf("no scheme specified for load balancer IP or DNS name\ntry \"https://\"") - } - - switch configVersion { - case "v1alpha1": - return genV1Alpha1Config(args) - } - - return nil - }, -} - -//nolint: gocyclo -func genV1Alpha1Config(args []string) error { - // If output dir isn't specified, set to the current working dir - var err error - if outputDir == "" { - outputDir, err = os.Getwd() - if err != nil { - return fmt.Errorf("failed to get working dir: %w", err) - } - } - - // Create dir path, ignoring "already exists" messages - if err = os.MkdirAll(outputDir, os.ModePerm); err != nil && !os.IsExist(err) { - return fmt.Errorf("failed to create output dir: %w", err) - } - - var genOptions []generate.GenOption //nolint: prealloc - - for _, registryMirror := range registryMirrors { - components := strings.SplitN(registryMirror, "=", 2) - if len(components) != 2 { - return fmt.Errorf("invalid registry mirror spec: %q", registryMirror) - } - - genOptions = append(genOptions, generate.WithRegistryMirror(components[0], components[1])) - } - - configBundle, err := config.NewConfigBundle( - config.WithInputOptions( - &config.InputOptions{ - ClusterName: args[0], - Endpoint: args[1], - KubeVersion: kubernetesVersion, - GenOptions: append(genOptions, - generate.WithInstallDisk(installDisk), - generate.WithInstallImage(installImage), - generate.WithAdditionalSubjectAltNames(additionalSANs), - generate.WithDNSDomain(dnsDomain), - ), - }, - ), - ) - if err != nil { - return fmt.Errorf("failed to generate config bundle: %w", err) - } - - for _, t := range []machine.Type{machine.TypeInit, machine.TypeControlPlane, machine.TypeWorker} { - name = strings.ToLower(t.String()) + ".yaml" - fullFilePath := filepath.Join(outputDir, name) - - var configString string - - switch t { - case machine.TypeInit: - configString, err = configBundle.Init().String() - if err != nil { - return err - } - case machine.TypeControlPlane: - configString, err = configBundle.ControlPlane().String() - if err != nil { - return err - } - case machine.TypeWorker: - configString, err = configBundle.Join().String() - if err != nil { - return err - } - } - - if err = ioutil.WriteFile(fullFilePath, []byte(configString), 0644); err != nil { - return err - } - - fmt.Printf("created %s\n", fullFilePath) - } - - // We set the default endpoint to localhost for configs generated, with expectation user will tweak later - configBundle.TalosConfig().Contexts[args[0]].Endpoints = []string{"127.0.0.1"} - - data, err := yaml.Marshal(configBundle.TalosConfig()) - if err != nil { - return fmt.Errorf("failed to marshal config: %+v", err) - } - - fullFilePath := filepath.Join(outputDir, "talosconfig") - - if err = ioutil.WriteFile(fullFilePath, data, 0644); err != nil { - return fmt.Errorf("%w", err) - } - - fmt.Printf("created %s\n", fullFilePath) - - return nil -} - -func init() { - configCmd.AddCommand(configContextCmd, configEndpointCmd, configNodeCmd, configAddCmd, configGenerateCmd) - configAddCmd.Flags().StringVar(&ca, "ca", "", "the path to the CA certificate") - configAddCmd.Flags().StringVar(&crt, "crt", "", "the path to the certificate") - configAddCmd.Flags().StringVar(&key, "key", "", "the path to the key") - configGenerateCmd.Flags().StringVar(&installDisk, "install-disk", "/dev/sda", "the disk to install to") - configGenerateCmd.Flags().StringVar(&installImage, "install-image", defaultImage(constants.DefaultInstallerImageRepository), "the image used to perform an installation") // nolint: lll - configGenerateCmd.Flags().StringSliceVar(&additionalSANs, "additional-sans", []string{}, "additional Subject-Alt-Names for the APIServer certificate") - configGenerateCmd.Flags().StringVar(&dnsDomain, "dns-domain", "cluster.local", "the dns domain to use for cluster") - configGenerateCmd.Flags().StringVar(&configVersion, "version", "v1alpha1", "the desired machine config version to generate") - configGenerateCmd.Flags().StringVar(&kubernetesVersion, "kubernetes-version", constants.DefaultKubernetesVersion, "desired kubernetes version to run") - configGenerateCmd.Flags().StringVarP(&outputDir, "output-dir", "o", "", "destination to output generated files") - configGenerateCmd.Flags().StringSliceVar(®istryMirrors, "registry-mirror", []string{}, "list of registry mirrors to use in format: =") - helpers.Should(configAddCmd.MarkFlagRequired("ca")) - helpers.Should(configAddCmd.MarkFlagRequired("crt")) - helpers.Should(configAddCmd.MarkFlagRequired("key")) - rootCmd.AddCommand(configCmd) -} diff --git a/cmd/osctl/cmd/mgmt/cluster.go b/cmd/osctl/cmd/mgmt/cluster.go new file mode 100644 index 000000000..f41e5bb4a --- /dev/null +++ b/cmd/osctl/cmd/mgmt/cluster.go @@ -0,0 +1,13 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package mgmt + +import ( + "github.com/talos-systems/talos/cmd/osctl/cmd/mgmt/cluster" +) + +func init() { + addCommand(cluster.Cmd) +} diff --git a/cmd/osctl/cmd/mgmt/cluster/cluster.go b/cmd/osctl/cmd/mgmt/cluster/cluster.go new file mode 100644 index 000000000..5e61f58ec --- /dev/null +++ b/cmd/osctl/cmd/mgmt/cluster/cluster.go @@ -0,0 +1,38 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// Package cluster implements "cluster" subcommands. +package cluster + +import ( + "path/filepath" + + "github.com/spf13/cobra" + + clientconfig "github.com/talos-systems/talos/cmd/osctl/pkg/client/config" +) + +// Cmd represents the cluster command +var Cmd = &cobra.Command{ + Use: "cluster", + Short: "A collection of commands for managing local docker-based or firecracker-based clusters", + Long: ``, +} + +var ( + provisioner string + stateDir string + clusterName string +) + +func init() { + defaultStateDir, err := clientconfig.GetTalosDirectory() + if err == nil { + defaultStateDir = filepath.Join(defaultStateDir, "clusters") + } + + Cmd.PersistentFlags().StringVar(&provisioner, "provisioner", "docker", "Talos cluster provisioner to use") + Cmd.PersistentFlags().StringVar(&stateDir, "state", defaultStateDir, "directory path to store cluster state") + Cmd.PersistentFlags().StringVar(&clusterName, "name", "talos-default", "the name of the cluster") +} diff --git a/cmd/osctl/cmd/cluster.go b/cmd/osctl/cmd/mgmt/cluster/create.go similarity index 54% rename from cmd/osctl/cmd/cluster.go rename to cmd/osctl/cmd/mgmt/cluster/create.go index 316f8303f..50599b069 100644 --- a/cmd/osctl/cmd/cluster.go +++ b/cmd/osctl/cmd/mgmt/cluster/create.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package cluster import ( "context" @@ -11,22 +11,19 @@ import ( "math/big" "net" "os" - "path/filepath" - "sort" "strings" - "text/tabwriter" "time" - "github.com/dustin/go-humanize" "github.com/spf13/cobra" clientconfig "github.com/talos-systems/talos/cmd/osctl/pkg/client/config" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/mgmt/helpers" "github.com/talos-systems/talos/internal/pkg/provision" "github.com/talos-systems/talos/internal/pkg/provision/access" "github.com/talos-systems/talos/internal/pkg/provision/check" "github.com/talos-systems/talos/internal/pkg/provision/providers" "github.com/talos-systems/talos/internal/pkg/runtime" + "github.com/talos-systems/talos/pkg/cli" "github.com/talos-systems/talos/pkg/config" "github.com/talos-systems/talos/pkg/config/types/v1alpha1/generate" "github.com/talos-systems/talos/pkg/constants" @@ -34,11 +31,11 @@ import ( ) var ( - provisioner string - clusterName string + talosconfig string nodeImage string nodeInstallImage string registryMirrors []string + kubernetesVersion string nodeVmlinuxPath string nodeInitramfsPath string bootloaderEmulation bool @@ -58,46 +55,16 @@ var ( cniBinPath []string cniConfDir string cniCacheDir string - stateDir string ) -// clusterCmd represents the cluster command -var clusterCmd = &cobra.Command{ - Use: "cluster", - Short: "A collection of commands for managing local docker-based or firecracker-based clusters", - Long: ``, -} - -// clusterUpCmd represents the cluster up command -var clusterUpCmd = &cobra.Command{ +// createCmd represents the cluster up command +var createCmd = &cobra.Command{ Use: "create", Short: "Creates a local docker-based or firecracker-based kubernetes cluster", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - return helpers.WithCLIContext(context.Background(), create) - }, -} - -// clusterDownCmd represents the cluster up command -var clusterDownCmd = &cobra.Command{ - Use: "destroy", - Short: "Destroys a local docker-based or firecracker-based kubernetes cluster", - Long: ``, - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - return helpers.WithCLIContext(context.Background(), destroy) - }, -} - -// clusterShowCmd represents the cluster show command -var clusterShowCmd = &cobra.Command{ - Use: "show", - Short: "Shows info about a local provisioned kubernetes cluster", - Long: ``, - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - return helpers.WithCLIContext(context.Background(), show) + return cli.WithContext(context.Background(), create) }, } @@ -305,87 +272,6 @@ func create(ctx context.Context) (err error) { return check.Wait(checkCtx, clusterAccess, check.DefaultClusterChecks(), check.StderrReporter()) } -func destroy(ctx context.Context) error { - provisioner, err := providers.Factory(ctx, provisioner) - if err != nil { - return err - } - - defer provisioner.Close() //nolint: errcheck - - cluster, err := provisioner.Reflect(ctx, clusterName, stateDir) - if err != nil { - return err - } - - return provisioner.Destroy(ctx, cluster) -} - -func show(ctx context.Context) error { - provisioner, err := providers.Factory(ctx, provisioner) - if err != nil { - return err - } - - defer provisioner.Close() //nolint: errcheck - - cluster, err := provisioner.Reflect(ctx, clusterName, stateDir) - if err != nil { - return err - } - - w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) - fmt.Fprintf(w, "PROVISIONER\t%s\n", cluster.Provisioner()) - fmt.Fprintf(w, "NAME\t%s\n", cluster.Info().ClusterName) - fmt.Fprintf(w, "NETWORK NAME\t%s\n", cluster.Info().Network.Name) - - ones, _ := cluster.Info().Network.CIDR.Mask.Size() - fmt.Fprintf(w, "NETWORK CIDR\t%s/%d\n", cluster.Info().Network.CIDR.IP, ones) - fmt.Fprintf(w, "NETWORK GATEWAY\t%s\n", cluster.Info().Network.GatewayAddr) - fmt.Fprintf(w, "NETWORK MTU\t%d\n", cluster.Info().Network.MTU) - - if err = w.Flush(); err != nil { - return err - } - - fmt.Fprint(os.Stdout, "\nNODES:\n\n") - - w = tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) - - fmt.Fprintf(w, "NAME\tTYPE\tIP\tCPU\tRAM\tDISK\n") - - nodes := cluster.Info().Nodes - sort.Slice(nodes, func(i, j int) bool { return nodes[i].Name < nodes[j].Name }) - - for _, node := range nodes { - cpus := "-" - if node.NanoCPUs > 0 { - cpus = fmt.Sprintf("%.2f", float64(node.NanoCPUs)/1000.0/1000.0/1000.0) - } - - mem := "-" - if node.Memory > 0 { - mem = humanize.Bytes(uint64(node.Memory)) - } - - disk := "-" - if node.DiskSize > 0 { - disk = humanize.Bytes(uint64(node.DiskSize)) - } - - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", - node.Name, - node.Type, - node.PrivateIP, - cpus, - mem, - disk, - ) - } - - return w.Flush() -} - func saveConfig(cluster provision.Cluster, talosConfigObj *clientconfig.Config) (err error) { c, err := clientconfig.Open(talosconfig) if err != nil { @@ -418,39 +304,34 @@ func parseCPUShare() (int64, error) { } func init() { - defaultStateDir, err := clientconfig.GetTalosDirectory() - if err == nil { - defaultStateDir = filepath.Join(defaultStateDir, "clusters") + defaultTalosConfig, err := clientconfig.GetDefaultPath() + if err != nil { + fmt.Fprintf(os.Stderr, "failed to find default Talos config path: %s", err) } - clusterUpCmd.Flags().StringVar(&nodeImage, "image", defaultImage(constants.DefaultTalosImageRepository), "the image to use") - clusterUpCmd.Flags().StringVar(&nodeInstallImage, "install-image", defaultImage(constants.DefaultInstallerImageRepository), "the installer image to use") - clusterUpCmd.Flags().StringVar(&nodeVmlinuxPath, "vmlinux-path", helpers.ArtifactPath(constants.KernelUncompressedAsset), "the uncompressed kernel image to use") - clusterUpCmd.Flags().StringVar(&nodeInitramfsPath, "initrd-path", helpers.ArtifactPath(constants.InitramfsAsset), "the uncompressed kernel image to use") - clusterUpCmd.Flags().BoolVar(&bootloaderEmulation, "with-bootloader-emulation", false, "enable bootloader emulation to load kernel and initramfs from disk image") - clusterUpCmd.Flags().StringSliceVar(®istryMirrors, "registry-mirror", []string{}, "list of registry mirrors to use in format: =") - clusterUpCmd.Flags().IntVar(&networkMTU, "mtu", 1500, "MTU of the docker bridge network") - clusterUpCmd.Flags().StringVar(&networkCIDR, "cidr", "10.5.0.0/24", "CIDR of the docker bridge network") - clusterUpCmd.Flags().StringSliceVar(&nameservers, "nameservers", []string{"8.8.8.8", "1.1.1.1"}, "list of nameservers to use (VM only)") - clusterUpCmd.Flags().IntVar(&workers, "workers", 1, "the number of workers to create") - clusterUpCmd.Flags().IntVar(&masters, "masters", 1, "the number of masters to create") - clusterUpCmd.Flags().StringVar(&clusterCpus, "cpus", "1.5", "the share of CPUs as fraction (each container)") - clusterUpCmd.Flags().IntVar(&clusterMemory, "memory", 1024, "the limit on memory usage in MB (each container)") - clusterUpCmd.Flags().IntVar(&clusterDiskSize, "disk", 4*1024, "the limit on disk size in MB (each VM)") - clusterUpCmd.Flags().BoolVar(&clusterWait, "wait", false, "wait for the cluster to be ready before returning") - clusterUpCmd.Flags().DurationVar(&clusterWaitTimeout, "wait-timeout", 20*time.Minute, "timeout to wait for the cluster to be ready") - clusterUpCmd.Flags().BoolVar(&forceInitNodeAsEndpoint, "init-node-as-endpoint", false, "use init node as endpoint instead of any load balancer endpoint") - clusterUpCmd.Flags().StringVar(&forceEndpoint, "endpoint", "", "use endpoint instead of provider defaults") - clusterUpCmd.Flags().StringVar(&kubernetesVersion, "kubernetes-version", constants.DefaultKubernetesVersion, "desired kubernetes version to run") - clusterUpCmd.Flags().StringVarP(&inputDir, "input-dir", "i", "", "location of pre-generated config files") - clusterUpCmd.Flags().StringSliceVar(&cniBinPath, "cni-bin-path", []string{"/opt/cni/bin"}, "search path for CNI binaries") - clusterUpCmd.Flags().StringVar(&cniConfDir, "cni-conf-dir", "/etc/cni/conf.d", "CNI config directory path") - clusterUpCmd.Flags().StringVar(&cniCacheDir, "cni-cache-dir", "/var/lib/cni", "CNI cache directory path") - clusterCmd.PersistentFlags().StringVar(&provisioner, "provisioner", "docker", "Talos cluster provisioner to use") - clusterCmd.PersistentFlags().StringVar(&stateDir, "state", defaultStateDir, "directory path to store cluster state") - clusterCmd.PersistentFlags().StringVar(&clusterName, "name", "talos-default", "the name of the cluster") - clusterCmd.AddCommand(clusterUpCmd) - clusterCmd.AddCommand(clusterDownCmd) - clusterCmd.AddCommand(clusterShowCmd) - rootCmd.AddCommand(clusterCmd) + createCmd.Flags().StringVar(&talosconfig, "talosconfig", defaultTalosConfig, "The path to the Talos configuration file") + createCmd.Flags().StringVar(&nodeImage, "image", helpers.DefaultImage(constants.DefaultTalosImageRepository), "the image to use") + createCmd.Flags().StringVar(&nodeInstallImage, "install-image", helpers.DefaultImage(constants.DefaultInstallerImageRepository), "the installer image to use") + createCmd.Flags().StringVar(&nodeVmlinuxPath, "vmlinux-path", helpers.ArtifactPath(constants.KernelUncompressedAsset), "the uncompressed kernel image to use") + createCmd.Flags().StringVar(&nodeInitramfsPath, "initrd-path", helpers.ArtifactPath(constants.InitramfsAsset), "the uncompressed kernel image to use") + createCmd.Flags().BoolVar(&bootloaderEmulation, "with-bootloader-emulation", false, "enable bootloader emulation to load kernel and initramfs from disk image") + createCmd.Flags().StringSliceVar(®istryMirrors, "registry-mirror", []string{}, "list of registry mirrors to use in format: =") + createCmd.Flags().IntVar(&networkMTU, "mtu", 1500, "MTU of the docker bridge network") + createCmd.Flags().StringVar(&networkCIDR, "cidr", "10.5.0.0/24", "CIDR of the docker bridge network") + createCmd.Flags().StringSliceVar(&nameservers, "nameservers", []string{"8.8.8.8", "1.1.1.1"}, "list of nameservers to use (VM only)") + createCmd.Flags().IntVar(&workers, "workers", 1, "the number of workers to create") + createCmd.Flags().IntVar(&masters, "masters", 1, "the number of masters to create") + createCmd.Flags().StringVar(&clusterCpus, "cpus", "1.5", "the share of CPUs as fraction (each container)") + createCmd.Flags().IntVar(&clusterMemory, "memory", 1024, "the limit on memory usage in MB (each container)") + createCmd.Flags().IntVar(&clusterDiskSize, "disk", 4*1024, "the limit on disk size in MB (each VM)") + createCmd.Flags().BoolVar(&clusterWait, "wait", false, "wait for the cluster to be ready before returning") + createCmd.Flags().DurationVar(&clusterWaitTimeout, "wait-timeout", 20*time.Minute, "timeout to wait for the cluster to be ready") + createCmd.Flags().BoolVar(&forceInitNodeAsEndpoint, "init-node-as-endpoint", false, "use init node as endpoint instead of any load balancer endpoint") + createCmd.Flags().StringVar(&forceEndpoint, "endpoint", "", "use endpoint instead of provider defaults") + createCmd.Flags().StringVar(&kubernetesVersion, "kubernetes-version", constants.DefaultKubernetesVersion, "desired kubernetes version to run") + createCmd.Flags().StringVarP(&inputDir, "input-dir", "i", "", "location of pre-generated config files") + createCmd.Flags().StringSliceVar(&cniBinPath, "cni-bin-path", []string{"/opt/cni/bin"}, "search path for CNI binaries") + createCmd.Flags().StringVar(&cniConfDir, "cni-conf-dir", "/etc/cni/conf.d", "CNI config directory path") + createCmd.Flags().StringVar(&cniCacheDir, "cni-cache-dir", "/var/lib/cni", "CNI cache directory path") + Cmd.AddCommand(createCmd) } diff --git a/cmd/osctl/cmd/mgmt/cluster/destroy.go b/cmd/osctl/cmd/mgmt/cluster/destroy.go new file mode 100644 index 000000000..6733974c1 --- /dev/null +++ b/cmd/osctl/cmd/mgmt/cluster/destroy.go @@ -0,0 +1,45 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package cluster + +import ( + "context" + + "github.com/spf13/cobra" + + "github.com/talos-systems/talos/internal/pkg/provision/providers" + "github.com/talos-systems/talos/pkg/cli" +) + +// destroyCmd represents the cluster destroy command +var destroyCmd = &cobra.Command{ + Use: "destroy", + Short: "Destroys a local docker-based or firecracker-based kubernetes cluster", + Long: ``, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.WithContext(context.Background(), destroy) + }, +} + +func destroy(ctx context.Context) error { + provisioner, err := providers.Factory(ctx, provisioner) + if err != nil { + return err + } + + defer provisioner.Close() //nolint: errcheck + + cluster, err := provisioner.Reflect(ctx, clusterName, stateDir) + if err != nil { + return err + } + + return provisioner.Destroy(ctx, cluster) +} + +func init() { + Cmd.AddCommand(destroyCmd) +} diff --git a/cmd/osctl/cmd/mgmt/cluster/show.go b/cmd/osctl/cmd/mgmt/cluster/show.go new file mode 100644 index 000000000..e19e66322 --- /dev/null +++ b/cmd/osctl/cmd/mgmt/cluster/show.go @@ -0,0 +1,99 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package cluster + +import ( + "context" + "fmt" + "os" + "sort" + "text/tabwriter" + + "github.com/dustin/go-humanize" + "github.com/spf13/cobra" + + "github.com/talos-systems/talos/internal/pkg/provision/providers" + "github.com/talos-systems/talos/pkg/cli" +) + +// showCmd represents the cluster show command +var showCmd = &cobra.Command{ + Use: "show", + Short: "Shows info about a local provisioned kubernetes cluster", + Long: ``, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.WithContext(context.Background(), show) + }, +} + +func show(ctx context.Context) error { + provisioner, err := providers.Factory(ctx, provisioner) + if err != nil { + return err + } + + defer provisioner.Close() //nolint: errcheck + + cluster, err := provisioner.Reflect(ctx, clusterName, stateDir) + if err != nil { + return err + } + + w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) + fmt.Fprintf(w, "PROVISIONER\t%s\n", cluster.Provisioner()) + fmt.Fprintf(w, "NAME\t%s\n", cluster.Info().ClusterName) + fmt.Fprintf(w, "NETWORK NAME\t%s\n", cluster.Info().Network.Name) + + ones, _ := cluster.Info().Network.CIDR.Mask.Size() + fmt.Fprintf(w, "NETWORK CIDR\t%s/%d\n", cluster.Info().Network.CIDR.IP, ones) + fmt.Fprintf(w, "NETWORK GATEWAY\t%s\n", cluster.Info().Network.GatewayAddr) + fmt.Fprintf(w, "NETWORK MTU\t%d\n", cluster.Info().Network.MTU) + + if err = w.Flush(); err != nil { + return err + } + + fmt.Fprint(os.Stdout, "\nNODES:\n\n") + + w = tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) + + fmt.Fprintf(w, "NAME\tTYPE\tIP\tCPU\tRAM\tDISK\n") + + nodes := cluster.Info().Nodes + sort.Slice(nodes, func(i, j int) bool { return nodes[i].Name < nodes[j].Name }) + + for _, node := range nodes { + cpus := "-" + if node.NanoCPUs > 0 { + cpus = fmt.Sprintf("%.2f", float64(node.NanoCPUs)/1000.0/1000.0/1000.0) + } + + mem := "-" + if node.Memory > 0 { + mem = humanize.Bytes(uint64(node.Memory)) + } + + disk := "-" + if node.DiskSize > 0 { + disk = humanize.Bytes(uint64(node.DiskSize)) + } + + fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", + node.Name, + node.Type, + node.PrivateIP, + cpus, + mem, + disk, + ) + } + + return w.Flush() +} + +func init() { + Cmd.AddCommand(showCmd) +} diff --git a/cmd/osctl/cmd/mgmt/config.go b/cmd/osctl/cmd/mgmt/config.go new file mode 100644 index 000000000..f36675992 --- /dev/null +++ b/cmd/osctl/cmd/mgmt/config.go @@ -0,0 +1,167 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package mgmt + +import ( + "fmt" + "io/ioutil" + "net/url" + "os" + "path/filepath" + "strings" + + "github.com/spf13/cobra" + "gopkg.in/yaml.v2" + + "github.com/talos-systems/talos/cmd/osctl/pkg/mgmt/helpers" + "github.com/talos-systems/talos/pkg/config" + "github.com/talos-systems/talos/pkg/config/machine" + "github.com/talos-systems/talos/pkg/config/types/v1alpha1/generate" + "github.com/talos-systems/talos/pkg/constants" +) + +var ( + additionalSANs []string + configVersion string + dnsDomain string + kubernetesVersion string + installDisk string + installImage string + outputDir string + registryMirrors []string +) + +// genConfigCmd represents the gen config command. +var genConfigCmd = &cobra.Command{ + Use: "config https://", + Short: "Generates a set of configuration files for Talos cluster", + Long: ``, + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + // Validate url input to ensure it has https:// scheme before we attempt to gen + u, err := url.Parse(args[1]) + if err != nil { + return fmt.Errorf("failed to parse load balancer IP or DNS name: %w", err) + } + if u.Scheme == "" { + return fmt.Errorf("no scheme specified for load balancer IP or DNS name\ntry \"https://\"") + } + + switch configVersion { + case "v1alpha1": + return genV1Alpha1Config(args) + } + + return nil + }, +} + +//nolint: gocyclo +func genV1Alpha1Config(args []string) error { + // If output dir isn't specified, set to the current working dir + var err error + if outputDir == "" { + outputDir, err = os.Getwd() + if err != nil { + return fmt.Errorf("failed to get working dir: %w", err) + } + } + + // Create dir path, ignoring "already exists" messages + if err = os.MkdirAll(outputDir, os.ModePerm); err != nil && !os.IsExist(err) { + return fmt.Errorf("failed to create output dir: %w", err) + } + + var genOptions []generate.GenOption //nolint: prealloc + + for _, registryMirror := range registryMirrors { + components := strings.SplitN(registryMirror, "=", 2) + if len(components) != 2 { + return fmt.Errorf("invalid registry mirror spec: %q", registryMirror) + } + + genOptions = append(genOptions, generate.WithRegistryMirror(components[0], components[1])) + } + + configBundle, err := config.NewConfigBundle( + config.WithInputOptions( + &config.InputOptions{ + ClusterName: args[0], + Endpoint: args[1], + KubeVersion: kubernetesVersion, + GenOptions: append(genOptions, + generate.WithInstallDisk(installDisk), + generate.WithInstallImage(installImage), + generate.WithAdditionalSubjectAltNames(additionalSANs), + generate.WithDNSDomain(dnsDomain), + ), + }, + ), + ) + if err != nil { + return fmt.Errorf("failed to generate config bundle: %w", err) + } + + for _, t := range []machine.Type{machine.TypeInit, machine.TypeControlPlane, machine.TypeWorker} { + name := strings.ToLower(t.String()) + ".yaml" + fullFilePath := filepath.Join(outputDir, name) + + var configString string + + switch t { + case machine.TypeInit: + configString, err = configBundle.Init().String() + if err != nil { + return err + } + case machine.TypeControlPlane: + configString, err = configBundle.ControlPlane().String() + if err != nil { + return err + } + case machine.TypeWorker: + configString, err = configBundle.Join().String() + if err != nil { + return err + } + } + + if err = ioutil.WriteFile(fullFilePath, []byte(configString), 0644); err != nil { + return err + } + + fmt.Printf("created %s\n", fullFilePath) + } + + // We set the default endpoint to localhost for configs generated, with expectation user will tweak later + configBundle.TalosConfig().Contexts[args[0]].Endpoints = []string{"127.0.0.1"} + + data, err := yaml.Marshal(configBundle.TalosConfig()) + if err != nil { + return fmt.Errorf("failed to marshal config: %+v", err) + } + + fullFilePath := filepath.Join(outputDir, "talosconfig") + + if err = ioutil.WriteFile(fullFilePath, data, 0644); err != nil { + return fmt.Errorf("%w", err) + } + + fmt.Printf("created %s\n", fullFilePath) + + return nil +} + +func init() { + genCmd.AddCommand(genConfigCmd) + genConfigCmd.Flags().StringVar(&installDisk, "install-disk", "/dev/sda", "the disk to install to") + genConfigCmd.Flags().StringVar(&installImage, "install-image", helpers.DefaultImage(constants.DefaultInstallerImageRepository), "the image used to perform an installation") // nolint: lll + genConfigCmd.Flags().StringSliceVar(&additionalSANs, "additional-sans", []string{}, "additional Subject-Alt-Names for the APIServer certificate") + genConfigCmd.Flags().StringVar(&dnsDomain, "dns-domain", "cluster.local", "the dns domain to use for cluster") + genConfigCmd.Flags().StringVar(&configVersion, "version", "v1alpha1", "the desired machine config version to generate") + genConfigCmd.Flags().StringVar(&kubernetesVersion, "kubernetes-version", constants.DefaultKubernetesVersion, "desired kubernetes version to run") + genConfigCmd.Flags().StringVarP(&outputDir, "output-dir", "o", "", "destination to output generated files") + genConfigCmd.Flags().StringSliceVar(®istryMirrors, "registry-mirror", []string{}, "list of registry mirrors to use in format: =") +} diff --git a/cmd/osctl/cmd/firecracker_launch_linux.go b/cmd/osctl/cmd/mgmt/firecracker_launch_linux.go similarity index 92% rename from cmd/osctl/cmd/firecracker_launch_linux.go rename to cmd/osctl/cmd/mgmt/firecracker_launch_linux.go index 8b7c4f1e7..33a863720 100644 --- a/cmd/osctl/cmd/firecracker_launch_linux.go +++ b/cmd/osctl/cmd/mgmt/firecracker_launch_linux.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package mgmt import ( "github.com/spf13/cobra" @@ -23,5 +23,5 @@ var firecrackerLaunchCmd = &cobra.Command{ } func init() { - rootCmd.AddCommand(firecrackerLaunchCmd) + addCommand(firecrackerLaunchCmd) } diff --git a/cmd/osctl/cmd/gen.go b/cmd/osctl/cmd/mgmt/gen.go similarity index 90% rename from cmd/osctl/cmd/gen.go rename to cmd/osctl/cmd/mgmt/gen.go index 9f4b13dfe..fc8946d40 100644 --- a/cmd/osctl/cmd/gen.go +++ b/cmd/osctl/cmd/mgmt/gen.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package mgmt import ( "encoding/pem" @@ -17,10 +17,22 @@ import ( "github.com/spf13/cobra" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/pkg/cli" "github.com/talos-systems/talos/pkg/crypto/x509" ) +var ( + ca string + csr string + caHours int + crtHours int + ip string + key string + name string + organization string + rsa bool +) + // genCmd represents the gen command var genCmd = &cobra.Command{ Use: "gen", @@ -231,30 +243,30 @@ var keypairCmd = &cobra.Command{ func init() { // Certificate Authorities caCmd.Flags().StringVar(&organization, "organization", "", "X.509 distinguished name for the Organization") - helpers.Should(cobra.MarkFlagRequired(caCmd.Flags(), "organization")) + cli.Should(cobra.MarkFlagRequired(caCmd.Flags(), "organization")) caCmd.Flags().IntVar(&caHours, "hours", 87600, "the hours from now on which the certificate validity period ends") caCmd.Flags().BoolVar(&rsa, "rsa", false, "generate in RSA format") // Keys keyCmd.Flags().StringVar(&name, "name", "", "the basename of the generated file") - helpers.Should(cobra.MarkFlagRequired(keyCmd.Flags(), "name")) + cli.Should(cobra.MarkFlagRequired(keyCmd.Flags(), "name")) // Certificates crtCmd.Flags().StringVar(&name, "name", "", "the basename of the generated file") - helpers.Should(cobra.MarkFlagRequired(crtCmd.Flags(), "name")) + cli.Should(cobra.MarkFlagRequired(crtCmd.Flags(), "name")) crtCmd.Flags().StringVar(&ca, "ca", "", "path to the PEM encoded CERTIFICATE") - helpers.Should(cobra.MarkFlagRequired(crtCmd.Flags(), "ca")) + cli.Should(cobra.MarkFlagRequired(crtCmd.Flags(), "ca")) crtCmd.Flags().StringVar(&csr, "csr", "", "path to the PEM encoded CERTIFICATE REQUEST") - helpers.Should(cobra.MarkFlagRequired(crtCmd.Flags(), "csr")) + cli.Should(cobra.MarkFlagRequired(crtCmd.Flags(), "csr")) crtCmd.Flags().IntVar(&crtHours, "hours", 24, "the hours from now on which the certificate validity period ends") // Keypairs keypairCmd.Flags().StringVar(&ip, "ip", "", "generate the certificate for this IP address") keypairCmd.Flags().StringVar(&organization, "organization", "", "X.509 distinguished name for the Organization") - helpers.Should(cobra.MarkFlagRequired(keypairCmd.Flags(), "organization")) + cli.Should(cobra.MarkFlagRequired(keypairCmd.Flags(), "organization")) // Certificate Signing Requests csrCmd.Flags().StringVar(&key, "key", "", "path to the PEM encoded EC or RSA PRIVATE KEY") - helpers.Should(cobra.MarkFlagRequired(csrCmd.Flags(), "key")) + cli.Should(cobra.MarkFlagRequired(csrCmd.Flags(), "key")) csrCmd.Flags().StringVar(&ip, "ip", "", "generate the certificate for this IP address") - helpers.Should(cobra.MarkFlagRequired(csrCmd.Flags(), "ip")) + cli.Should(cobra.MarkFlagRequired(csrCmd.Flags(), "ip")) genCmd.AddCommand(caCmd, keypairCmd, keyCmd, csrCmd, crtCmd) - rootCmd.AddCommand(genCmd) + addCommand(genCmd) } diff --git a/cmd/osctl/cmd/loadbalancer_launch.go b/cmd/osctl/cmd/mgmt/loadbalancer_launch.go similarity index 94% rename from cmd/osctl/cmd/loadbalancer_launch.go rename to cmd/osctl/cmd/mgmt/loadbalancer_launch.go index a6f9c3185..ed086a49f 100644 --- a/cmd/osctl/cmd/loadbalancer_launch.go +++ b/cmd/osctl/cmd/mgmt/loadbalancer_launch.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package mgmt import ( "fmt" @@ -22,7 +22,7 @@ var loadbalancerLaunchCmdFlags struct { // loadbalancerLaunchCmd represents the loadbalancer-launch command var loadbalancerLaunchCmd = &cobra.Command{ Use: "loadbalancer-launch", - Short: "Intneral command used by Firecracker provisioner", + Short: "Internal command used by Firecracker provisioner", Long: ``, Args: cobra.NoArgs, Hidden: true, @@ -55,5 +55,5 @@ func init() { loadbalancerLaunchCmd.Flags().StringVar(&loadbalancerLaunchCmdFlags.addr, "loadbalancer-addr", "localhost", "load balancer listen address (IP or host)") loadbalancerLaunchCmd.Flags().StringSliceVar(&loadbalancerLaunchCmdFlags.upstreams, "loadbalancer-upstreams", []string{}, "load balancer upstreams (nodes to proxy to)") loadbalancerLaunchCmd.Flags().BoolVar(&loadbalancerLaunchCmdFlags.apidOnlyInitNode, "apid-only-init-node", false, "use only apid init node for load balancing") - rootCmd.AddCommand(loadbalancerLaunchCmd) + addCommand(loadbalancerLaunchCmd) } diff --git a/cmd/osctl/cmd/mgmt/root.go b/cmd/osctl/cmd/mgmt/root.go new file mode 100644 index 000000000..527afd17a --- /dev/null +++ b/cmd/osctl/cmd/mgmt/root.go @@ -0,0 +1,14 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package mgmt + +import "github.com/spf13/cobra" + +// Commands is a list of commands published by the package +var Commands []*cobra.Command + +func addCommand(cmd *cobra.Command) { + Commands = append(Commands, cmd) +} diff --git a/cmd/osctl/cmd/validate.go b/cmd/osctl/cmd/mgmt/validate.go similarity index 88% rename from cmd/osctl/cmd/validate.go rename to cmd/osctl/cmd/mgmt/validate.go index 845c0cf5c..c87870c75 100644 --- a/cmd/osctl/cmd/validate.go +++ b/cmd/osctl/cmd/mgmt/validate.go @@ -2,15 +2,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package mgmt import ( "fmt" "github.com/spf13/cobra" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" "github.com/talos-systems/talos/internal/pkg/runtime" + "github.com/talos-systems/talos/pkg/cli" "github.com/talos-systems/talos/pkg/config" ) @@ -48,6 +48,6 @@ var validateCmd = &cobra.Command{ func init() { validateCmd.Flags().StringVarP(&validateConfigArg, "config", "c", "", "the path of the config file") validateCmd.Flags().StringVarP(&validateModeArg, "mode", "m", "", "the mode to validate the config for") - helpers.Should(validateCmd.MarkFlagRequired("mode")) - rootCmd.AddCommand(validateCmd) + cli.Should(validateCmd.MarkFlagRequired("mode")) + addCommand(validateCmd) } diff --git a/cmd/osctl/cmd/root.go b/cmd/osctl/cmd/root.go index f761103b7..33b899cdf 100644 --- a/cmd/osctl/cmd/root.go +++ b/cmd/osctl/cmd/root.go @@ -5,39 +5,15 @@ package cmd import ( - "context" "fmt" "os" "strings" "github.com/spf13/cobra" - "github.com/talos-systems/talos/cmd/osctl/pkg/client" + "github.com/talos-systems/talos/cmd/osctl/cmd/mgmt" + "github.com/talos-systems/talos/cmd/osctl/cmd/talos" "github.com/talos-systems/talos/cmd/osctl/pkg/client/config" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" - "github.com/talos-systems/talos/pkg/constants" - "github.com/talos-systems/talos/pkg/grpc/tls" - "github.com/talos-systems/talos/pkg/version" -) - -var ( - ca string - crt string - additionalSANs []string - csr string - caHours int - crtHours int - ip string - key string - kubernetes bool - useCRI bool - name string - organization string - rsa bool - talosconfig string - endpoints []string - nodes []string - cmdcontext string ) // rootCmd represents the base command when called without any subcommands @@ -58,10 +34,10 @@ func Execute() error { return err } - rootCmd.PersistentFlags().StringVar(&talosconfig, "talosconfig", defaultTalosConfig, "The path to the Talos configuration file") - rootCmd.PersistentFlags().StringVar(&cmdcontext, "context", "", "Context to be used in command") - rootCmd.PersistentFlags().StringSliceVarP(&nodes, "nodes", "n", []string{}, "target the specified nodes") - rootCmd.PersistentFlags().StringSliceVarP(&endpoints, "endpoints", "e", []string{}, "override default endpoints in Talos configuration") + rootCmd.PersistentFlags().StringVar(&talos.Talosconfig, "talosconfig", defaultTalosConfig, "The path to the Talos configuration file") + rootCmd.PersistentFlags().StringVar(&talos.Cmdcontext, "context", "", "Context to be used in command") + rootCmd.PersistentFlags().StringSliceVarP(&talos.Nodes, "nodes", "n", []string{}, "target the specified nodes") + rootCmd.PersistentFlags().StringSliceVarP(&talos.Endpoints, "endpoints", "e", []string{}, "override default endpoints in Talos configuration") cmd, err := rootCmd.ExecuteC() if err != nil { @@ -79,58 +55,8 @@ func Execute() error { return err } -// WithClient wraps common code to initialize Talos client and provide cancellable context. -func WithClient(action func(context.Context, *client.Client) error) error { - return helpers.WithCLIContext(context.Background(), func(ctx context.Context) error { - configContext, creds, err := client.NewClientContextAndCredentialsFromConfig(talosconfig, cmdcontext) - if err != nil { - return fmt.Errorf("error getting client credentials: %w", err) - } - - configEndpoints := configContext.Endpoints - - if len(endpoints) > 0 { - // override endpoints from command-line flags - configEndpoints = endpoints - } - - targetNodes := configContext.Nodes - - if len(nodes) > 0 { - targetNodes = nodes - } - - // Update context with grpc metadata for proxy/relay requests - ctx = client.WithNodes(ctx, targetNodes...) - - tlsconfig, err := tls.New( - tls.WithKeypair(creds.Crt), - tls.WithClientAuthType(tls.Mutual), - tls.WithCACertPEM(creds.CA), - ) - if err != nil { - return err - } - - c, err := client.NewClient(tlsconfig, configEndpoints, constants.ApidPort) - if err != nil { - return fmt.Errorf("error constructing client: %w", err) - } - // nolint: errcheck - defer c.Close() - - return action(ctx, c) - }) -} - -func defaultImage(image string) string { - return fmt.Sprintf("%s:%s", image, getEnv("TAG", version.Tag)) -} - -func getEnv(key, fallback string) string { - if value, ok := os.LookupEnv(key); ok { - return value +func init() { + for _, cmd := range append(talos.Commands, mgmt.Commands...) { + rootCmd.AddCommand(cmd) } - - return fallback } diff --git a/cmd/osctl/cmd/talos/config.go b/cmd/osctl/cmd/talos/config.go new file mode 100644 index 000000000..6d5b82ccf --- /dev/null +++ b/cmd/osctl/cmd/talos/config.go @@ -0,0 +1,172 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package talos + +import ( + "encoding/base64" + "fmt" + "io/ioutil" + + "github.com/spf13/cobra" + + clientconfig "github.com/talos-systems/talos/cmd/osctl/pkg/client/config" + "github.com/talos-systems/talos/pkg/cli" +) + +var ( + ca string + crt string + key string +) + +// configCmd represents the config command. +var configCmd = &cobra.Command{ + Use: "config", + Short: "Manage the client configuration", + Long: ``, +} + +// configEndpointCmd represents the config endpoint command. +var configEndpointCmd = &cobra.Command{ + Use: "endpoint ...", + Aliases: []string{"endpoints"}, + Short: "Set the endpoint(s) for the current context", + Long: ``, + Args: cobra.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + c, err := clientconfig.Open(Talosconfig) + if err != nil { + return fmt.Errorf("error reading config: %w", err) + } + if c.Context == "" { + return fmt.Errorf("no context is set") + } + + c.Contexts[c.Context].Endpoints = args + if err := c.Save(Talosconfig); err != nil { + return fmt.Errorf("error writing config: %w", err) + } + + return nil + }, +} + +// configNodeCmd represents the config node command. +var configNodeCmd = &cobra.Command{ + Use: "node ...", + Aliases: []string{"nodes"}, + Short: "Set the node(s) for the current context", + Long: ``, + Args: cobra.ArbitraryArgs, + RunE: func(cmd *cobra.Command, args []string) error { + c, err := clientconfig.Open(Talosconfig) + if err != nil { + return fmt.Errorf("error reading config: %w", err) + } + if c.Context == "" { + return fmt.Errorf("no context is set") + } + + c.Contexts[c.Context].Nodes = args + if err := c.Save(Talosconfig); err != nil { + return fmt.Errorf("error writing config: %w", err) + } + + return nil + }, +} + +// configContextCmd represents the configc context command. +var configContextCmd = &cobra.Command{ + Use: "context ", + Short: "Set the current context", + Long: ``, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + context := args[0] + + c, err := clientconfig.Open(Talosconfig) + if err != nil { + return fmt.Errorf("error reading config: %w", err) + } + + c.Context = context + + if err := c.Save(Talosconfig); err != nil { + return fmt.Errorf("error writing config: %s", err) + } + + return nil + }, +} + +// configAddCmd represents the config add command. +var configAddCmd = &cobra.Command{ + Use: "add ", + Short: "Add a new context", + Long: ``, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + context := args[0] + c, err := clientconfig.Open(Talosconfig) + if err != nil { + return fmt.Errorf("error reading config: %w", err) + } + + caBytes, err := ioutil.ReadFile(ca) + if err != nil { + return fmt.Errorf("error reading CA: %w", err) + } + + crtBytes, err := ioutil.ReadFile(crt) + if err != nil { + return fmt.Errorf("error reading certificate: %w", err) + } + + keyBytes, err := ioutil.ReadFile(key) + if err != nil { + return fmt.Errorf("error reading key: %w", err) + } + + newContext := &clientconfig.Context{ + CA: base64.StdEncoding.EncodeToString(caBytes), + Crt: base64.StdEncoding.EncodeToString(crtBytes), + Key: base64.StdEncoding.EncodeToString(keyBytes), + } + + if c.Contexts == nil { + c.Contexts = map[string]*clientconfig.Context{} + } + + c.Contexts[context] = newContext + if err := c.Save(Talosconfig); err != nil { + return fmt.Errorf("error writing config: %w", err) + } + + return nil + }, +} + +// configGenerateCmd represents the config generate stub command. +var configGenerateCmd = &cobra.Command{ + Use: "generate", + Short: "Generate Talos config", + Long: ``, + Hidden: true, + RunE: func(cmd *cobra.Command, args []string) error { + return fmt.Errorf("'osctl config generate' was renamed to 'osctl gen config'") + }, +} + +func init() { + configCmd.AddCommand(configContextCmd, configEndpointCmd, configNodeCmd, configAddCmd, configGenerateCmd) + configAddCmd.Flags().StringVar(&ca, "ca", "", "the path to the CA certificate") + configAddCmd.Flags().StringVar(&crt, "crt", "", "the path to the certificate") + configAddCmd.Flags().StringVar(&key, "key", "", "the path to the key") + cli.Should(configAddCmd.MarkFlagRequired("ca")) + cli.Should(configAddCmd.MarkFlagRequired("crt")) + cli.Should(configAddCmd.MarkFlagRequired("key")) + addCommand(configCmd) +} diff --git a/cmd/osctl/cmd/containers.go b/cmd/osctl/cmd/talos/containers.go similarity index 93% rename from cmd/osctl/cmd/containers.go rename to cmd/osctl/cmd/talos/containers.go index a7d414a7c..9a9c0c9cc 100644 --- a/cmd/osctl/cmd/containers.go +++ b/cmd/osctl/cmd/talos/containers.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -20,7 +20,8 @@ import ( "github.com/talos-systems/talos/api/common" osapi "github.com/talos-systems/talos/api/os" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" + "github.com/talos-systems/talos/pkg/cli" "github.com/talos-systems/talos/pkg/constants" ) @@ -52,7 +53,7 @@ var containersCmd = &cobra.Command{ return fmt.Errorf("error getting container list: %s", err) } - helpers.Warning("%s", err) + cli.Warning("%s", err) } return containerRender(&remotePeer, resp) @@ -96,5 +97,5 @@ func containerRender(remotePeer *peer.Peer, resp *osapi.ContainersResponse) erro func init() { containersCmd.Flags().BoolVarP(&kubernetes, "kubernetes", "k", false, "use the k8s.io containerd namespace") containersCmd.Flags().BoolVarP(&useCRI, "use-cri", "c", false, "use the CRI driver") - rootCmd.AddCommand(containersCmd) + addCommand(containersCmd) } diff --git a/cmd/osctl/cmd/copy.go b/cmd/osctl/cmd/talos/copy.go similarity index 95% rename from cmd/osctl/cmd/copy.go rename to cmd/osctl/cmd/talos/copy.go index 8390eb78e..fa0f73142 100644 --- a/cmd/osctl/cmd/copy.go +++ b/cmd/osctl/cmd/talos/copy.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -15,7 +15,7 @@ import ( "github.com/spf13/cobra" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" ) // cpCmd represents the cp command @@ -83,5 +83,5 @@ captures ownership and permission bits.`, } func init() { - rootCmd.AddCommand(cpCmd) + addCommand(cpCmd) } diff --git a/cmd/osctl/cmd/dmesg.go b/cmd/osctl/cmd/talos/dmesg.go similarity index 94% rename from cmd/osctl/cmd/dmesg.go rename to cmd/osctl/cmd/talos/dmesg.go index fb3e340d2..59d495fe9 100644 --- a/cmd/osctl/cmd/dmesg.go +++ b/cmd/osctl/cmd/talos/dmesg.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -15,7 +15,7 @@ import ( "google.golang.org/grpc/status" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" ) var dmesgTail bool @@ -65,7 +65,7 @@ var dmesgCmd = &cobra.Command{ } func init() { - rootCmd.AddCommand(dmesgCmd) + addCommand(dmesgCmd) dmesgCmd.Flags().BoolVarP(&follow, "follow", "f", false, "specify if the kernel log should be streamed") dmesgCmd.Flags().BoolVarP(&dmesgTail, "tail", "", false, "specify if only new messages should be sent (makes sense only when combined with --follow)") } diff --git a/cmd/osctl/cmd/interfaces.go b/cmd/osctl/cmd/talos/interfaces.go similarity index 90% rename from cmd/osctl/cmd/interfaces.go rename to cmd/osctl/cmd/talos/interfaces.go index 1fa22f2ab..f4f36d11b 100644 --- a/cmd/osctl/cmd/interfaces.go +++ b/cmd/osctl/cmd/talos/interfaces.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -16,7 +16,8 @@ import ( networkapi "github.com/talos-systems/talos/api/network" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" + "github.com/talos-systems/talos/pkg/cli" ) // interfacesCmd represents the net interfaces command @@ -35,7 +36,7 @@ var interfacesCmd = &cobra.Command{ return fmt.Errorf("error getting interfaces: %s", err) } - helpers.Warning("%s", err) + cli.Warning("%s", err) } return intersRender(&remotePeer, resp) @@ -67,5 +68,5 @@ func intersRender(remotePeer *peer.Peer, resp *networkapi.InterfacesResponse) er } func init() { - rootCmd.AddCommand(interfacesCmd) + addCommand(interfacesCmd) } diff --git a/cmd/osctl/cmd/kubeconfig.go b/cmd/osctl/cmd/talos/kubeconfig.go similarity index 94% rename from cmd/osctl/cmd/kubeconfig.go rename to cmd/osctl/cmd/talos/kubeconfig.go index f51f5ec7e..2bd8ee38a 100644 --- a/cmd/osctl/cmd/kubeconfig.go +++ b/cmd/osctl/cmd/talos/kubeconfig.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -14,7 +14,7 @@ import ( "github.com/spf13/cobra" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" ) var force bool @@ -74,5 +74,5 @@ Kubeconfig will be written to PWD/kubeconfig or [local-path]/kubeconfig if speci func init() { kubeconfigCmd.Flags().BoolVarP(&force, "force", "f", false, "Force overwrite of kubeconfig if already present") - rootCmd.AddCommand(kubeconfigCmd) + addCommand(kubeconfigCmd) } diff --git a/cmd/osctl/cmd/list.go b/cmd/osctl/cmd/talos/list.go similarity index 97% rename from cmd/osctl/cmd/list.go rename to cmd/osctl/cmd/talos/list.go index c554ab5da..e1e982066 100644 --- a/cmd/osctl/cmd/list.go +++ b/cmd/osctl/cmd/talos/list.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -19,7 +19,7 @@ import ( machineapi "github.com/talos-systems/talos/api/machine" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" ) const sixMonths = 6 * time.Hour * 24 * 30 @@ -171,5 +171,5 @@ func init() { lsCmd.Flags().BoolVarP(&recurse, "recurse", "r", false, "recurse into subdirectories") lsCmd.Flags().BoolVarP(&humanizeFlag, "humanize", "H", false, "humanize size and time in the output") lsCmd.Flags().Int32VarP(&recursionDepth, "depth", "d", 0, "maximum recursion depth") - rootCmd.AddCommand(lsCmd) + addCommand(lsCmd) } diff --git a/cmd/osctl/cmd/logs.go b/cmd/osctl/cmd/talos/logs.go similarity index 96% rename from cmd/osctl/cmd/logs.go rename to cmd/osctl/cmd/talos/logs.go index a9a9d2527..fc23219d8 100644 --- a/cmd/osctl/cmd/logs.go +++ b/cmd/osctl/cmd/talos/logs.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "bufio" @@ -20,7 +20,8 @@ import ( "github.com/talos-systems/talos/api/common" "github.com/talos-systems/talos/api/machine" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" + "github.com/talos-systems/talos/pkg/cli" "github.com/talos-systems/talos/pkg/constants" ) @@ -177,7 +178,7 @@ func (slicer *lineSlicer) run(stream machine.MachineService_LogsClient) { } _, err = slicer.getPipe(node).Write(data.Bytes) - helpers.Should(err) + cli.Should(err) } } @@ -186,5 +187,5 @@ func init() { logsCmd.Flags().BoolVarP(&useCRI, "use-cri", "c", false, "use the CRI driver") logsCmd.Flags().BoolVarP(&follow, "follow", "f", false, "specify if the logs should be streamed") logsCmd.Flags().Int32VarP(&tailLines, "tail", "", -1, "lines of log file to display (default is to show from the beginning)") - rootCmd.AddCommand(logsCmd) + addCommand(logsCmd) } diff --git a/cmd/osctl/cmd/memory.go b/cmd/osctl/cmd/talos/memory.go similarity index 97% rename from cmd/osctl/cmd/memory.go rename to cmd/osctl/cmd/talos/memory.go index 3d2a9b54c..f8aa7a06f 100644 --- a/cmd/osctl/cmd/memory.go +++ b/cmd/osctl/cmd/talos/memory.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -16,7 +16,8 @@ import ( osapi "github.com/talos-systems/talos/api/os" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" + "github.com/talos-systems/talos/pkg/cli" ) var verbose bool @@ -38,7 +39,7 @@ var memoryCmd = &cobra.Command{ return fmt.Errorf("error getting memory stats: %s", err) } - helpers.Warning("%s", err) + cli.Warning("%s", err) } if verbose { @@ -146,5 +147,5 @@ func verboseRender(remotePeer *peer.Peer, resp *osapi.MemoryResponse) error { func init() { memoryCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "display extended memory statistics") - rootCmd.AddCommand(memoryCmd) + addCommand(memoryCmd) } diff --git a/cmd/osctl/cmd/mounts.go b/cmd/osctl/cmd/talos/mounts.go similarity index 91% rename from cmd/osctl/cmd/mounts.go rename to cmd/osctl/cmd/talos/mounts.go index 401572621..c6e617ade 100644 --- a/cmd/osctl/cmd/mounts.go +++ b/cmd/osctl/cmd/talos/mounts.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -17,7 +17,8 @@ import ( machineapi "github.com/talos-systems/talos/api/machine" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" + "github.com/talos-systems/talos/pkg/cli" ) // mountsCmd represents the mounts command. @@ -37,7 +38,7 @@ var mountsCmd = &cobra.Command{ return fmt.Errorf("error getting interfaces: %s", err) } - helpers.Warning("%s", err) + cli.Warning("%s", err) } return mountsRender(&remotePeer, resp) @@ -74,5 +75,5 @@ func mountsRender(remotePeer *peer.Peer, resp *machineapi.MountsResponse) error } func init() { - rootCmd.AddCommand(mountsCmd) + addCommand(mountsCmd) } diff --git a/cmd/osctl/cmd/processes.go b/cmd/osctl/cmd/talos/processes.go similarity index 96% rename from cmd/osctl/cmd/processes.go rename to cmd/osctl/cmd/talos/processes.go index dd5dc1d84..225b9fea9 100644 --- a/cmd/osctl/cmd/processes.go +++ b/cmd/osctl/cmd/talos/processes.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -23,7 +23,8 @@ import ( osapi "github.com/talos-systems/talos/api/os" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" + "github.com/talos-systems/talos/pkg/cli" ) var ( @@ -69,7 +70,7 @@ var processesCmd = &cobra.Command{ func init() { processesCmd.Flags().StringVarP(&sortMethod, "sort", "s", "rss", "Column to sort output by. [rss|cpu]") processesCmd.Flags().BoolVarP(&watchProcesses, "watch", "w", false, "Stream running processes") - rootCmd.AddCommand(processesCmd) + addCommand(processesCmd) } // nolint: gocyclo @@ -87,12 +88,12 @@ func processesUI(ctx context.Context, c *client.Client) { // Since we're getting this data on each call // we'll be able to handle terminal window resizing w, h, err := terminal.GetSize(0) - helpers.Should(err) + cli.Should(err) // x, y, w, h l.SetRect(0, 0, w, h) processOutput, err = processesOutput(ctx, c) - helpers.Should(err) + cli.Should(err) // Dont refresh if we dont have any output if processOutput == "" { diff --git a/cmd/osctl/cmd/read.go b/cmd/osctl/cmd/talos/read.go similarity index 92% rename from cmd/osctl/cmd/read.go rename to cmd/osctl/cmd/talos/read.go index 615605927..1d6f4990a 100644 --- a/cmd/osctl/cmd/read.go +++ b/cmd/osctl/cmd/talos/read.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -14,7 +14,7 @@ import ( "github.com/spf13/cobra" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" ) // readCmd represents the read command @@ -59,5 +59,5 @@ var readCmd = &cobra.Command{ } func init() { - rootCmd.AddCommand(readCmd) + addCommand(readCmd) } diff --git a/cmd/osctl/cmd/reboot.go b/cmd/osctl/cmd/talos/reboot.go similarity index 94% rename from cmd/osctl/cmd/reboot.go rename to cmd/osctl/cmd/talos/reboot.go index c9401f555..d457d4199 100644 --- a/cmd/osctl/cmd/reboot.go +++ b/cmd/osctl/cmd/talos/reboot.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -31,5 +31,5 @@ var rebootCmd = &cobra.Command{ } func init() { - rootCmd.AddCommand(rebootCmd) + addCommand(rebootCmd) } diff --git a/cmd/osctl/cmd/reset.go b/cmd/osctl/cmd/talos/reset.go similarity index 96% rename from cmd/osctl/cmd/reset.go rename to cmd/osctl/cmd/talos/reset.go index 97ac2e891..a9cd8ab71 100644 --- a/cmd/osctl/cmd/reset.go +++ b/cmd/osctl/cmd/talos/reset.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -38,5 +38,5 @@ var resetCmd = &cobra.Command{ func init() { resetCmd.Flags().BoolVar(&graceful, "graceful", true, "if true, attempt to cordon/drain node and leave etcd (if applicable)") resetCmd.Flags().BoolVar(&reboot, "reboot", false, "if true, reboot the node after resetting instead of shutting down") - rootCmd.AddCommand(resetCmd) + addCommand(resetCmd) } diff --git a/cmd/osctl/cmd/restart.go b/cmd/osctl/cmd/talos/restart.go similarity index 96% rename from cmd/osctl/cmd/restart.go rename to cmd/osctl/cmd/talos/restart.go index 4322f80d2..f8b701626 100644 --- a/cmd/osctl/cmd/restart.go +++ b/cmd/osctl/cmd/talos/restart.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -46,5 +46,5 @@ var restartCmd = &cobra.Command{ func init() { restartCmd.Flags().BoolVarP(&kubernetes, "kubernetes", "k", false, "use the k8s.io containerd namespace") restartCmd.Flags().BoolVarP(&useCRI, "use-cri", "c", false, "use the CRI driver") - rootCmd.AddCommand(restartCmd) + addCommand(restartCmd) } diff --git a/cmd/osctl/cmd/talos/root.go b/cmd/osctl/cmd/talos/root.go new file mode 100644 index 000000000..7da96b96f --- /dev/null +++ b/cmd/osctl/cmd/talos/root.go @@ -0,0 +1,81 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package talos + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + + "github.com/talos-systems/talos/cmd/osctl/pkg/client" + "github.com/talos-systems/talos/pkg/cli" + "github.com/talos-systems/talos/pkg/constants" + "github.com/talos-systems/talos/pkg/grpc/tls" +) + +var ( + kubernetes bool + useCRI bool +) + +// Common options set on root command +var ( + Talosconfig string + Endpoints []string + Nodes []string + Cmdcontext string +) + +// WithClient wraps common code to initialize Talos client and provide cancellable context. +func WithClient(action func(context.Context, *client.Client) error) error { + return cli.WithContext(context.Background(), func(ctx context.Context) error { + configContext, creds, err := client.NewClientContextAndCredentialsFromConfig(Talosconfig, Cmdcontext) + if err != nil { + return fmt.Errorf("error getting client credentials: %w", err) + } + + configEndpoints := configContext.Endpoints + + if len(Endpoints) > 0 { + // override endpoints from command-line flags + configEndpoints = Endpoints + } + + targetNodes := configContext.Nodes + + if len(Nodes) > 0 { + targetNodes = Nodes + } + + // Update context with grpc metadata for proxy/relay requests + ctx = client.WithNodes(ctx, targetNodes...) + + tlsconfig, err := tls.New( + tls.WithKeypair(creds.Crt), + tls.WithClientAuthType(tls.Mutual), + tls.WithCACertPEM(creds.CA), + ) + if err != nil { + return err + } + + c, err := client.NewClient(tlsconfig, configEndpoints, constants.ApidPort) + if err != nil { + return fmt.Errorf("error constructing client: %w", err) + } + // nolint: errcheck + defer c.Close() + + return action(ctx, c) + }) +} + +// Commands is a list of commands published by the package +var Commands []*cobra.Command + +func addCommand(cmd *cobra.Command) { + Commands = append(Commands, cmd) +} diff --git a/cmd/osctl/cmd/routes.go b/cmd/osctl/cmd/talos/routes.go similarity index 90% rename from cmd/osctl/cmd/routes.go rename to cmd/osctl/cmd/talos/routes.go index 78b708908..534900a62 100644 --- a/cmd/osctl/cmd/routes.go +++ b/cmd/osctl/cmd/talos/routes.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -16,7 +16,8 @@ import ( networkapi "github.com/talos-systems/talos/api/network" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" + "github.com/talos-systems/talos/pkg/cli" ) // routesCmd represents the net routes command @@ -33,7 +34,7 @@ var routesCmd = &cobra.Command{ if resp == nil { return fmt.Errorf("error getting routes: %w", err) } - helpers.Warning("%s", err) + cli.Warning("%s", err) } return routesRender(&remotePeer, resp) @@ -63,5 +64,5 @@ func routesRender(remotePeer *peer.Peer, resp *networkapi.RoutesResponse) error } func init() { - rootCmd.AddCommand(routesCmd) + addCommand(routesCmd) } diff --git a/cmd/osctl/cmd/service.go b/cmd/osctl/cmd/talos/service.go similarity index 95% rename from cmd/osctl/cmd/service.go rename to cmd/osctl/cmd/talos/service.go index cf1ddbeb2..58c59e13d 100644 --- a/cmd/osctl/cmd/service.go +++ b/cmd/osctl/cmd/talos/service.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -18,7 +18,8 @@ import ( machineapi "github.com/talos-systems/talos/api/machine" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" + "github.com/talos-systems/talos/pkg/cli" ) // serviceCmd represents the service command @@ -72,7 +73,7 @@ func serviceList(ctx context.Context, c *client.Client) error { return fmt.Errorf("error listing services: %w", err) } - helpers.Warning("%s", err) + cli.Warning("%s", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) @@ -106,7 +107,7 @@ func serviceInfo(ctx context.Context, c *client.Client, id string) error { return fmt.Errorf("error listing services: %w", err) } - helpers.Warning("%s", err) + cli.Warning("%s", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) @@ -159,7 +160,7 @@ func serviceStart(ctx context.Context, c *client.Client, id string) error { return fmt.Errorf("error starting service: %w", err) } - helpers.Warning("%s", err) + cli.Warning("%s", err) } defaultNode := helpers.AddrFromPeer(&remotePeer) @@ -189,7 +190,7 @@ func serviceStop(ctx context.Context, c *client.Client, id string) error { return fmt.Errorf("error starting service: %w", err) } - helpers.Warning("%s", err) + cli.Warning("%s", err) } defaultNode := helpers.AddrFromPeer(&remotePeer) @@ -219,7 +220,7 @@ func serviceRestart(ctx context.Context, c *client.Client, id string) error { return fmt.Errorf("error starting service: %w", err) } - helpers.Warning("%s", err) + cli.Warning("%s", err) } defaultNode := helpers.AddrFromPeer(&remotePeer) @@ -276,5 +277,5 @@ func (svc serviceInfoWrapper) HealthStatus() string { } func init() { - rootCmd.AddCommand(serviceCmd) + addCommand(serviceCmd) } diff --git a/cmd/osctl/cmd/shutdown.go b/cmd/osctl/cmd/talos/shutdown.go similarity index 94% rename from cmd/osctl/cmd/shutdown.go rename to cmd/osctl/cmd/talos/shutdown.go index bda59a59b..40fde3154 100644 --- a/cmd/osctl/cmd/shutdown.go +++ b/cmd/osctl/cmd/talos/shutdown.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -31,5 +31,5 @@ var shutdownCmd = &cobra.Command{ } func init() { - rootCmd.AddCommand(shutdownCmd) + addCommand(shutdownCmd) } diff --git a/cmd/osctl/cmd/stats.go b/cmd/osctl/cmd/talos/stats.go similarity index 93% rename from cmd/osctl/cmd/stats.go rename to cmd/osctl/cmd/talos/stats.go index 8b4644b0f..116a399d1 100644 --- a/cmd/osctl/cmd/stats.go +++ b/cmd/osctl/cmd/talos/stats.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -20,7 +20,8 @@ import ( "github.com/talos-systems/talos/api/common" osapi "github.com/talos-systems/talos/api/os" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" + "github.com/talos-systems/talos/pkg/cli" "github.com/talos-systems/talos/pkg/constants" ) @@ -51,7 +52,7 @@ var statsCmd = &cobra.Command{ return fmt.Errorf("error getting stats: %s", err) } - helpers.Warning("%s", err) + cli.Warning("%s", err) } return statsRender(&remotePeer, resp) @@ -96,5 +97,5 @@ func statsRender(remotePeer *peer.Peer, resp *osapi.StatsResponse) error { func init() { statsCmd.Flags().BoolVarP(&kubernetes, "kubernetes", "k", false, "use the k8s.io containerd namespace") statsCmd.Flags().BoolVarP(&useCRI, "use-cri", "c", false, "use the CRI driver") - rootCmd.AddCommand(statsCmd) + addCommand(statsCmd) } diff --git a/cmd/osctl/cmd/time.go b/cmd/osctl/cmd/talos/time.go similarity index 92% rename from cmd/osctl/cmd/time.go rename to cmd/osctl/cmd/talos/time.go index 19f0d8882..81857f098 100644 --- a/cmd/osctl/cmd/time.go +++ b/cmd/osctl/cmd/talos/time.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -18,7 +18,8 @@ import ( timeapi "github.com/talos-systems/talos/api/time" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" + "github.com/talos-systems/talos/pkg/cli" ) // timeCmd represents the time command @@ -50,7 +51,7 @@ var timeCmd = &cobra.Command{ return fmt.Errorf("error fetching time: %w", err) } - helpers.Warning("%s", err) + cli.Warning("%s", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) @@ -85,5 +86,5 @@ var timeCmd = &cobra.Command{ func init() { timeCmd.Flags().StringP("check", "c", "pool.ntp.org", "checks server time against specified ntp server") - rootCmd.AddCommand(timeCmd) + addCommand(timeCmd) } diff --git a/cmd/osctl/cmd/upgrade.go b/cmd/osctl/cmd/talos/upgrade.go similarity index 90% rename from cmd/osctl/cmd/upgrade.go rename to cmd/osctl/cmd/talos/upgrade.go index 1b28a4ba2..5df6b30c2 100644 --- a/cmd/osctl/cmd/upgrade.go +++ b/cmd/osctl/cmd/talos/upgrade.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -16,7 +16,8 @@ import ( "google.golang.org/grpc/peer" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" + "github.com/talos-systems/talos/pkg/cli" ) var upgradeImage string @@ -34,7 +35,7 @@ var upgradeCmd = &cobra.Command{ func init() { upgradeCmd.Flags().StringVarP(&upgradeImage, "image", "i", "", "the container image to use for performing the install") - rootCmd.AddCommand(upgradeCmd) + addCommand(upgradeCmd) } func upgrade() error { @@ -49,7 +50,7 @@ func upgrade() error { return fmt.Errorf("error performing upgrade: %s", err) } - helpers.Warning("%s", err) + cli.Warning("%s", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) diff --git a/cmd/osctl/cmd/version.go b/cmd/osctl/cmd/talos/version.go similarity index 90% rename from cmd/osctl/cmd/version.go rename to cmd/osctl/cmd/talos/version.go index 32815743f..11aa7b643 100644 --- a/cmd/osctl/cmd/version.go +++ b/cmd/osctl/cmd/talos/version.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package cmd +package talos import ( "context" @@ -13,7 +13,8 @@ import ( "google.golang.org/grpc/peer" "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/talos/helpers" + "github.com/talos-systems/talos/pkg/cli" "github.com/talos-systems/talos/pkg/version" ) @@ -50,7 +51,7 @@ var versionCmd = &cobra.Command{ if resp == nil { return fmt.Errorf("error getting version: %s", err) } - helpers.Warning("%s", err) + cli.Warning("%s", err) } defaultNode := helpers.AddrFromPeer(&remotePeer) @@ -76,5 +77,5 @@ func init() { versionCmd.Flags().BoolVar(&shortVersion, "short", false, "Print the short version") versionCmd.Flags().BoolVar(&clientOnly, "client", false, "Print client version only") - rootCmd.AddCommand(versionCmd) + addCommand(versionCmd) } diff --git a/cmd/osctl/main.go b/cmd/osctl/main.go index ed90e141c..b367ee334 100644 --- a/cmd/osctl/main.go +++ b/cmd/osctl/main.go @@ -8,12 +8,12 @@ import ( "os" "github.com/talos-systems/talos/cmd/osctl/cmd" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/pkg/cli" "github.com/talos-systems/talos/pkg/startup" ) func main() { - helpers.Should(startup.RandSeed()) + cli.Should(startup.RandSeed()) if err := cmd.Execute(); err != nil { os.Exit(1) diff --git a/cmd/osctl/pkg/helpers/artifacts.go b/cmd/osctl/pkg/mgmt/helpers/artifacts.go similarity index 100% rename from cmd/osctl/pkg/helpers/artifacts.go rename to cmd/osctl/pkg/mgmt/helpers/artifacts.go diff --git a/cmd/osctl/pkg/helpers/helpers_test.go b/cmd/osctl/pkg/mgmt/helpers/helpers_test.go similarity index 100% rename from cmd/osctl/pkg/helpers/helpers_test.go rename to cmd/osctl/pkg/mgmt/helpers/helpers_test.go diff --git a/cmd/osctl/pkg/mgmt/helpers/image.go b/cmd/osctl/pkg/mgmt/helpers/image.go new file mode 100644 index 000000000..494b0d95c --- /dev/null +++ b/cmd/osctl/pkg/mgmt/helpers/image.go @@ -0,0 +1,25 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package helpers + +import ( + "fmt" + "os" + + "github.com/talos-systems/talos/pkg/version" +) + +// DefaultImage appends default image version. +func DefaultImage(image string) string { + return fmt.Sprintf("%s:%s", image, getEnv("TAG", version.Tag)) +} + +func getEnv(key, fallback string) string { + if value, ok := os.LookupEnv(key); ok { + return value + } + + return fallback +} diff --git a/cmd/osctl/pkg/helpers/archive.go b/cmd/osctl/pkg/talos/helpers/archive.go similarity index 100% rename from cmd/osctl/pkg/helpers/archive.go rename to cmd/osctl/pkg/talos/helpers/archive.go diff --git a/cmd/osctl/pkg/helpers/checks.go b/cmd/osctl/pkg/talos/helpers/checks.go similarity index 100% rename from cmd/osctl/pkg/helpers/checks.go rename to cmd/osctl/pkg/talos/helpers/checks.go diff --git a/cmd/osctl/pkg/talos/helpers/helpers_test.go b/cmd/osctl/pkg/talos/helpers/helpers_test.go new file mode 100644 index 000000000..5ea27158f --- /dev/null +++ b/cmd/osctl/pkg/talos/helpers/helpers_test.go @@ -0,0 +1,14 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package helpers_test + +import "testing" + +func TestEmpty(t *testing.T) { + // added for accurate coverage estimation + // + // please remove it once any unit-test is added + // for this package +} diff --git a/cmd/osctl/pkg/helpers/peer.go b/cmd/osctl/pkg/talos/helpers/peer.go similarity index 100% rename from cmd/osctl/pkg/helpers/peer.go rename to cmd/osctl/pkg/talos/helpers/peer.go diff --git a/docs/osctl/osctl_config.md b/docs/osctl/osctl_config.md index 843ecba45..8f4a0ea93 100644 --- a/docs/osctl/osctl_config.md +++ b/docs/osctl/osctl_config.md @@ -28,6 +28,5 @@ Manage the client configuration * [osctl config add](osctl_config_add.md) - Add a new context * [osctl config context](osctl_config_context.md) - Set the current context * [osctl config endpoint](osctl_config_endpoint.md) - Set the endpoint(s) for the current context -* [osctl config generate](osctl_config_generate.md) - Generate a set of configuration files * [osctl config node](osctl_config_node.md) - Set the node(s) for the current context diff --git a/docs/osctl/osctl_gen.md b/docs/osctl/osctl_gen.md index 64ac032cf..ad7b51552 100644 --- a/docs/osctl/osctl_gen.md +++ b/docs/osctl/osctl_gen.md @@ -26,6 +26,7 @@ Generate CAs, certificates, and private keys * [osctl](osctl.md) - A CLI for out-of-band management of Kubernetes nodes created by Talos * [osctl gen ca](osctl_gen_ca.md) - Generates a self-signed X.509 certificate authority +* [osctl gen config](osctl_gen_config.md) - Generates a set of configuration files for Talos cluster * [osctl gen crt](osctl_gen_crt.md) - Generates an X.509 Ed25519 certificate * [osctl gen csr](osctl_gen_csr.md) - Generates a CSR using an Ed25519 private key * [osctl gen key](osctl_gen_key.md) - Generates an Ed25519 private key diff --git a/docs/osctl/osctl_config_generate.md b/docs/osctl/osctl_gen_config.md similarity index 78% rename from docs/osctl/osctl_config_generate.md rename to docs/osctl/osctl_gen_config.md index 55a6d758d..bf2616e9b 100644 --- a/docs/osctl/osctl_config_generate.md +++ b/docs/osctl/osctl_gen_config.md @@ -1,14 +1,14 @@ -## osctl config generate +## osctl gen config -Generate a set of configuration files +Generates a set of configuration files for Talos cluster ### Synopsis -Generate a set of configuration files +Generates a set of configuration files for Talos cluster ``` -osctl config generate https:// [flags] +osctl gen config https:// [flags] ``` ### Options @@ -16,7 +16,7 @@ osctl config generate https:// [fla ``` --additional-sans strings additional Subject-Alt-Names for the APIServer certificate --dns-domain string the dns domain to use for cluster (default "cluster.local") - -h, --help help for generate + -h, --help help for config --install-disk string the disk to install to (default "/dev/sda") --install-image string the image used to perform an installation (default "docker.io/autonomy/installer:latest") --kubernetes-version string desired kubernetes version to run (default "1.17.1") @@ -36,5 +36,5 @@ osctl config generate https:// [fla ### SEE ALSO -* [osctl config](osctl_config.md) - Manage the client configuration +* [osctl gen](osctl_gen.md) - Generate CAs, certificates, and private keys diff --git a/internal/integration/cli/validate.go b/internal/integration/cli/validate.go index ce6854a57..1fee0533d 100644 --- a/internal/integration/cli/validate.go +++ b/internal/integration/cli/validate.go @@ -44,7 +44,7 @@ func (suite *ValidateSuite) TearDownTest() { // TestValidate generates config and validates it for all the modes. func (suite *ValidateSuite) TestValidate() { - suite.RunOsctl([]string{"config", "generate", "foobar", "https://10.0.0.1"}) + suite.RunOsctl([]string{"gen", "config", "foobar", "https://10.0.0.1"}) for _, configFile := range []string{"init.yaml", "controlplane.yaml", "join.yaml"} { for _, mode := range []string{"Cloud", "Container", "Interactive", "Metal"} { diff --git a/internal/integration/provision/upgrade.go b/internal/integration/provision/upgrade.go index 5d18065e1..0cb8be6bf 100644 --- a/internal/integration/provision/upgrade.go +++ b/internal/integration/provision/upgrade.go @@ -22,7 +22,7 @@ import ( machineapi "github.com/talos-systems/talos/api/machine" talosclient "github.com/talos-systems/talos/cmd/osctl/pkg/client" - "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" + "github.com/talos-systems/talos/cmd/osctl/pkg/mgmt/helpers" "github.com/talos-systems/talos/internal/integration/base" "github.com/talos-systems/talos/internal/pkg/provision" "github.com/talos-systems/talos/internal/pkg/provision/access" diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go new file mode 100644 index 000000000..f4bcfd84e --- /dev/null +++ b/pkg/cli/cli.go @@ -0,0 +1,6 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// Package cli provides utilities for CLI tools. +package cli diff --git a/cmd/osctl/pkg/helpers/context.go b/pkg/cli/context.go similarity index 83% rename from cmd/osctl/pkg/helpers/context.go rename to pkg/cli/context.go index e9e3d2d2c..0e8f84caa 100644 --- a/cmd/osctl/pkg/helpers/context.go +++ b/pkg/cli/context.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package helpers +package cli import ( "context" @@ -12,8 +12,8 @@ import ( "syscall" ) -// WithCLIContext wraps function call to provide a context cancellable with ^C. -func WithCLIContext(ctx context.Context, f func(context.Context) error) error { +// WithContext wraps function call to provide a context cancellable with ^C. +func WithContext(ctx context.Context, f func(context.Context) error) error { wrappedCtx, wrappedCtxCancel := context.WithCancel(ctx) defer wrappedCtxCancel() diff --git a/cmd/osctl/pkg/helpers/cli.go b/pkg/cli/output.go similarity index 97% rename from cmd/osctl/pkg/helpers/cli.go rename to pkg/cli/output.go index 33948f006..6f944862a 100644 --- a/cmd/osctl/pkg/helpers/cli.go +++ b/pkg/cli/output.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package helpers +package cli import ( "fmt" diff --git a/cmd/osctl/pkg/helpers/should.go b/pkg/cli/should.go similarity index 96% rename from cmd/osctl/pkg/helpers/should.go rename to pkg/cli/should.go index cb0e9fce6..963bb86fe 100644 --- a/cmd/osctl/pkg/helpers/should.go +++ b/pkg/cli/should.go @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package helpers +package cli // Should panics if err != nil //