-
Notifications
You must be signed in to change notification settings - Fork 550
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement talosctl edit and patch config commands
Fixes: #3209 Using parts of `kubectl` package to run the editor. Also using the same approach as in `kubectl edit` command: - add commented section to the top of the file with the description. - if the config has errors, display validation errors in the commented section at the top of the file. - retry apply config until it succeeds. - abort if no changes were detected or if the edited file is empty. Patch currently supports jsonpatch only and can read it either from the file or from the inline argument. https://asciinema.org/a/wPawpctjoCFbJZKo2z2ATDXeC Signed-off-by: Artem Chernyshev <artem.0xD2@gmail.com>
- Loading branch information
Showing
13 changed files
with
590 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
// 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 ( | ||
"bytes" | ||
"context" | ||
"fmt" | ||
"io" | ||
"runtime" | ||
"strings" | ||
|
||
"github.com/spf13/cobra" | ||
"gopkg.in/yaml.v3" | ||
cmdutil "k8s.io/kubectl/pkg/cmd/util" | ||
"k8s.io/kubectl/pkg/cmd/util/editor" | ||
"k8s.io/kubectl/pkg/cmd/util/editor/crlf" | ||
|
||
"github.com/talos-systems/talos/cmd/talosctl/pkg/talos/helpers" | ||
"github.com/talos-systems/talos/pkg/machinery/api/machine" | ||
"github.com/talos-systems/talos/pkg/machinery/client" | ||
"github.com/talos-systems/talos/pkg/resources/config" | ||
) | ||
|
||
var editCmdFlags struct { | ||
namespace string | ||
immediate bool | ||
} | ||
|
||
// editCmd represents the edit command. | ||
var editCmd = &cobra.Command{ | ||
Use: "edit <type> [<id>]", | ||
Short: "Edit a resource from the default editor.", | ||
Args: cobra.RangeArgs(1, 2), | ||
Long: `The edit command allows you to directly edit any API resource | ||
you can retrieve via the command line tools. | ||
It will open the editor defined by your TALOS_EDITOR, | ||
or EDITOR environment variables, or fall back to 'vi' for Linux | ||
or 'notepad' for Windows.`, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return WithClient(func(ctx context.Context, c *client.Client) error { | ||
edit := editor.NewDefaultEditor([]string{ | ||
"TALOS_EDITOR", | ||
"EDITOR", | ||
}) | ||
|
||
resourceType := args[0] | ||
if resourceType != config.Type { | ||
return fmt.Errorf("only the config resource can be edited") | ||
} | ||
|
||
var lastError string | ||
|
||
editFn := func(parentCtx context.Context, msg client.ResourceResponse) error { | ||
if msg.Resource == nil { | ||
return nil | ||
} | ||
|
||
for { | ||
var ( | ||
buf bytes.Buffer | ||
w io.Writer = &buf | ||
id string | ||
) | ||
|
||
metadata := msg.Resource.Metadata() | ||
|
||
if metadata != nil { | ||
id = metadata.ID() | ||
} | ||
|
||
if runtime.GOOS == "windows" { | ||
w = crlf.NewCRLFWriter(w) | ||
} | ||
|
||
_, err := w.Write([]byte( | ||
fmt.Sprintf( | ||
"# Editing %s/%s at node %s\n", resourceType, id, msg.Metadata.GetHostname(), | ||
), | ||
)) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if lastError != "" { | ||
lines := strings.Split(lastError, "\n") | ||
|
||
_, err = w.Write([]byte( | ||
fmt.Sprintf("#\n# %s\n", strings.Join(lines, "\n# ")), | ||
)) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
body, err := yaml.Marshal(msg.Resource.Spec()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
_, err = w.Write(body) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
edited, _, err := edit.LaunchTempFile(fmt.Sprintf("%s-%s-edit-", resourceType, id), ".yaml", &buf) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
editedWithoutComments := bytes.TrimSpace(cmdutil.StripComments(edited)) | ||
|
||
if len(bytes.TrimSpace(editedWithoutComments)) == 0 { | ||
fmt.Println("Apply was skipped: empty file.") | ||
|
||
break | ||
} | ||
|
||
if bytes.Equal( | ||
editedWithoutComments, | ||
bytes.TrimSpace(cmdutil.StripComments(body)), | ||
) { | ||
fmt.Println("Apply was skipped: no changes detected.") | ||
|
||
break | ||
} | ||
|
||
_, err = c.ApplyConfiguration(parentCtx, &machine.ApplyConfigurationRequest{ | ||
Data: edited, | ||
Immediate: editCmdFlags.immediate, | ||
}) | ||
if err != nil { | ||
lastError = err.Error() | ||
|
||
continue | ||
} | ||
|
||
break | ||
} | ||
|
||
return nil | ||
} | ||
|
||
for _, node := range Nodes { | ||
nodeCtx := client.WithNodes(ctx, node) | ||
if err := helpers.ForEachResource(nodeCtx, c, editFn, editCmdFlags.namespace, args...); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
}) | ||
}, | ||
} | ||
|
||
func init() { | ||
editCmd.Flags().StringVar(&editCmdFlags.namespace, "namespace", "", "resource namespace (default is to use default namespace per resource)") | ||
editCmd.Flags().BoolVar(&editCmdFlags.immediate, "immediate", false, "apply the change immediately (without a reboot)") | ||
addCommand(editCmd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.