Skip to content

Commit

Permalink
feat: add helm kcl template alpha command.
Browse files Browse the repository at this point in the history
  • Loading branch information
Peefy committed Mar 30, 2023
1 parent 3da1aff commit 016caa2
Show file tree
Hide file tree
Showing 26 changed files with 1,570 additions and 4 deletions.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,31 @@ You can use the `Helm-KCL-Plugin` to
+ For multi-environment and multi-tenant scenarios, you can maintain these configurations gracefully rather than simply copy and paste.
+ Validate all KRM resources using the KCL schema.

## Prerequisites

+ Install Helm
+ Golang (at least version 1.18)

## Test the Plugin

You need to put your KCL script source in the functionConfig of kind KCLRun and then the function will run the KCL script that you provide.

```bash
# Verify that the annotation is added to the `Deployment` resource and the other resource `Service`
# does not have this annotation.
diff \
<(helm template ./examples/workload-charts-with-kcl/workload-charts) \
<(go run main.go template --file ./examples/workload-charts-with-kcl/kcl-run.yaml) |\
grep annotations -A1
```

The output is

```diff
> annotations:
> managed-by: helm-kcl-plugin
```

## Install

### Using Helm plugin manager (> 2.3.x)
Expand Down Expand Up @@ -63,6 +88,12 @@ make install
make install/helm3
```

## Quick Start

```shell
helm create my-chart
```

## Build

### Prerequisites
Expand Down
4 changes: 3 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ func New() *cobra.Command {
Use: "kcl",
Short: "Edit, transformer, validate Helm charts using the KCL programming language.",
Long: rootCmdLongUsage,
SilenceUsage: true,
}

cmd.AddCommand(newVersionCmd())
cmd.AddCommand(NewVersionCmd())
cmd.AddCommand(NewTemplateCmd())
cmd.SetHelpCommand(&cobra.Command{}) // Disable the help command
return cmd
}
52 changes: 52 additions & 0 deletions cmd/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package cmd

import (
"github.com/spf13/cobra"

"kusionstack.io/helm-kcl/pkg/app"
"kusionstack.io/helm-kcl/pkg/config"
)

// NewTemplateCmd returm template subcmd
func NewTemplateCmd() *cobra.Command {
templateOptions := config.NewTemplateOptions()

cmd := &cobra.Command{
Use: "template",
Short: "Template releases defined in the KCL state file",
PreRun: func(*cobra.Command, []string) {
app.ExpandTLSPaths()
},
RunE: func(*cobra.Command, []string) error {
err := app.New().Template(config.NewTemplateImpl(templateOptions))
if err != nil {
return err
}
return nil
},
SilenceUsage: true,
}

f := cmd.Flags()
f.StringVar(&templateOptions.File, "file", "", "input kcl file to pass to helm kcl template")
f.StringArrayVar(&templateOptions.Set, "set", nil, "additional values to be merged into the helm command --set flag")
f.StringArrayVar(&templateOptions.Values, "values", nil, "additional value files to be merged into the helm command --values flag")
f.StringVar(&templateOptions.OutputDir, "output-dir", "", "output directory to pass to helm template (helm template --output-dir)")
f.StringVar(&templateOptions.OutputDirTemplate, "output-dir-template", "", "go text template for generating the output directory. Default: {{ .OutputDir }}/{{ .State.BaseName }}-{{ .State.AbsPathSHA1 }}-{{ .Release.Name}}")
f.IntVar(&templateOptions.Concurrency, "concurrency", 0, "maximum number of concurrent helm processes to run, 0 is unlimited")
f.BoolVar(&templateOptions.Validate, "validate", false, "validate your manifests against the Kubernetes cluster you are currently pointing at. Note that this requires access to a Kubernetes cluster to obtain information necessary for validating, like the template of available API versions")
f.BoolVar(&templateOptions.IncludeCRDs, "include-crds", false, "include CRDs in the templated output")
f.BoolVar(&templateOptions.SkipTests, "skip-tests", false, "skip tests from templated output")
f.BoolVar(&templateOptions.SkipNeeds, "skip-needs", true, `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`)
f.BoolVar(&templateOptions.IncludeNeeds, "include-needs", false, `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when --selector/-l flag is not provided`)
f.BoolVar(&templateOptions.IncludeTransitiveNeeds, "include-transitive-needs", false, `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`)
f.BoolVar(&templateOptions.SkipDeps, "skip-deps", false, `skip running "helm repo update" and "helm dependency build"`)
f.BoolVar(&templateOptions.SkipCleanup, "skip-cleanup", false, "Stop cleaning up temporary values generated by helmfile and helm-secrets. Useful for debugging. Don't use in production for security")
f.StringVar(&templateOptions.PostRenderer, "post-renderer", "", `pass --post-renderer to "helm template" or "helm upgrade --install"`)

if !app.IsHelm3() {
app.AddCommonCmdOptions(f)
}

return cmd
}
3 changes: 2 additions & 1 deletion cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import (
// Version identifier populated via the CI/CD process.
var Version = "HEAD"

func newVersionCmd() *cobra.Command {
func NewVersionCmd() *cobra.Command {
return &cobra.Command{
Use: "version",
Short: "Show version of the helm kcl plugin",
Run: func(*cobra.Command, []string) {
fmt.Println(Version)
},
SilenceUsage: true,
}
}
17 changes: 17 additions & 0 deletions examples/workload-charts-with-kcl/kcl-run.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# kcl-config.yaml
apiVersion: fn.kpt.dev/v1alpha1
kind: KCLRun
metadata:
name: set-annotation
# EDIT THE SOURCE!
# This should be your KCL code which preloads the `ResourceList` to `option("resource_list")
source: |
[resource | {if resource.kind == "Deployment": metadata.annotations: {"managed-by" = "helm-kcl-plugin"}} for resource in option("resource_list").items]
repositories:
- name: workload
path: ./workload-charts
# TDDO:
# url
# git
# oci
23 changes: 23 additions & 0 deletions examples/workload-charts-with-kcl/workload-charts/.helmignore
Original file line number Diff line number Diff line change
@@ -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/
6 changes: 6 additions & 0 deletions examples/workload-charts-with-kcl/workload-charts/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v2
appVersion: 0.1.0
description: A helm chart to provision standard workloads.
name: workload
type: application
version: 0.1.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "workload.name" -}}
{{- default .Release.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 "workload.fullname" -}}
{{- $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 }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "workload.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "workload.labels" -}}
helm.sh/chart: {{ include "workload.chart" . }}
{{ include "workload.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "workload.selectorLabels" -}}
app.kubernetes.io/name: {{ include "workload.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "workload.name" . }}
labels:
{{- include "workload.labels" . | nindent 4 }}
spec:
selector:
matchLabels:
{{- include "workload.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "workload.selectorLabels" . | nindent 8 }}
spec:
containers:
{{- range $name, $container := .Values.containers }}
- name: {{ $name }}
image: "{{ $container.image.name }}"
{{- with $container.command }}
command:
{{- toYaml $container.command | nindent 12 }}
{{- end }}
{{- with $container.args }}
args:
{{- toYaml $container.args | nindent 12 }}
{{- end }}
{{- with $container.env }}
env:
{{- toYaml $container.env | nindent 12 }}
{{- end }}
{{- with $container.volumeMounts }}
volumeMounts:
{{- toYaml $container.volumeMounts | nindent 12 }}
{{- end }}
{{- with $container.livenessProbe }}
livenessProbe:
{{- toYaml $container.livenessProbe | nindent 12 }}
{{- end }}
{{- with $container.readinessProbe }}
readinessProbe:
{{- toYaml $container.readinessProbe | nindent 12 }}
{{- end }}
{{- with $container.resources }}
resources:
{{- toYaml $container.resources | nindent 12 }}
{{- end }}
{{- end }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{{ if .Values.service }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "workload.name" . }}
labels:
{{- include "workload.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
selector:
{{- include "workload.selectorLabels" . | nindent 4 }}
{{- with .Values.service.ports }}
ports:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
16 changes: 16 additions & 0 deletions examples/workload-charts-with-kcl/workload-charts/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#
# Default values for the chart (for reference only).
# An actual values file is rendered from the source SCORE file by the CLI tool.

containers:
frontend:
image:
name: nginx:alpine

service:
type: ClusterIP
ports:
- name: www
protocol: TCP
port: 80
targetPort: 80
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
# Source: workload/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: release-name
labels:
helm.sh/chart: workload-0.1.0
app.kubernetes.io/name: release-name
app.kubernetes.io/instance: release-name
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/managed-by: Helm
spec:
selector:
matchLabels:
app.kubernetes.io/name: release-name
app.kubernetes.io/instance: release-name
template:
metadata:
labels:
app.kubernetes.io/name: release-name
app.kubernetes.io/instance: release-name
spec:
containers:
- name: frontend
image: "nginx:alpine"
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
# Source: workload/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: release-name
labels:
helm.sh/chart: workload-0.1.0
app.kubernetes.io/name: release-name
app.kubernetes.io/instance: release-name
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/managed-by: Helm
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: release-name
app.kubernetes.io/instance: release-name
ports:
- name: www
port: 80
protocol: TCP
targetPort: 80
Loading

0 comments on commit 016caa2

Please sign in to comment.