From 5dcf663693a12dd9257e45198b8a74ee71cd03ae Mon Sep 17 00:00:00 2001 From: Sam Foo Date: Sun, 25 Apr 2021 17:58:38 -0700 Subject: [PATCH] Add editor tab to upgrade release values Signed-off-by: Sam Foo --- Makefile | 13 ++++++- go.mod | 1 + pkg/plugin/actions/actions.go | 50 ++++++++++++++++++++++++ pkg/plugin/router/handlers.go | 7 +++- pkg/plugin/settings/capabilities.go | 1 + pkg/plugin/views/helm_release.go | 60 ++++++++++++++++++++++++++++- 6 files changed, 128 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index eac868d..e398ac0 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,19 @@ SHELL = /bin/bash +PLUGIN_NAME=octant-helm + +ifdef XDG_CONFIG_HOME + OCTANT_PLUGIN_DIR ?= ${XDG_CONFIG_HOME}/octant/plugins +else ifeq ($(OS),Windows_NT) + OCTANT_PLUGIN_DIR ?= ${LOCALAPPDATA}/octant/plugins +else + OCTANT_PLUGIN_DIR ?= ${HOME}/.config/octant/plugins +endif + .PHONY: build build: - go build -o bin/octant-helm cmd/octant-helm/main.go + @go build -o bin/$(PLUGIN_NAME) cmd/octant-helm/main.go + @cp bin/$(PLUGIN_NAME) $(OCTANT_PLUGIN_DIR)/$(PLUGIN_NAME) .PHONY: dev dev: diff --git a/go.mod b/go.mod index 4084c4a..08c212f 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( helm.sh/helm/v3 v3.4.2 k8s.io/api v0.19.4 k8s.io/apimachinery v0.19.4 + sigs.k8s.io/yaml v1.2.0 ) replace ( diff --git a/pkg/plugin/actions/actions.go b/pkg/plugin/actions/actions.go index c75e9bf..563b31c 100644 --- a/pkg/plugin/actions/actions.go +++ b/pkg/plugin/actions/actions.go @@ -3,12 +3,17 @@ package actions import ( "fmt" "github.com/bloodorangeio/octant-helm/pkg/config" + "github.com/bloodorangeio/octant-helm/pkg/helm" "github.com/vmware-tanzu/octant/pkg/action" "github.com/vmware-tanzu/octant/pkg/plugin/service" + "github.com/vmware-tanzu/octant/pkg/store" helmAction "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chartutil" + "k8s.io/apimachinery/pkg/labels" ) const ( + UpdateHelmReleaseValues = "octant-helm.dev/update" UninstallHelmReleaseAction = "octant-helm.dev/uninstall" ) @@ -30,6 +35,16 @@ func ActionHandler(request *service.ActionRequest) error { return err } return uninstallRelease(request, actionConfig, releaseName) + case UpdateHelmReleaseValues: + releaseValues, err := request.Payload.String("update") + if err != nil { + return err + } + releaseName, err := request.Payload.String("releaseName") + if err != nil { + return err + } + return updateReleaseValues(request, actionConfig, releaseValues, releaseName) default: return fmt.Errorf("unable to find handler for plugin: %s", "octant-helm") } @@ -46,3 +61,38 @@ func uninstallRelease(request *service.ActionRequest, config *helmAction.Configu request.DashboardClient.SendAlert(request.Context(), request.ClientState.ClientID, alert) return nil } + +func updateReleaseValues(request *service.ActionRequest, config *helmAction.Configuration, values string, releaseName string) error { + upgradeClient := helmAction.NewUpgrade(config) + + client := request.DashboardClient + ul, err := client.List(request.Context(), store.Key{ + APIVersion: "v1", + Kind: "Secret", + Selector: &labels.Set{ + "owner": "helm", + "name": releaseName, + }, + }) + if err != nil { + return err + } + r := helm.UnstructuredListToHelmReleaseByName(ul, releaseName) + if r == nil { + return fmt.Errorf("cannot find release name: %s", releaseName) + } + v, err := chartutil.ReadValues([]byte(values)) + if err != nil { + return err + } + // TODO: There are upgrades which require secrets. How would this be shown to a user? + release, err := upgradeClient.Run(releaseName, r.Chart, v) + if err != nil { + message := fmt.Sprintf("Unable to upgrade release: %s", err) + request.DashboardClient.SendAlert(request.Context(), request.ClientState.ClientID, action.CreateAlert(action.AlertTypeError, message, action.DefaultAlertExpiration)) + } else { + alert := action.CreateAlert(action.AlertTypeInfo, "Upgrade helm release: "+release.Name, action.DefaultAlertExpiration) + request.DashboardClient.SendAlert(request.Context(), request.ClientState.ClientID, alert) + } + return nil +} diff --git a/pkg/plugin/router/handlers.go b/pkg/plugin/router/handlers.go index 30b1250..d05a8dd 100644 --- a/pkg/plugin/router/handlers.go +++ b/pkg/plugin/router/handlers.go @@ -38,7 +38,12 @@ func helmReleaseHandler(request service.Request) (component.ContentResponse, err if err != nil { return component.EmptyContentResponse, err } + helmEditorView, err := views.BuildHelmReleaseViewForValues(request) + if err != nil { + return component.EmptyContentResponse, err + } + response := component.NewContentResponse(title) - response.Add(helmReleaseView) + response.Add(helmReleaseView, helmEditorView) return *response, nil } diff --git a/pkg/plugin/settings/capabilities.go b/pkg/plugin/settings/capabilities.go index 4590bf0..e138bd8 100644 --- a/pkg/plugin/settings/capabilities.go +++ b/pkg/plugin/settings/capabilities.go @@ -25,6 +25,7 @@ func GetCapabilities() *plugin.Capabilities { return &plugin.Capabilities{ ActionNames: []string{ actions.UninstallHelmReleaseAction, + actions.UpdateHelmReleaseValues, }, IsModule: true, } diff --git a/pkg/plugin/views/helm_release.go b/pkg/plugin/views/helm_release.go index 03c1a9c..b908465 100644 --- a/pkg/plugin/views/helm_release.go +++ b/pkg/plugin/views/helm_release.go @@ -19,7 +19,12 @@ package views // import "github.com/bloodorangeio/octant-helm/pkg/plugin/views" import ( "fmt" "github.com/bloodorangeio/octant-helm/pkg/config" + "github.com/bloodorangeio/octant-helm/pkg/plugin/actions" helmAction "helm.sh/helm/v3/pkg/action" + //"helm.sh/helm/v3/pkg/chartutil" + "sigs.k8s.io/yaml" + + //"helm.sh/helm/v3/pkg/cli" "log" "strconv" "strings" @@ -92,7 +97,7 @@ func BuildHelmReleaseViewForRequest(request service.Request) (component.Componen "Revision": component.NewText(strconv.Itoa(h.Version)), "Updated": component.NewTimestamp(h.Info.LastDeployed.Time), "Status": component.NewText(h.Info.Status.String()), - "Chart": component.NewText(fmt.Sprintf("%s-%s", h.Name, h.Chart.Metadata.Version)), + "Chart": component.NewText(fmt.Sprintf("%s-%s", h.Chart.Metadata.Name, h.Chart.Metadata.Version)), "App Version": component.NewText(appVersion), "Description": component.NewText(h.Info.Description), }) @@ -102,7 +107,8 @@ func BuildHelmReleaseViewForRequest(request service.Request) (component.Componen notesBody := component.NewMarkdownText(fmt.Sprintf("```\n%s\n```", strings.TrimSpace(r.Info.Notes))) notesCard.SetBody(notesBody) - flexLayout := component.NewFlexLayout("") + flexLayout := component.NewFlexLayout("Summary") + flexLayout.SetAccessor("summary") flexLayout.AddSections(component.FlexLayoutSection{ {Width: component.WidthHalf, View: statusSummary}, {Width: component.WidthFull, View: historyTable}, @@ -111,3 +117,53 @@ func BuildHelmReleaseViewForRequest(request service.Request) (component.Componen return flexLayout, title, nil } + +func BuildHelmReleaseViewForValues(request service.Request) (component.Component, error) { + releaseName := strings.TrimPrefix(request.Path(), "/") + client := request.DashboardClient() + ul, err := client.List(request.Context(), store.Key{ + APIVersion: "v1", + Kind: "Secret", + Selector: &labels.Set{ + "owner": "helm", + "name": releaseName, + }, + }) + if err != nil { + return component.NewError(component.TitleFromString("Cannot list Helm releases: "), err), nil + } + + r := helm.UnstructuredListToHelmReleaseByName(ul, releaseName) + if r == nil { + return component.NewText("Error: release not found"), nil + } + + + actionConfig, err := config.NewActionConfig(request.ClientState().Namespace) + if err != nil { + return component.NewError(component.TitleFromString("Create Helm config: "), err), nil + } + valuesClient := helmAction.NewGetValues(actionConfig) + values, err := valuesClient.Run(r.Name) + if err != nil { + return component.NewError(component.TitleFromString("Get values: "), err), nil + } + + // No user supplied values so show all supplied defaults instead + if len(values) == 0 { + values = r.Chart.Values + } + v, err := yaml.Marshal(values) + if err != nil { + return component.NewError(component.TitleFromString("Error"), err), nil + } + + editor := component.NewEditor(component.TitleFromString("Values"), string(v), false) + editor.Config.Language = "yaml" + editor.Config.Metadata = map[string]string{ + "releaseName": releaseName, + } + editor.SetAccessor("chart") + editor.Config.SubmitAction = actions.UpdateHelmReleaseValues + return editor, nil +}