Skip to content

Commit

Permalink
fix: add --force flag for talosctl gen
Browse files Browse the repository at this point in the history
Error out if file(s) already exists and warn user to use
`--force` to overwrite.

Fixes: #6963

Signed-off-by: Noel Georgi <git@frezbo.dev>
  • Loading branch information
frezbo committed Mar 17, 2023
1 parent ea2aa06 commit cf101e5
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 17 deletions.
14 changes: 11 additions & 3 deletions cmd/talosctl/cmd/mgmt/gen/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,23 @@ var genCACmd = &cobra.Command{
return fmt.Errorf("error generating CA: %w", err)
}

if err := os.WriteFile(genCACmdFlags.organization+".crt", ca.CrtPEM, 0o600); err != nil {
caCertFile := genCACmdFlags.organization + ".crt"
caHashFile := genCACmdFlags.organization + ".sha256"
caKeyFile := genCACmdFlags.organization + ".key"

if err := validateFilesExists([]string{caCertFile, caHashFile, caKeyFile}); err != nil {
return err
}

if err := os.WriteFile(caCertFile, ca.CrtPEM, 0o600); err != nil {
return fmt.Errorf("error writing CA certificate: %w", err)
}

if err := os.WriteFile(genCACmdFlags.organization+".sha256", []byte(x509.Hash(ca.Crt)), 0o600); err != nil {
if err := os.WriteFile(caHashFile, []byte(x509.Hash(ca.Crt)), 0o600); err != nil {
return fmt.Errorf("error writing certificate hash: %w", err)
}

if err := os.WriteFile(genCACmdFlags.organization+".key", ca.KeyPEM, 0o600); err != nil {
if err := os.WriteFile(caKeyFile, ca.KeyPEM, 0o600); err != nil {
return fmt.Errorf("error writing key: %w", err)
}

Expand Down
11 changes: 4 additions & 7 deletions cmd/talosctl/cmd/mgmt/gen/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ var genConfigCmdFlags struct {
withDocs bool
withClusterDiscovery bool
withKubeSpan bool
force bool
withSecrets string
}

Expand Down Expand Up @@ -280,6 +279,7 @@ func validateFlags() error {
return err
}

// nolint:gocyclo
func writeConfigBundle(configBundle *bundle.ConfigBundle, outputPaths configOutputPaths, commentsFlags encoder.CommentsFlags) error {
outputTypesSet := slices.ToSet(genConfigCmdFlags.outputTypes)

Expand Down Expand Up @@ -326,16 +326,14 @@ func writeToDestination(data []byte, destination string, permissions os.FileMode
return err
}

if !genConfigCmdFlags.force {
if _, err := os.Stat(destination); err == nil {
return fmt.Errorf("%s already exists, use --force to overwrite", destination)
}
if err := validateFileExists(destination); err != nil {
return err
}

parentDir := filepath.Dir(destination)

// Create dir path, ignoring "already exists" messages
if err := os.MkdirAll(parentDir, os.ModePerm); err != nil && !os.IsExist(err) {
if err := os.MkdirAll(parentDir, os.ModePerm); err != nil {
return fmt.Errorf("failed to create output dir: %w", err)
}

Expand Down Expand Up @@ -438,7 +436,6 @@ func init() {
genConfigCmd.Flags().BoolVarP(&genConfigCmdFlags.withDocs, "with-docs", "", true, "renders all machine configs adding the documentation for each field")
genConfigCmd.Flags().BoolVarP(&genConfigCmdFlags.withClusterDiscovery, "with-cluster-discovery", "", true, "enable cluster discovery feature")
genConfigCmd.Flags().BoolVarP(&genConfigCmdFlags.withKubeSpan, "with-kubespan", "", false, "enable KubeSpan feature")
genConfigCmd.Flags().BoolVarP(&genConfigCmdFlags.force, "force", "", false, "\"gen\" will overwrite existing files")
genConfigCmd.Flags().StringVar(&genConfigCmdFlags.withSecrets, "with-secrets", "", "use a secrets file generated using 'gen secrets'")

genConfigCmd.Flags().StringSliceVarP(&genConfigCmdFlags.outputTypes, "output-types", "t", allOutputTypes, fmt.Sprintf("types of outputs to be generated. valid types are: %q", allOutputTypes))
Expand Down
8 changes: 7 additions & 1 deletion cmd/talosctl/cmd/mgmt/gen/crt.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,13 @@ var genCrtCmd = &cobra.Command{
return fmt.Errorf("error signing certificate: %s", err)
}

if err = os.WriteFile(genCrtCmdFlags.name+".crt", signedCrt.X509CertificatePEM, 0o600); err != nil {
certFile := genCrtCmdFlags.name + ".crt"

if err = validateFileExists(certFile); err != nil {
return err
}

if err = os.WriteFile(certFile, signedCrt.X509CertificatePEM, 0o600); err != nil {
return fmt.Errorf("error writing certificate: %s", err)
}

Expand Down
8 changes: 7 additions & 1 deletion cmd/talosctl/cmd/mgmt/gen/csr.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ var genCSRCmd = &cobra.Command{
return fmt.Errorf("error generating CSR: %s", err)
}

if err := os.WriteFile(strings.TrimSuffix(genCSRCmdFlags.key, path.Ext(genCSRCmdFlags.key))+".csr", csr.X509CertificateRequestPEM, 0o600); err != nil {
csrFile := strings.TrimSuffix(genCSRCmdFlags.key, path.Ext(genCSRCmdFlags.key)) + ".csr"

if err := validateFileExists(csrFile); err != nil {
return err
}

if err := os.WriteFile(csrFile, csr.X509CertificateRequestPEM, 0o600); err != nil {
return fmt.Errorf("error writing CSR: %s", err)
}

Expand Down
34 changes: 34 additions & 0 deletions cmd/talosctl/cmd/mgmt/gen/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,46 @@
package gen

import (
"fmt"
"os"

"github.com/hashicorp/go-multierror"
"github.com/spf13/cobra"
)

var genCmdFlags struct {
force bool
}

// Cmd represents the `gen` command.
var Cmd = &cobra.Command{
Use: "gen",
Short: "Generate CAs, certificates, and private keys",
Long: ``,
}

func init() {
Cmd.PersistentFlags().BoolVarP(&genCmdFlags.force, "force", "f", false, "will overwrite existing files")
}

func validateFileExists(file string) error {
if !genCmdFlags.force {
if _, err := os.Stat(file); err == nil {
return fmt.Errorf("file %q already exists, use --force to overwrite", file)
}
}

return nil
}

func validateFilesExists(files []string) error {
var combinedErr multierror.Error

for _, file := range files {
if err := validateFileExists(file); err != nil {
combinedErr.Errors = append(combinedErr.Errors, err)
}
}

return combinedErr.ErrorOrNil()
}
8 changes: 7 additions & 1 deletion cmd/talosctl/cmd/mgmt/gen/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ var genKeyCmd = &cobra.Command{
return fmt.Errorf("error generating key: %w", err)
}

if err := os.WriteFile(genKeyCmdFlags.name+".key", key.PrivateKeyPEM, 0o600); err != nil {
keyFile := genKeyCmdFlags.name + ".key"

if err = validateFileExists(keyFile); err != nil {
return err
}

if err := os.WriteFile(keyFile, key.PrivateKeyPEM, 0o600); err != nil {
return fmt.Errorf("error writing key: %w", err)
}

Expand Down
12 changes: 10 additions & 2 deletions cmd/talosctl/cmd/mgmt/gen/keypair.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,18 @@ var genKeypairCmd = &cobra.Command{
if err != nil {
return fmt.Errorf("error generating CA: %s", err)
}
if err := os.WriteFile(genKeypairCmdFlags.organization+".crt", ca.CrtPEM, 0o600); err != nil {

certFile := genKeypairCmdFlags.organization + ".crt"
keyFile := genKeypairCmdFlags.organization + ".key"

if err = validateFilesExists([]string{certFile, keyFile}); err != nil {
return err
}

if err := os.WriteFile(certFile, ca.CrtPEM, 0o600); err != nil {
return fmt.Errorf("error writing certificate: %s", err)
}
if err := os.WriteFile(genKeypairCmdFlags.organization+".key", ca.KeyPEM, 0o600); err != nil {
if err := os.WriteFile(keyFile, ca.KeyPEM, 0o600); err != nil {
return fmt.Errorf("error writing key: %s", err)
}

Expand Down
4 changes: 4 additions & 0 deletions cmd/talosctl/cmd/mgmt/gen/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ func writeSecretsBundleToFile(bundle *generate.SecretsBundle) error {
return err
}

if err = validateFileExists(genSecretsCmdFlags.outputFile); err != nil {
return err
}

return os.WriteFile(genSecretsCmdFlags.outputFile, bundleBytes, 0o600)
}

Expand Down
11 changes: 11 additions & 0 deletions internal/integration/cli/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,17 @@ func (suite *GenSuite) TestSecrets() {
suite.RunCLI([]string{"gen", "secrets"}, base.StdoutEmpty())
suite.Assert().FileExists("secrets.yaml")

suite.RunCLI([]string{"gen", "secrets"}, base.StdoutEmpty(), base.ShouldFail(), base.StderrMatchFunc(func(output string) error {
expected := "file \"secrets.yaml\" already exists, use --force to overwrite"
if !strings.Contains(output, expected) {
return fmt.Errorf("stdout does not contain %q: %q", expected, output)
}

return nil
}))

suite.RunCLI([]string{"gen", "secrets", "--force"}, base.StdoutEmpty())

defer os.Remove("secrets.yaml") //nolint:errcheck

suite.RunCLI([]string{"gen", "secrets", "--output-file", "/tmp/secrets2.yaml"}, base.StdoutEmpty())
Expand Down
11 changes: 9 additions & 2 deletions website/content/v1.4/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -1283,6 +1283,7 @@ talosctl gen ca [flags]
--cluster string Cluster to connect to if a proxy endpoint is used.
--context string Context to be used in command
-e, --endpoints strings override default endpoints in Talos configuration
-f, --force will overwrite existing files
-n, --nodes strings target the specified nodes
--talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order.
```
Expand Down Expand Up @@ -1314,7 +1315,6 @@ talosctl gen config <cluster name> <cluster endpoint> [flags]
--config-patch-control-plane stringArray patch generated machineconfigs (applied to 'init' and 'controlplane' types)
--config-patch-worker stringArray patch generated machineconfigs (applied to 'worker' type)
--dns-domain string the dns domain to use for cluster (default "cluster.local")
--force "gen" will overwrite existing files
-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 "ghcr.io/siderolabs/installer:latest")
Expand All @@ -1338,6 +1338,7 @@ talosctl gen config <cluster name> <cluster endpoint> [flags]
--cluster string Cluster to connect to if a proxy endpoint is used.
--context string Context to be used in command
-e, --endpoints strings override default endpoints in Talos configuration
-f, --force will overwrite existing files
-n, --nodes strings target the specified nodes
--talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order.
```
Expand Down Expand Up @@ -1370,6 +1371,7 @@ talosctl gen crt [flags]
--cluster string Cluster to connect to if a proxy endpoint is used.
--context string Context to be used in command
-e, --endpoints strings override default endpoints in Talos configuration
-f, --force will overwrite existing files
-n, --nodes strings target the specified nodes
--talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order.
```
Expand Down Expand Up @@ -1401,6 +1403,7 @@ talosctl gen csr [flags]
--cluster string Cluster to connect to if a proxy endpoint is used.
--context string Context to be used in command
-e, --endpoints strings override default endpoints in Talos configuration
-f, --force will overwrite existing files
-n, --nodes strings target the specified nodes
--talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order.
```
Expand Down Expand Up @@ -1430,6 +1433,7 @@ talosctl gen key [flags]
--cluster string Cluster to connect to if a proxy endpoint is used.
--context string Context to be used in command
-e, --endpoints strings override default endpoints in Talos configuration
-f, --force will overwrite existing files
-n, --nodes strings target the specified nodes
--talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order.
```
Expand Down Expand Up @@ -1460,6 +1464,7 @@ talosctl gen keypair [flags]
--cluster string Cluster to connect to if a proxy endpoint is used.
--context string Context to be used in command
-e, --endpoints strings override default endpoints in Talos configuration
-f, --force will overwrite existing files
-n, --nodes strings target the specified nodes
--talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order.
```
Expand Down Expand Up @@ -1492,6 +1497,7 @@ talosctl gen secrets [flags]
--cluster string Cluster to connect to if a proxy endpoint is used.
--context string Context to be used in command
-e, --endpoints strings override default endpoints in Talos configuration
-f, --force will overwrite existing files
-n, --nodes strings target the specified nodes
--talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order.
```
Expand All @@ -1507,7 +1513,8 @@ Generate CAs, certificates, and private keys
### Options

```
-h, --help help for gen
-f, --force will overwrite existing files
-h, --help help for gen
```

### Options inherited from parent commands
Expand Down

0 comments on commit cf101e5

Please sign in to comment.