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 //