Skip to content

Commit

Permalink
Support working with local charts (#215)
Browse files Browse the repository at this point in the history
* Basic functioning

* Support reconfiguring

* Improve tests coverage

* Always update local repo, don't offer to delete it

* Handle multi-repo correctly

* Document local charts usage

* Screenshot for docs
  • Loading branch information
undera authored Feb 15, 2023
1 parent 6a4ca79 commit f49f52e
Show file tree
Hide file tree
Showing 17 changed files with 282 additions and 177 deletions.
30 changes: 8 additions & 22 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
<!-- If your PR fixes an open issue, use `Closes #999` to link your PR with the issue. #999 stands for the issue number you are fixing -->
## Changes Proposed

## Fixes Issue
<!-- Describe the proposed changes and any additional information -->

<!-- Remove this section if not applicable -->
<!-- Add all the screenshots which illustrate your changes -->

<!-- Example: Closes #31 -->

## Changes proposed

<!-- List all the proposed changes in your PR -->
## Check List

<!-- Mark all the applicable boxes. To mark the box as done follow the following conventions -->
<!--
Expand All @@ -18,18 +14,8 @@
[ ] - Not correct; marked as **not** done
-->

## Check List (Check all the applicable boxes) <!-- Follow the above conventions to check the box -->

- [ ] My code follows the code style of this project.
- [ ] My change requires changes to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] All new and existing tests passed.
- [ ] The title of my pull request is a short description of the requested changes.

## Screenshots

<!-- Add all the screenshots which support your changes -->

## Note to reviewers
- [ ] The title of my pull request is a short description of the changes
- [ ] This PR relates to some issue: <!-- use "Closes #999" to auto-close related issue -->
- [ ] I have documented the changes made (if applicable)
- [ ] I have covered the changes with unit tests

<!-- Add notes to reviewers if applicable -->
29 changes: 22 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

![GitHub contributors](https://img.shields.io/github/contributors/komodorio/helm-dashboard) [![GitHub issues](https://img.shields.io/github/issues-raw/komodorio/helm-dashboard)](https://github.com/komodorio/helm-dashboard/issues) ![GitHub stars](https://img.shields.io/github/stars/komodorio/helm-dashboard?style=social) ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/komodorio/helm-dashboard) ![GitHub pull requests](https://img.shields.io/github/issues-pr/komodorio/helm-dashboard) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/komodorio/helm-dashboard) ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/komodorio/helm-dashboard) [![GitHub license](https://img.shields.io/github/license/komodorio/helm-dashboard)](https://github.com/komodorio/helm-dashboard)

<kbd>[<img src="screenshot.png" style="width: 100%; border: 1px solid silver;" border="1" alt="Screenshot">](screenshot.png)</kbd>
<kbd>[<img src="images/screenshot.png" style="width: 100%; border: 1px solid silver;" border="1" alt="Screenshot">](images/screenshot.png)</kbd>


## Description
Expand Down Expand Up @@ -96,29 +96,44 @@ If you want to increase the logging verbosity and see all the debug info, use th

The official helm chart is [available here](https://github.com/komodorio/helm-charts/blob/master/charts/helm-dashboard)

## Selected Features

## Execute Helm tests
### Support for Local Charts

Local Helm chart is a directory with specially named files and a `Chart.yaml` file, which you can install via Helm, without the need to publish the chart into Helm repository. Chart developers might want to experiment with the chart locally, before uploading into public repository. Also, a proprietary application might only use non-published chart as an approach to deploy the software.

For all the above use-cases, you may use Helm Dashboard UI, spcifying location of your local chart folders via special `--local-chart` command-line parameter. The parameter might be specified multiple times, for example:

```shell
helm-dashboard --local-chart=/opt/charts/my-private-app --local-chart=/home/dev/sources/app/chart
```

When _valid_ local chart sources specified, the repository list would contain a surrogate `[local]` entry, with those charts listed inside. All the chart operations are normal: installing, reconfiguring and upgrading.

![](images/screenshot_local_charts.png)

### Execute Helm tests

For all the release(s) (installed helm charts), you can execute helm tests for that release. For the tests to execute successfully, you need to have existing tests for that helm chart.

You can execute `helm test` for the specific release as below:
![](screenshot_run_test.png)
![](images/screenshot_run_test.png)

The result of executed `helm test` for the release will be disapled as below:
![](screenshot_run_test_result.png)
![](images/screenshot_run_test_result.png)

## Scanner Integrations
### Scanner Integrations

Upon startup, Helm Dashboard detects the presence of [Trivy](https://github.com/aquasecurity/trivy)
and [Checkov](https://github.com/bridgecrewio/checkov) scanners. When available, these scanners are offered on k8s
resources page, as well as install/upgrade preview page.

You can request scanning of the specific k8s resource in your cluster:
![](screenshot_scan_resource.png)
![](images/screenshot_scan_resource.png)

If you want to validate the k8s manifest prior to installing/reconfiguring a Helm chart, look for "Scan for Problems"
button at the bottom of the dialog:
![](screenshot_scan_manifest.png)
![](images/screenshot_scan_manifest.png)

## Support Channels

Expand Down
File renamed without changes
Binary file added images/screenshot_local_charts.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
30 changes: 16 additions & 14 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ var (
)

type options struct {
Version bool `long:"version" description:"Show tool version"`
Verbose bool `short:"v" long:"verbose" description:"Show verbose debug information"`
NoBrowser bool `short:"b" long:"no-browser" description:"Do not attempt to open Web browser upon start"`
NoTracking bool `long:"no-analytics" description:"Disable user analytics (GA, DataDog etc.)"`
BindHost string `long:"bind" description:"Host binding to start server (default: localhost)"` // default should be printed but not assigned as the precedence: flag > env > default
Port uint `short:"p" long:"port" description:"Port to start server on" default:"8080"`
Namespace string `short:"n" long:"namespace" description:"Namespace for HELM operations"`
Devel bool `long:"devel" description:"Include development versions of charts"`
Version bool `long:"version" description:"Show tool version"`
Verbose bool `short:"v" long:"verbose" description:"Show verbose debug information"`
NoBrowser bool `short:"b" long:"no-browser" description:"Do not attempt to open Web browser upon start"`
NoTracking bool `long:"no-analytics" description:"Disable user analytics (Heap, DataDog etc.)"`
BindHost string `long:"bind" description:"Host binding to start server (default: localhost)"` // default should be printed but not assigned as the precedence: flag > env > default
Port uint `short:"p" long:"port" description:"Port to start server on" default:"8080"`
Namespace string `short:"n" long:"namespace" description:"Namespace for HELM operations"`
Devel bool `long:"devel" description:"Include development versions of charts"`
LocalChart []string `long:"local-chart" description:"Specify location of local chart to include into UI"`
}

func main() {
Expand All @@ -46,12 +47,13 @@ func main() {
setupLogging(opts.Verbose)

server := dashboard.Server{
Version: version,
Namespaces: strings.Split(opts.Namespace, ","),
Address: fmt.Sprintf("%s:%d", opts.BindHost, opts.Port),
Debug: opts.Verbose,
NoTracking: opts.NoTracking,
Devel: opts.Devel,
Version: version,
Namespaces: strings.Split(opts.Namespace, ","),
Address: fmt.Sprintf("%s:%d", opts.BindHost, opts.Port),
Debug: opts.Verbose,
NoTracking: opts.NoTracking,
Devel: opts.Devel,
LocalCharts: opts.LocalChart,
}

ctx, cancel := context.WithCancel(context.Background())
Expand Down
57 changes: 45 additions & 12 deletions pkg/dashboard/handlers/helmHandlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,25 @@ package handlers
import (
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/hexops/gotextdiff"
"github.com/hexops/gotextdiff/myers"
"github.com/hexops/gotextdiff/span"
"github.com/joomcode/errorx"
"github.com/komodorio/helm-dashboard/pkg/dashboard/objects"
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
"github.com/rogpeppe/go-internal/semver"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/repo"
helmtime "helm.sh/helm/v3/pkg/time"
"k8s.io/utils/strings/slices"
"net/http"
"sort"
"strconv"

"github.com/gin-gonic/gin"
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
"strings"
)

type HelmHandler struct {
Expand Down Expand Up @@ -162,6 +163,7 @@ func (h *HelmHandler) RepoVersions(c *gin.Context) {
AppVersion: r.AppVersion,
Description: r.Description,
Repository: r.Annotations[objects.AnnRepo],
URLs: r.URLs,
})
}

Expand Down Expand Up @@ -194,6 +196,7 @@ func (h *HelmHandler) RepoLatestVer(c *gin.Context) {
AppVersion: r.AppVersion,
Description: r.Description,
Repository: r.Annotations[objects.AnnRepo],
URLs: r.URLs,
})
}

Expand Down Expand Up @@ -288,12 +291,19 @@ func (h *HelmHandler) Install(c *gin.Context) {
return
}

repoChart, err := h.checkLocalRepo(c.PostForm("chart"))
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}

justTemplate := c.PostForm("preview") == "true"
ns := c.Param("ns")
if ns == "[empty]" {
ns = ""
}
rel, err := app.Releases.Install(ns, c.PostForm("name"), c.PostForm("chart"), c.PostForm("version"), justTemplate, values)

rel, err := app.Releases.Install(ns, c.PostForm("name"), repoChart, c.PostForm("version"), justTemplate, values)
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
Expand All @@ -306,6 +316,16 @@ func (h *HelmHandler) Install(c *gin.Context) {
}
}

func (h *HelmHandler) checkLocalRepo(repoChart string) (string, error) {
if strings.HasPrefix(repoChart, "file://") {
repoChart = repoChart[len("file://"):]
if !slices.Contains(h.Data.LocalCharts, repoChart) {
return "", fmt.Errorf("chart path is not present in local charts: %s", repoChart)
}
}
return repoChart, nil
}

func (h *HelmHandler) Upgrade(c *gin.Context) {
app := h.GetApp(c)
if app == nil {
Expand All @@ -325,8 +345,14 @@ func (h *HelmHandler) Upgrade(c *gin.Context) {
return
}

repoChart, err := h.checkLocalRepo(c.PostForm("chart"))
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}

justTemplate := c.PostForm("preview") == "true"
rel, err := existing.Upgrade(c.PostForm("chart"), c.PostForm("version"), justTemplate, values)
rel, err := existing.Upgrade(repoChart, c.PostForm("version"), justTemplate, values)
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
Expand Down Expand Up @@ -409,7 +435,13 @@ func (h *HelmHandler) RepoValues(c *gin.Context) {
return // sets error inside
}

out, err := app.Repositories.GetChartValues(c.Query("chart"), c.Query("version"))
repoChart, err := h.checkLocalRepo(c.Query("chart"))
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}

out, err := app.Repositories.GetChartValues(repoChart, c.Query("version"))
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
Expand All @@ -433,8 +465,8 @@ func (h *HelmHandler) RepoList(c *gin.Context) {
out := []RepositoryElement{}
for _, r := range repos {
out = append(out, RepositoryElement{
Name: r.Orig.Name,
URL: r.Orig.URL,
Name: r.Name(),
URL: r.URL(),
})
}

Expand Down Expand Up @@ -521,15 +553,16 @@ func (h *HelmHandler) handleGetSection(rel *objects.Release, section string, rDi
return res, nil
}

type RepoChartElement struct {
type RepoChartElement struct { // TODO: do we need it at all? there is existing repo.ChartVersion in Helm
Name string `json:"name"`
Version string `json:"version"`
AppVersion string `json:"app_version"`
Description string `json:"description"`

InstalledNamespace string `json:"installed_namespace"`
InstalledName string `json:"installed_name"`
Repository string `json:"repository"`
InstalledNamespace string `json:"installed_namespace"`
InstalledName string `json:"installed_name"`
Repository string `json:"repository"`
URLs []string `json:"urls"`
}

func HReleaseToJSON(o *release.Release) *ReleaseElement {
Expand Down
5 changes: 4 additions & 1 deletion pkg/dashboard/objects/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type DataLayer struct {
appPerContext map[string]*Application
appPerContextMx *sync.Mutex
devel bool
LocalCharts []string
}

type StatusInfo struct {
Expand Down Expand Up @@ -170,6 +171,8 @@ func (d *DataLayer) AppForCtx(ctx string) (*Application, error) {
return nil, errorx.Decorate(err, "Failed to create application for context '%s'", ctx)
}

a.Repositories.LocalCharts = d.LocalCharts

app = a
d.appPerContext[ctx] = app
}
Expand Down Expand Up @@ -218,7 +221,7 @@ func (d *DataLayer) loopUpdateRepos(ctx context.Context, interval time.Duration)
for _, repo := range repos {
err := repo.Update()
if err != nil {
log.Warnf("Failed to update repo %s: %v", repo.Orig.Name, err)
log.Warnf("Failed to update repo %s: %v", repo.Name(), err)
}
}
}
Expand Down
Loading

0 comments on commit f49f52e

Please sign in to comment.