diff --git a/internal/client/gvr_test.go b/internal/client/gvr_test.go index 744ef26773..1c6a79fddd 100644 --- a/internal/client/gvr_test.go +++ b/internal/client/gvr_test.go @@ -37,7 +37,7 @@ func TestGVRCan(t *testing.T) { "view": {[]string{"get", "list", "watch"}, "view", true}, "delete": {[]string{"delete", "list", "watch"}, "delete", true}, "no_delete": {[]string{"get", "list", "watch"}, "delete", false}, - "edit": {[]string{"path", "update", "watch"}, "edit", true}, + "edit": {[]string{"patch", "update", "watch"}, "edit", true}, "no_edit": {[]string{"get", "list", "watch"}, "edit", false}, } diff --git a/internal/ui/key.go b/internal/ui/key.go index ff90b310ba..3e4dd7b7d0 100644 --- a/internal/ui/key.go +++ b/internal/ui/key.go @@ -13,6 +13,7 @@ func initKeys() { tcell.KeyNames[KeyHelp] = "?" tcell.KeyNames[KeySlash] = "/" tcell.KeyNames[KeySpace] = "space" + tcell.KeyNames[KeyPlus] = "+" initNumbKeys() initStdKeys() @@ -80,6 +81,7 @@ const ( KeySlash = 47 KeyColon = 58 KeySpace = 32 + KeyPlus = 43 ) // Define Shift Keys. diff --git a/internal/view/browser.go b/internal/view/browser.go index 7f0876d1ab..6ce07f8b3b 100644 --- a/internal/view/browser.go +++ b/internal/view/browser.go @@ -4,8 +4,10 @@ package view import ( + "bytes" "context" "fmt" + "os" "sort" "strconv" "strings" @@ -23,6 +25,7 @@ import ( "github.com/derailed/tcell/v2" "github.com/rs/zerolog/log" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kubectl/pkg/cmd/util/editor" ) // Browser represents a generic resource browser. @@ -416,6 +419,52 @@ func (b *Browser) describeCmd(evt *tcell.EventKey) *tcell.EventKey { return nil } +func (b *Browser) applyCmd(evt *tcell.EventKey) *tcell.EventKey { + b.Stop() + defer b.Start() + { + a := b.app + a.Halt() + defer a.Resume() + + var data []byte + var path string + var err error + + if !a.Suspend(func() { + edit := editor.NewDefaultEditor([]string{}) + buf := &bytes.Buffer{} + data, path, err = edit.LaunchTempFile("kubectl-apply-", ".yaml", buf) + }) { + time.AfterFunc(200*time.Millisecond, func() { + a.Flash().Errf("Failed to suspend the application") + }) + return nil + } + + defer os.Remove(path) + if err != nil { + time.AfterFunc(200*time.Millisecond, func() { + a.Flash().Errf("Failed to edit a temporary file: %s", err) + }) + } else if len(data) > 0 { + args := make([]string, 0, 10) + args = append(args, "apply", "-f", path) + if err := runK(a, shellOpts{clear: true, args: args}); err != nil { + time.AfterFunc(200*time.Millisecond, func() { + a.Flash().Errf("Failed to apply resource: %s", err) + }) + } else { + time.AfterFunc(200*time.Millisecond, func() { + a.Flash().Info("Resource applied successfully!") + }) + } + } + } + + return evt +} + func (b *Browser) editCmd(evt *tcell.EventKey) *tcell.EventKey { path := b.GetSelectedItem() if path == "" { @@ -543,6 +592,11 @@ func (b *Browser) refreshActions() { Visible: true, Dangerous: true, })) + aa.Add(ui.KeyPlus, ui.NewKeyActionWithOpts("Apply", b.applyCmd, + ui.ActionOpts{ + Visible: true, + Dangerous: true, + })) } if client.Can(b.meta.Verbs, "delete") { aa.Add(tcell.KeyCtrlD, ui.NewKeyActionWithOpts("Delete", b.deleteCmd,