fix: add --force
flag for talosctl gen
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>
This commit is contained in:
parent
ea2aa06116
commit
cf101e56fb
@ -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)
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,6 @@ var genConfigCmdFlags struct {
|
||||
withDocs bool
|
||||
withClusterDiscovery bool
|
||||
withKubeSpan bool
|
||||
force bool
|
||||
withSecrets string
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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))
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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.
|
||||
```
|
||||
@ -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")
|
||||
@ -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.
|
||||
```
|
||||
@ -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.
|
||||
```
|
||||
@ -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.
|
||||
```
|
||||
@ -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.
|
||||
```
|
||||
@ -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.
|
||||
```
|
||||
@ -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.
|
||||
```
|
||||
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user