Skip to content
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

Support working with local charts #215

Merged
merged 7 commits into from
Feb 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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