-
Notifications
You must be signed in to change notification settings - Fork 23
feat(commands): improve rotate-ca with validation and better help #103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| // Copyright Cozystack Authors | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| package commands | ||
|
|
||
| import ( | ||
| "fmt" | ||
|
|
||
| "github.com/spf13/cobra" | ||
| ) | ||
|
|
||
| // wrapRotateCACommand adds special handling for rotate-ca command: | ||
| // - validates that only one node/endpoint is provided (must be a control-plane node) | ||
| // - updates command description | ||
| func wrapRotateCACommand(wrappedCmd *cobra.Command) { | ||
| // Update command description to be more helpful | ||
| wrappedCmd.Long = `Rotates Talos and/or Kubernetes root Certificate Authorities. | ||
|
|
||
| This command must be run against a SINGLE control-plane node. The specified node | ||
| will be used to coordinate the CA rotation across the entire cluster. | ||
|
|
||
| The command works by: | ||
| 1. Auto-discovering all cluster nodes (control-plane and workers) | ||
| 2. Generating new CA certificates | ||
| 3. Gracefully rolling out the new CAs to all nodes | ||
| 4. Updating the talosconfig with new credentials | ||
|
|
||
| IMPORTANT: You must specify exactly ONE control-plane node via --endpoints/-e or --nodes/-n | ||
| flags, or through a single config file (-f). The node must be a control-plane node. | ||
|
|
||
| By default, both Talos API CA and Kubernetes API CA are rotated. Use --talos=false | ||
| or --kubernetes=false to rotate only one of them. | ||
|
|
||
| The command runs in dry-run mode by default. Use --dry-run=false to perform actual rotation.` | ||
|
|
||
| wrappedCmd.Example = ` # Dry-run CA rotation (recommended first step) | ||
| talm rotate-ca -e 192.168.1.10 | ||
|
|
||
| # Rotate CAs using config file (must contain single control-plane node) | ||
| talm rotate-ca -f nodes/controlplane-1.yaml | ||
|
|
||
| # Actually perform the rotation | ||
| talm rotate-ca -f nodes/controlplane-1.yaml --dry-run=false | ||
|
|
||
| # Rotate only Talos API CA | ||
| talm rotate-ca -f nodes/controlplane-1.yaml --kubernetes=false --dry-run=false | ||
|
|
||
| # Rotate only Kubernetes API CA | ||
| talm rotate-ca -f nodes/controlplane-1.yaml --talos=false --dry-run=false` | ||
|
|
||
| // Store original PreRunE to chain it | ||
| originalPreRunE := wrappedCmd.PreRunE | ||
|
|
||
| wrappedCmd.PreRunE = func(cmd *cobra.Command, args []string) error { | ||
| // Run original PreRunE first (processes modeline, syncs GlobalArgs, etc.) | ||
| if originalPreRunE != nil { | ||
| if err := originalPreRunE(cmd, args); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| // Validate that only one endpoint/node is provided | ||
| if len(GlobalArgs.Endpoints) > 1 { | ||
| return fmt.Errorf("rotate-ca requires exactly one control-plane node, but %d endpoints were provided\n\nThe rotate-ca command coordinates CA rotation across the entire cluster from a single\ncontrol-plane node. Please specify only one endpoint using -e flag or a single config file", len(GlobalArgs.Endpoints)) | ||
| } | ||
|
|
||
| if len(GlobalArgs.Nodes) > 1 { | ||
| return fmt.Errorf("rotate-ca requires exactly one control-plane node, but %d nodes were provided\n\nThe rotate-ca command coordinates CA rotation across the entire cluster from a single\ncontrol-plane node. Please specify only one node using -n flag or a single config file", len(GlobalArgs.Nodes)) | ||
| } | ||
|
kvaps marked this conversation as resolved.
|
||
|
|
||
| return nil | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -94,9 +94,8 @@ func wrapTalosCommand(cmd *cobra.Command, cmdName string) *cobra.Command { | |
| } | ||
| if baseCmdName == "kubeconfig" { | ||
| if !cmd.Flags().Changed("force") { | ||
| if err := cmd.Flags().Set("force", "true"); err != nil { | ||
| // Flag might not exist, ignore error | ||
| } | ||
| // Flag might not exist, ignore error | ||
| _ = cmd.Flags().Set("force", "true") | ||
| } | ||
|
Comment on lines
+97
to
99
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cat -n pkg/commands/talosctl_wrapper.go | sed -n '85,110p'Repository: cozystack/talm Length of output: 1268 🏁 Script executed: # Let's see more context to understand what this code does
cat -n pkg/commands/talosctl_wrapper.go | sed -n '1,50p'Repository: cozystack/talm Length of output: 2054 🌐 Web query:
💡 Result:
Practical handling patternfs := cmd.Flags() // in Cobra: local + persistent from parents
if fs.Lookup("my-flag") == nil {
// flag not registered on this command (or its parents)
return nil // or return a custom error
}
if err := fs.Set("my-flag", "123"); err != nil {
var ne *pflag.NotExistError
if errors.As(err, &ne) {
// handle missing flag explicitly
return nil
}
return err // invalid value, etc.
}Cobra-specific “missing flag” gotchaIf you call Sources: 🏁 Script executed: rg -A 5 -B 5 "force" pkg/commands/talosctl_wrapper.go | head -40Repository: cozystack/talm Length of output: 798 🏁 Script executed: # Find the kubeconfig command definition in talosctl
fd -type f -name "*.go" | xargs rg -l "kubeconfig" | head -5Repository: cozystack/talm Length of output: 362 🏁 Script executed: # Search for where force flag is defined
rg "AddBoolFlag.*force" --type goRepository: cozystack/talm Length of output: 40 🏁 Script executed: # Check the original talosctl kubeconfig command to see if it has force flag
rg -B 3 -A 3 "kubeconfig.*Command" pkg/commands/talosctl_wrapper.goRepository: cozystack/talm Length of output: 40 🏁 Script executed: rg "force.*Bool" . --max-count 20Repository: cozystack/talm Length of output: 40 🏁 Script executed: # Look for force flag in kubeconfig_handler.go
cat pkg/commands/kubeconfig_handler.go | head -100Repository: cozystack/talm Length of output: 3346 🏁 Script executed: # Search for force flag registration in talos package imports
rg -B 5 -A 5 "cmd\.Flags\(\)\.BoolVar.*force" .Repository: cozystack/talm Length of output: 40 🏁 Script executed: # Search for all places where force flag is referenced
rg "\"force\"" pkg/commands/ -B 2 -A 2Repository: cozystack/talm Length of output: 2248 🏁 Script executed: # Search for where the kubeconfig command is created/wrapped
rg -B 10 -A 10 "wrapTalosCommand.*kubeconfig" pkg/commands/Repository: cozystack/talm Length of output: 40 🏁 Script executed: # Check the talosctl import to understand the command structure
rg "taloscommands" pkg/commands/talosctl_wrapper.go -A 5 -B 5Repository: cozystack/talm Length of output: 1038 🏁 Script executed: # Check if force flag is typically a persistent flag in talosctl
rg -A 20 "func wrapTalosCommand" pkg/commands/talosctl_wrapper.go | head -50Repository: cozystack/talm Length of output: 845 🏁 Script executed: # Check if kubeconfig command is called with force flag anywhere
rg "kubeconfig.*--force" .Repository: cozystack/talm Length of output: 181 🏁 Script executed: # Look for how the original talosctl kubeconfig is defined
rg -B 5 -A 15 "kubeconfig.*Command" pkg/commands/root.goRepository: cozystack/talm Length of output: 40 🏁 Script executed: # Check the Apply command which does have force flag to see the pattern
cat -n pkg/commands/apply.go | sed -n '1,100p'Repository: cozystack/talm Length of output: 3712 🏁 Script executed: # Search for kubeconfig command in talosctl package reference
git ls-files | xargs grep -l "kubeconfig" | head -5Repository: cozystack/talm Length of output: 254 🏁 Script executed: # Check the initialization of talosctl commands and if force flag is added
cat -n pkg/commands/talosctl_wrapper.go | sed -n '160,180p'Repository: cozystack/talm Length of output: 695 🏁 Script executed: # Look for any flag additions or modifications to kubeconfig specifically
rg -B 10 -A 10 "wrapKubeconfigCommand" pkg/commands/Repository: cozystack/talm Length of output: 2799 🏁 Script executed: # Look at the full context of how kubeconfig is being wrapped, including the PreRunE logic
cat -n pkg/commands/talosctl_wrapper.go | sed -n '87,135p'Repository: cozystack/talm Length of output: 2055 🏁 Script executed: # Search for where force flag might be added to kubeconfig
rg -A 30 "func wrapKubeconfigCommand" pkg/commands/kubeconfig_handler.goRepository: cozystack/talm Length of output: 1188 Check flag existence before calling Silently ignoring 🛠️ Proposed fix- // Flag might not exist, ignore error
- _ = cmd.Flags().Set("force", "true")
+ if cmd.Flags().Lookup("force") != nil {
+ if err := cmd.Flags().Set("force", "true"); err != nil {
+ return err
+ }
+ }🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
|
|
@@ -164,6 +163,11 @@ func wrapTalosCommand(cmd *cobra.Command, cmdName string) *cobra.Command { | |
| wrapUpgradeCommand(wrappedCmd, originalRunE) | ||
| } | ||
|
|
||
| // Special handling for rotate-ca command | ||
| if baseCmdName == "rotate-ca" { | ||
| wrapRotateCACommand(wrappedCmd) | ||
| } | ||
|
|
||
| // Copy all subcommands | ||
| for _, subCmd := range cmd.Commands() { | ||
| wrappedCmd.AddCommand(wrapTalosCommand(subCmd, subCmd.Name())) | ||
|
|
||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Propagate decryption errors instead of ignoring them.
handleTalosconfigEncryption(false)already returnsnilwhen the key is missing and decryption should be skipped. Ignoring all errors can hide corruption/invalid key issues and leavetalosconfig.encryptedstale while a new plaintext is generated.🛠️ Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents