Allow installing into cluster (#128)

* Dockerize it

* Default chart layout

* Installable

* Starts and loads

* Progressing

* Hide cluster block

* Add scanners

* Add icon for helm chart (#130)

Co-authored-by: Harshit Mehta <harshitm@nvidia.com>

* Image build and push scripts

* Build local img

* Local img

* ci stuff

* ci and chart changes

* add readme

* modify readme

* move readme location

* update docs and delete file

* remove file

* allow write actions

* allow write actions

* update .gitignore

* update readme

* delete file

* add persistence and update documentation

* update logo

* update volume paths and documentation

* change pvc size

* Comment

Co-authored-by: Harshit Mehta <hdm23061993@gmail.com>
Co-authored-by: Harshit Mehta <harshitm@nvidia.com>
Co-authored-by: ronahk <rona@komodor.io>
This commit is contained in:
Andrey Pokhilko
2022-12-18 14:09:07 +02:00
committed by GitHub
parent 9bac7306a4
commit f6d3e519e2
30 changed files with 829 additions and 50 deletions

4
.dockerignore Normal file
View File

@@ -0,0 +1,4 @@
Dockerfile
*.md
bin
.idea

View File

@@ -2,9 +2,9 @@ name: Build
on: on:
push: push:
branches: [ "main" ] branches: main
pull_request: pull_request:
branches: [ "main" ] branches: main
jobs: jobs:
build: build:
@@ -41,3 +41,30 @@ jobs:
skip-pkg-cache: true skip-pkg-cache: true
skip-build-cache: true skip-build-cache: true
# args: --timeout=15m # args: --timeout=15m
image:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Check out the repo
uses: actions/checkout@v2
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
outputs: local
build-args: VER=0.0.0-dev
helm_check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Helm Template Check For Sanity
uses: igabaydulin/helm-check-action@0.1.4
env:
CHART_LOCATION: ./charts/helm-dashboard
CHART_VALUES: ./charts/helm-dashboard/values.yaml

43
.github/workflows/publish-chart.yaml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: publish helm chart
# for manual running in case we need to update the chart without releasing the dashboard app
on:
workflow_dispatch:
env:
HELM_REP: helm-charts
GH_OWNER: komodorio
CHART_DIR: charts/helm-dashboard
jobs:
publish_chart:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Bump versions
run: |
git config user.email komi@komodor.io
git config user.name komodor-bot
git fetch --tags
git checkout main
sh ./ci/bump-versions.sh
git add charts/helm-dashboard/Chart.yaml
git commit -m "Increment chart versions [skip ci]" || echo "Already up-to-date"
git push -f || echo "Nothing to push!"
env:
APP_VERSION: ${{ needs.pre_release.outputs.release_tag }}
- name: Push folder to helm-charts repository
uses: crykn/copy_folder_to_another_repo_action@v1.0.6
env:
API_TOKEN_GITHUB: ${{ secrets.KOMI_WORKFLOW_TOKEN }}
with:
source_folder: "charts/helm-dashboard"
destination_repo: "komodorio/helm-charts"
destination_folder: "charts/helm-dashboard"
user_email: "komi@komodor.io"
user_name: "komodor-bot"
destination_branch: "master"
commit_msg: "feat(helm-dashboard): update chart" #important!! don't change this commit message unless you change the condition in pipeline.yml on helm-charts repo

View File

@@ -3,7 +3,12 @@ name: release
on: on:
push: push:
tags: tags:
- "*" - "v*"
env:
HELM_REP: helm-charts
GH_OWNER: komodorio
CHART_DIR: charts/helm-dashboard
jobs: jobs:
pre_release: pre_release:
@@ -52,3 +57,68 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Test Binary Versions - name: Test Binary Versions
run: "dist/helm-dashboard_linux_amd64_v1/helm-dashboard --help" run: "dist/helm-dashboard_linux_amd64_v1/helm-dashboard --help"
image:
runs-on: ubuntu-latest
needs: release
timeout-minutes: 60
steps:
- name: Check out the repo
uses: actions/checkout@v2
- name: Docker meta
uses: docker/metadata-action@v3
id: meta
with:
images: komodorio/helm-dashboard
- name: Login to DockerHub
uses: docker/login-action@v1
if: github.event_name != 'pull_request'
with:
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_PASS }}
- name: Build and push
uses: docker/build-push-action@v2
if: github.event_name != 'pull_request'
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ needs.pre_release.outputs.release_tag }}
labels: ${{ steps.meta.outputs.labels }}
build-args: VER=${{ needs.pre_release.outputs.release_tag }}
publish_chart:
runs-on: ubuntu-latest
need: image
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Bump versions
run: |
git config user.email komi@komodor.io
git config user.name komodor-bot
git fetch --tags
git checkout main
sh ./ci/bump-versions.sh
git add charts/helm-dashboard/Chart.yaml
git commit -m "Increment chart versions [skip ci]" || echo "Already up-to-date"
git push -f || echo "Nothing to push!"
env:
APP_VERSION: ${{ needs.pre_release.outputs.release_tag }}
- name: Push folder to helm-charts repository
uses: crykn/copy_folder_to_another_repo_action@v1.0.6
env:
API_TOKEN_GITHUB: ${{ secrets.KOMI_WORKFLOW_TOKEN }}
with:
source_folder: "charts/helm-dashboard"
destination_repo: "komodorio/helm-charts"
destination_folder: "charts/helm-dashboard"
user_email: "komi@komodor.io"
user_name: "komodor-bot"
destination_branch: "master"
commit_msg: "feat(helm-dashboard): ${{ github.event.head_commit.message }}" #important!! don't change this commit message unless you change the condition in pipeline.yml on helm-charts repo

3
.gitignore vendored
View File

@@ -24,3 +24,6 @@ go.work
/bin /bin
/.idea/ /.idea/
.DS_Store
.vscode/

41
Dockerfile Normal file
View File

@@ -0,0 +1,41 @@
# Stage - builder
FROM golang as builder
ARG VER
ENV GOOS=linux
ENV GOARCH=amd64
ENV CGO_ENABLED=0
ENV VERSION=0.0.0
WORKDIR /build
COPY go.mod ./
COPY go.sum ./
COPY main.go ./
RUN go mod download
ADD . src
WORKDIR /build/src
RUN make build
# Stage - runner
FROM alpine/helm
RUN curl -o /bin/kubectl -vf -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && chmod +x /bin/kubectl && kubectl --help
# Checkov scanner
RUN apk add --update --no-cache python3
RUN python3 -m ensurepip
RUN pip3 install checkov
# Trivy
RUN curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.18.3
COPY --from=builder /build/src/bin/dashboard /bin/helm-dashboard
ENTRYPOINT ["/bin/helm-dashboard", "--no-browser", "--bind=0.0.0.0"]
# docker build . -t komodorio/helm-dashboard:0.0.0 && kind load docker-image komodorio/helm-dashboard:0.0.0

View File

@@ -6,7 +6,7 @@ A simplified way of working with Helm.
## What it Does? ## What it Does?
The _Helm Dashboard_ plugin offers a UI-driven way to view the installed Helm charts, see their revision history and _Helm Dashboard_ offers a UI-driven way to view the installed Helm charts, see their revision history and
corresponding k8s resources. Also, you can perform simple actions like roll back to a revision or upgrade to newer corresponding k8s resources. Also, you can perform simple actions like roll back to a revision or upgrade to newer
version. version.
@@ -22,9 +22,11 @@ Some of the key capabilities of the tool:
- Integration with popular problem scanners - Integration with popular problem scanners
- Easy switch between multiple clusters - Easy switch between multiple clusters
## Installing ## Setup
To install it, simply run Helm command: ### Using Helm plugin manager
To install the plugin, simply run Helm command:
```shell ```shell
helm plugin install https://github.com/komodorio/helm-dashboard.git helm plugin install https://github.com/komodorio/helm-dashboard.git
@@ -42,10 +44,6 @@ To uninstall, run:
helm plugin uninstall dashboard helm plugin uninstall dashboard
``` ```
> In case standard Helm plugin way did not work for you, you can just download the appropriate [release package](https://github.com/komodorio/helm-dashboard/releases) for your platform, unpack it and just run `dashboard` binary from it.
## Running
To use the plugin, your machine needs to have working `helm` and also `kubectl` commands. Helm version 3.4.0+ is required. To use the plugin, your machine needs to have working `helm` and also `kubectl` commands. Helm version 3.4.0+ is required.
After installing, start the UI by running: After installing, start the UI by running:
@@ -62,6 +60,7 @@ You can see the list of available command-line flags by running `helm dashboard
By default, the web server is only available locally. You can change that by specifying `HD_BIND` environment variable By default, the web server is only available locally. You can change that by specifying `HD_BIND` environment variable
to the desired value. For example, `0.0.0.0` would bind to all IPv4 addresses or `[::0]` would be all IPv6 addresses. to the desired value. For example, `0.0.0.0` would bind to all IPv4 addresses or `[::0]` would be all IPv6 addresses.
This can also be specified using flag `--bind <host>`, for example `--bind=0.0.0.0` or `--bind 0.0.0.0`. This can also be specified using flag `--bind <host>`, for example `--bind=0.0.0.0` or `--bind 0.0.0.0`.
> Precedence order: flag `--bind=<host>` > env `HD_BIND=<host>` > default value `localhost` > Precedence order: flag `--bind=<host>` > env `HD_BIND=<host>` > default value `localhost`
If your port 8080 is busy, you can specify a different port to use via `--port <number>` command-line flag. If your port 8080 is busy, you can specify a different port to use via `--port <number>` command-line flag.
@@ -74,6 +73,14 @@ If you want to increase the logging verbosity and see all the debug info, use th
> Disclaimer: For the sake of improving the project quality, there is user analytics collected by the tool. You can disable this collecting with `--no-analytics` option. The collection is done via DataDog RUM and Heap Analytics. Only the anonymous data is collected, no sensitive information is used. > Disclaimer: For the sake of improving the project quality, there is user analytics collected by the tool. You can disable this collecting with `--no-analytics` option. The collection is done via DataDog RUM and Heap Analytics. Only the anonymous data is collected, no sensitive information is used.
### Deploying Helm Dashboard on Kubernetes
The official helm chart is [available here](https://github.com/komodorio/helm-charts/blob/master/charts/helm-dashboard)
### Manual Installation
Download the appropriate [release package](https://github.com/komodorio/helm-dashboard/releases) for your platform, unpack it and just run `dashboard` binary from it.
## Scanner Integrations ## Scanner Integrations
Upon startup, Helm Dashboard detects the presence of [Trivy](https://github.com/aquasecurity/trivy) Upon startup, Helm Dashboard detects the presence of [Trivy](https://github.com/aquasecurity/trivy)
@@ -93,7 +100,6 @@ We have two main channels for supporting the Helm Dashboard
users: [Slack community](https://join.slack.com/t/komodorkommunity/shared_invite/zt-1dm3cnkue-ov1Yh~_95teA35QNx5yuMg) for general conversations users: [Slack community](https://join.slack.com/t/komodorkommunity/shared_invite/zt-1dm3cnkue-ov1Yh~_95teA35QNx5yuMg) for general conversations
and [GitHub issues](https://github.com/komodorio/helm-dashboard/issues) for real bugs. and [GitHub issues](https://github.com/komodorio/helm-dashboard/issues) for real bugs.
## Local Dev Testing ## Local Dev Testing
Prerequisites: `helm` and `kubectl` binaries installed and operational. Prerequisites: `helm` and `kubectl` binaries installed and operational.

View File

@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@@ -0,0 +1,9 @@
apiVersion: v2
type: application
name: helm-dashboard
description: A GUI Dashboard for Helm by Komodor
icon: "https://github.com/komodorio/helm-dashboard/blob/main/pkg/dashboard/static/logo.png"
version: 0.1.0
appVersion: "0.0.0"

View File

@@ -0,0 +1,76 @@
# Helm Dashboard
## TL;DR;
```bash
helm repo add komodorio https://helm-charts.komodor.io
helm repo update
helm upgrade --install my-release komodorio/helm-dashboard
```
## Introduction
This chart bootstraps a Helm Dashboard deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
## Prerequisites
- Kubernetes 1.16+
- Helm 3+
## Installing the Chart
To install the chart with the release name `my-release`:
```bash
helm install my-release .
```
The command deploys Helm Dashboard on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation.
> **Tip**: List all releases using `helm list`
## Uninstalling the Chart
To uninstall/delete the `my-release` deployment:
```bash
helm uninstall my-release
```
The command removes all the Kubernetes components associated with the chart and deletes the release.
## Parameters
The following table lists the configurable parameters of the chart and their default values.
| Parameter | Description | Default |
| ------------------------------------ | ---------------------------------------------------------------------------------------------- | ------------------------------------ |
| `image.repository` | Image registry/name | `docker.io/komodorio/helm-dashboard` |
| `image.tag` | Image tag | |
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
| `replicaCount` | Number of dashboard Pods to run | `1` |
| `dashboard.allowWriteActions` | Enables write actions. Allow modifying, deleting and creating charts and kubernetes resources. | `false` |
| `resources.requests.cpu` | CPU resource requests | `200m` |
| `resources.limits.cpu` | CPU resource limits | `1` |
| `resources.requests.memory` | Memory resource requests | `256Mi` |
| `resources.limits.memory` | Memory resource limits | `1Gi` |
| `service.type ` | Kubernetes service type | `ClusterIP` |
| `service.port ` | Kubernetes service port | `8080` |
| `serviceAccount.create` | Creates a service account | `true` |
| `serviceAccount.name` | Optional name for the service account | `{RELEASE_FULLNAME}` |
| `nodeSelector` | Node labels for pod assignment | |
| `affinity` | Affinity settings for pod assignment | |
| `tolerations` | Tolerations for pod assignment | |
| `dashboard.persistence.enabled` | Enable helm data persistene using PVC | `true` |
| `dashboard.persistence.accessModes` | Persistent Volume access modes | `["ReadWriteOnce"]` |
| `dashboard.persistence.storageClass` | Persistent Volume storage class | `""` |
| `dashboard.persistence.size` | Persistent Volume size | `100M` |
| `dashboard.persistence.hostPath` | Set path in case you want to use local host path volumes (not recommended in production) | `""` |
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`.
```bash
helm upgrade --install my-release komodorio/helm-dashboard --set dashboard.allowWriteActions=true --set service.port=9090
```
> **Tip**: You can use the default [values.yaml](values.yaml)

View File

@@ -0,0 +1,16 @@
Thank you for installing Helm Dashboard.
Helm Dashboard can be accessed:
* Within your cluster, at the following DNS name at port {{ .Values.service.port }}:
{{ template "helm-dashboard.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
* From outside the cluster, run these commands in the same shell:
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "helm-dashboard.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
Visit our repo at:
https://github.com/komodorio/helm-dashboard

View File

@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "helm-dashboard.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "helm-dashboard.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "helm-dashboard.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "helm-dashboard.labels" -}}
helm.sh/chart: {{ include "helm-dashboard.chart" . }}
{{ include "helm-dashboard.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "helm-dashboard.selectorLabels" -}}
app.kubernetes.io/name: {{ include "helm-dashboard.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "helm-dashboard.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "helm-dashboard.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,79 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "helm-dashboard.fullname" . }}
labels:
{{- include "helm-dashboard.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "helm-dashboard.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "helm-dashboard.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "helm-dashboard.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: HELM_CACHE_HOME
value: /opt/dashboard/helm/cache
- name: HELM_CONFIG_HOME
value: /opt/dashboard/helm/config
- name: HELM_DATA_HOME
value: /opt/dashboard/helm/data
ports:
- name: http
containerPort: 8080
protocol: TCP
livenessProbe:
httpGet:
path: /status
port: http
readinessProbe:
httpGet:
path: /status
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
- name: data
mountPath: /opt/dashboard/helm
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
- name: data
{{- if .Values.dashboard.persistence.enabled }}
persistentVolumeClaim:
claimName: {{ include "helm-dashboard.fullname" . }}
{{- else }}
emptyDir: {}
{{- end }}

View File

@@ -0,0 +1,61 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "helm-dashboard.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "helm-dashboard.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
pathType: {{ .pathType }}
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}
port:
number: {{ $svcPort }}
{{- else }}
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,48 @@
{{- if .Values.dashboard.persistence.enabled -}}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "helm-dashboard.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "helm-dashboard.labels" . | nindent 4 }}
{{- with .Values.dashboard.persistence.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.dashboard.persistence.hostPath }}
storageClassName: ""
{{- end }}
accessModes:
{{- if not (empty .Values.dashboard.persistence.accessModes) }}
{{- range .Values.dashboard.persistence.accessModes }}
- {{ . | quote }}
{{- end }}
{{- end }}
resources:
requests:
storage: {{ .Values.dashboard.persistence.size | quote }}
{{- end }}
---
{{- if and .Values.dashboard.persistence.enabled .Values.dashboard.persistence.hostPath -}}
apiVersion: v1
kind: PersistentVolume
metadata:
name: {{ include "helm-dashboard.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "helm-dashboard.labels" . | nindent 4 }}
{{- with .Values.dashboard.persistence.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
accessModes:
- {{ .Values.dashboard.persistence.accessMode | quote }}
capacity:
storage: {{ .Values.dashboard.persistence.size | quote }}
hostPath:
path: {{ .Values.dashboard.persistence.hostPath | quote }}
{{- end -}}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "helm-dashboard.fullname" . }}
labels:
{{- include "helm-dashboard.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "helm-dashboard.selectorLabels" . | nindent 4 }}

View File

@@ -0,0 +1,40 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "helm-dashboard.serviceAccountName" . }}
labels:
{{- include "helm-dashboard.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: {{ include "helm-dashboard.serviceAccountName" . }}
rules:
- apiGroups: ["", "apps", "rbac.authorization.k8s.io", "rbac", "batch", "extensions", "networking.k8s.io", "storage.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
{{- if .Values.dashboard.allowWriteActions }}
- apiGroups: ["", "apps", "rbac.authorization.k8s.io", "rbac", "batch", "extensions", "networking.k8s.io", "storage.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch", "create", "delete", "patch", "update"]
{{- end }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "helm-dashboard.serviceAccountName" . }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ include "helm-dashboard.serviceAccountName" . }}
subjects:
- kind: ServiceAccount
namespace: {{ .Release.Namespace }}
name: {{ include "helm-dashboard.serviceAccountName" . }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "helm-dashboard.fullname" . }}-test-connection"
labels:
{{- include "helm-dashboard.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['--timeout=5', '{{ include "helm-dashboard.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

View File

@@ -0,0 +1,95 @@
replicaCount: 1
image:
repository: komodorio/helm-dashboard
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: ""
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1
memory: 1Gi
dashboard:
allowWriteActions: false
persistence:
enabled: true
## If defined, storageClassName: <storageClass>
## If set to "-", storageClassName: "", which disables dynamic provisioning
## If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner. (gp2 on AWS, standard on
## GKE, AWS & OpenStack)
##
storageClass: ""
## Helm Dashboard Persistent Volume access modes
## Must match those of existing PV or dynamic provisioner
## Ref: http://kubernetes.io/docs/user-guide/persistent-volumes/
##
accessModes:
- ReadWriteOnce
## Helm Dashboard Persistent Volume labels
##
labels: {}
## Helm Dashboard Persistent Volume annotations
##
annotations: {}
## Set path in case you want to use local host path volumes (not recommended in production)
##
hostPath: ""
## Helm Dashboard data Persistent Volume size
##
size: 100M
podAnnotations: {}
podSecurityContext: {}
securityContext: {}
service:
type: ClusterIP
port: 8080
ingress:
enabled: false
className: ""
annotations: {}
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}

14
ci/bump-versions.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
WORKING_DIRECTORY="$PWD"
[ -z "$HELM_CHARTS_SOURCE" ] && HELM_CHARTS_SOURCE="$WORKING_DIRECTORY/charts/helm-dashboard"
[ -z "$APP_VERSION" ] && {
APP_VERSION=$(cat ${HELM_CHARTS_SOURCE}/Chart.yaml | grep 'appVersion:' | awk -F'"' '{print $2}')
}
sed -i -e "s/appVersion.*/appVersion: \"${APP_VERSION}\" /g" ${HELM_CHARTS_SOURCE}/Chart.yaml
CURRENT_VERSION=$(cat ${HELM_CHARTS_SOURCE}/Chart.yaml | grep 'version:' | awk '{print $2}')
NEW_VERSION=$(echo $CURRENT_VERSION | awk -F. '{$NF = $NF + 1;} 1' | sed 's/ /./g')
sed -i -e "s/${CURRENT_VERSION}/${NEW_VERSION}/g" ${HELM_CHARTS_SOURCE}/Chart.yaml

View File

@@ -37,6 +37,7 @@ func main() {
opts.BindHost = host opts.BindHost = host
} }
opts.Verbose = opts.Verbose || os.Getenv("DEBUG") != ""
setupLogging(opts.Verbose) setupLogging(opts.Verbose)
server := dashboard.Server{ server := dashboard.Server{

View File

@@ -29,6 +29,11 @@ func (s Server) StartServer() (string, utils.ControlChan) {
data := subproc.DataLayer{ data := subproc.DataLayer{
Namespace: s.Namespace, Namespace: s.Namespace,
Cache: subproc.NewCache(), Cache: subproc.NewCache(),
StatusInfo: &subproc.StatusInfo{
CurVer: s.Version,
Analytics: false,
LimitedToNamespace: s.Namespace,
},
} }
err := data.CheckConnectivity() err := data.CheckConnectivity()
if err != nil { if err != nil {
@@ -36,12 +41,7 @@ func (s Server) StartServer() (string, utils.ControlChan) {
os.Exit(1) // TODO: propagate error instead? os.Exit(1) // TODO: propagate error instead?
} }
isDevModeWithAnalytics := os.Getenv("HD_DEV_ANALYTICS") == "true" isDevModeWithAnalytics := os.Getenv("HD_DEV_ANALYTICS") == "true"
enableAnalytics := (!s.NoTracking && s.Version != "0.0.0") || isDevModeWithAnalytics data.StatusInfo.Analytics = (!s.NoTracking && s.Version != "0.0.0") || isDevModeWithAnalytics
data.StatusInfo = &subproc.StatusInfo{
CurVer: s.Version,
Analytics: enableAnalytics,
LimitedToNamespace: s.Namespace,
}
go checkUpgrade(data.StatusInfo) go checkUpgrade(data.StatusInfo)
discoverScanners(&data) discoverScanners(&data)
@@ -113,7 +113,7 @@ func discoverScanners(data *subproc.DataLayer) {
} }
} }
func checkUpgrade(d *subproc.StatusInfo) { func checkUpgrade(d *subproc.StatusInfo) { // TODO: check it once an hour
url := "https://api.github.com/repos/komodorio/helm-dashboard/releases/latest" url := "https://api.github.com/repos/komodorio/helm-dashboard/releases/latest"
type GHRelease struct { type GHRelease struct {
Name string `json:"name"` Name string `json:"name"`

View File

@@ -13,7 +13,11 @@ function revisionClicked(namespace, name, self) {
$("#sectionDetails .rev-tags .rev-chart").text(elm.chart) $("#sectionDetails .rev-tags .rev-chart").text(elm.chart)
$("#sectionDetails .rev-tags .rev-app").text(elm.app_version) $("#sectionDetails .rev-tags .rev-app").text(elm.app_version)
$("#sectionDetails .rev-tags .rev-ns").text(getHashParam("namespace")) $("#sectionDetails .rev-tags .rev-ns").text(getHashParam("namespace"))
if (getHashParam("context")) {
$("#sectionDetails .rev-tags .rev-cluster").text(getHashParam("context")) $("#sectionDetails .rev-tags .rev-cluster").text(getHashParam("context"))
} else {
$("#sectionDetails .rev-tags .rev-cluster").parent().hide() // TODO: makes UI jumpy, change to showing
}
$("#revDescr").text(elm.description).removeClass("text-danger") $("#revDescr").text(elm.description).removeClass("text-danger")
if (elm.status === "failed") { if (elm.status === "failed") {
@@ -158,7 +162,9 @@ function showResources(namespace, chart, revision) {
} }
resBody.empty(); resBody.empty();
data = data.sort(function(a, b){return interestingResources.indexOf(a.kind.toUpperCase()) - interestingResources.indexOf(b.kind.toUpperCase())}).reverse(); data = data.sort(function (a, b) {
return interestingResources.indexOf(a.kind.toUpperCase()) - interestingResources.indexOf(b.kind.toUpperCase())
}).reverse();
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
const res = data[i] const res = data[i]
const resBlock = $(` const resBlock = $(`

View File

@@ -75,24 +75,6 @@
</div> </div>
<div class="separator-vertical"><span></span></div> <div class="separator-vertical"><span></span></div>
<i class="btn bi-power text-muted p-2 m-1 mx-2" title="Shut down the Helm Dashboard application"></i> <i class="btn bi-power text-muted p-2 m-1 mx-2" title="Shut down the Helm Dashboard application"></i>
<!-- Modal -->
<div class="modal fade" id="PowerOffModal" tabindex="-1" aria-labelledby="ModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="ModalLabel">Message</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Close this browser tab now.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div> </div>
</nav> </nav>
<!-- /TOP BAR --> <!-- /TOP BAR -->
@@ -144,9 +126,11 @@
<!-- FILTER BLOCK --> <!-- FILTER BLOCK -->
<div class="p-2 ps-2 bg-white rounded-1 b-shadow" id="filters"> <div class="p-2 ps-2 bg-white rounded-1 b-shadow" id="filters">
<form> <form>
<div id="clusterFilterBlock">
<h4>Clusters</h4> <h4>Clusters</h4>
<ul class="list-unstyled" id="cluster"> <ul class="list-unstyled" id="cluster">
</ul> </ul>
</div>
<h4 class="mt-4">Namespaces</h4> <h4 class="mt-4">Namespaces</h4>
<p id="limitNamespace" class="display-none ps-3"><span class="fw-bold"></span> (forced)</p> <p id="limitNamespace" class="display-none ps-3"><span class="fw-bold"></span> (forced)</p>
@@ -165,7 +149,8 @@
<h2 class="m-0 p-1"><img class="m-2 mx-3 me-2" src="static/helm-gray.svg" alt="Installed Charts">Installed <h2 class="m-0 p-1"><img class="m-2 mx-3 me-2" src="static/helm-gray.svg" alt="Installed Charts">Installed
Charts (<span></span>)</h2> Charts (<span></span>)</h2>
<div class="form-outline w-25"> <div class="form-outline w-25">
<input type="text" id="installedSearch" class="form-control form-control-sm" placeholder="Filter..." /> <input type="text" id="installedSearch" class="form-control form-control-sm"
placeholder="Filter..."/>
</div> </div>
</div> </div>
<div class="bg-secondary rounded-bottom m-0 row p-2"> <div class="bg-secondary rounded-bottom m-0 row p-2">
@@ -182,7 +167,8 @@
<div class="bg-white rounded shadow p-3 display-none no-charts">Looks like you don't have any charts <div class="bg-white rounded shadow p-3 display-none no-charts">Looks like you don't have any charts
installed. "Repository" section may be a good place to start. installed. "Repository" section may be a good place to start.
</div> </div>
<div class="bg-white rounded shadow p-3 display-none all-filtered">There are no releases matching your filter criteria. Reset your filters or install more charts. <div class="bg-white rounded shadow p-3 display-none all-filtered">There are no releases matching your
filter criteria. Reset your filters or install more charts.
</div> </div>
</div> </div>
<!-- /INSTALLED LIST --> <!-- /INSTALLED LIST -->
@@ -430,6 +416,21 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Modal -->
<div class="modal fade" id="PowerOffModal" tabindex="-1" aria-labelledby="ModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="ModalLabel">Session Ended</h5>
</div>
<div class="modal-body">
The Helm Dashboard application has been shut down. You can now close the browser tab.
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>

View File

@@ -132,7 +132,8 @@ function repoChartClicked() {
window.location.reload() window.location.reload()
} else { } else {
const contexts = $("body").data("contexts") const contexts = $("body").data("contexts")
const contextNamespace = contexts.filter(obj => {return obj.Name === getHashParam("context")})[0].Namespace const ctxFiltered = contexts.filter(obj => {return obj.Name === getHashParam("context")});
const contextNamespace = ctxFiltered.length?ctxFiltered[0].Namespace:""
popUpUpgrade(elm, contextNamespace) popUpUpgrade(elm, contextNamespace)
} }
} }

View File

@@ -3,12 +3,18 @@ $(function () {
$.getJSON("/status").fail(function (xhr) { // maybe /options call in the future $.getJSON("/status").fail(function (xhr) { // maybe /options call in the future
reportError("Failed to get tool version", xhr) reportError("Failed to get tool version", xhr)
}).done(function (data) { }).done(function (data) {
$("body").data("status", data)
fillToolVersion(data) fillToolVersion(data)
limNS = data.LimitedToNamespace limNS = data.LimitedToNamespace
if (limNS) { if (limNS) {
$("#limitNamespace").show().find("span").text(limNS) $("#limitNamespace").show().find("span").text(limNS)
} }
fillClusters(limNS) fillClusters(limNS)
if (data.ClusterMode) {
$(".bi-power").hide()
$("#clusterFilterBlock").hide()
}
}) })
$.getJSON("/api/scanners").fail(function (xhr) { $.getJSON("/api/scanners").fail(function (xhr) {
@@ -220,7 +226,7 @@ function fillNamespaceList(data) {
if (filteredNamespace.split('+').includes(elm.metadata.name)) { if (filteredNamespace.split('+').includes(elm.metadata.name)) {
opt.find("input").prop("checked", true) opt.find("input").prop("checked", true)
} }
} else if (curContextNamespaces && curContextNamespaces[0].Namespace === elm.metadata.name) { } else if (curContextNamespaces.length && curContextNamespaces[0].Namespace === elm.metadata.name) {
opt.find("input").prop("checked", true) opt.find("input").prop("checked", true)
setFilteredNamespaces([elm.metadata.name]) setFilteredNamespaces([elm.metadata.name])
} }

View File

@@ -35,6 +35,7 @@ type StatusInfo struct {
Analytics bool Analytics bool
LimitedToNamespace string LimitedToNamespace string
CacheHitRatio float64 CacheHitRatio float64
ClusterMode bool
} }
func (d *DataLayer) runCommand(cmd ...string) (string, error) { func (d *DataLayer) runCommand(cmd ...string) (string, error) {
@@ -74,10 +75,17 @@ func (d *DataLayer) CheckConnectivity() error {
} }
if len(contexts) < 1 { if len(contexts) < 1 {
log.Debugf("Did not find any contexts, will try checking k8s")
_, err := d.runCommandKubectl("get", "pods")
if err != nil {
log.Debugf("The error were: %s", err)
return errors.New("did not find any kubectl contexts configured") return errors.New("did not find any kubectl contexts configured")
} }
log.Infof("Assuming k8s environment")
d.StatusInfo.ClusterMode = true
}
_, err = d.runCommandHelm("--help") // no point in doing is, since the default context may be invalid _, err = d.runCommandHelm("--help")
if err != nil { if err != nil {
return err return err
} }

View File

@@ -3,6 +3,7 @@ package subproc
import ( import (
"encoding/json" "encoding/json"
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1" v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
"os"
"regexp" "regexp"
"sort" "sort"
"strings" "strings"
@@ -31,6 +32,12 @@ type KubeContext struct {
} }
func (d *DataLayer) ListContexts() (res []KubeContext, err error) { func (d *DataLayer) ListContexts() (res []KubeContext, err error) {
res = []KubeContext{}
if os.Getenv("HD_CLUSTER_MODE") != "" {
return res, nil
}
out, err := d.runCommandKubectl("config", "get-contexts") out, err := d.runCommandKubectl("config", "get-contexts")
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -12,6 +12,7 @@ import (
func (d *DataLayer) ChartRepoList() (res []RepositoryElement, err error) { func (d *DataLayer) ChartRepoList() (res []RepositoryElement, err error) {
out, err := d.Cache.String(CacheKeyAllRepos, nil, func() (string, error) { out, err := d.Cache.String(CacheKeyAllRepos, nil, func() (string, error) {
// TODO: do a bg check, if the state is changed - do reset some caches
return d.runCommandHelm("repo", "list", "--output", "json") return d.runCommandHelm("repo", "list", "--output", "json")
}) })
if err != nil { if err != nil {

View File

@@ -11,6 +11,7 @@ if [ -n "${HELM_PUSH_PLUGIN_NO_INSTALL_HOOK}" ]; then
fi fi
version="$(cat plugin.yaml | grep "version" | cut -d '"' -f 2)" version="$(cat plugin.yaml | grep "version" | cut -d '"' -f 2)"
# TODO: if no version provided, get it from https://api.github.com/repos/komodorio/helm-dashboard/releases/latest
echo "Downloading and installing ${name} v${version} ..." echo "Downloading and installing ${name} v${version} ..."
url="" url=""