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

change in flags and templates to implement optional secrets #159

Closed
wants to merge 13 commits into from
Closed
32 changes: 30 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,18 @@ jobs:
cache: false

- name: Fmt
run: go fmt ./...

run: |
# Run gofmt in "diff" mode to check for unformatted code
UNFORMATTED_FILES=$(gofmt -l .)
# Check if any files are unformatted
if [[ -n "$UNFORMATTED_FILES" ]]; then
echo "::error::The following Go files are not formatted correctly:"
echo "$UNFORMATTED_FILES" # List unformatted files in the log
echo "::error::Please format your Go code by running \`go fmt ./...\` and commit the changes."
exit 1 # Fail the check
else
echo "All Go files are properly formatted."
fi
- name: Vet
run: go vet ./...

Expand All @@ -33,6 +43,24 @@ jobs:
uses: golangci/golangci-lint-action@v3
with:
version: v1.54
# Generate example charts
- name: Generate example charts
run: |
cat test_data/sample-app.yaml | go run ./cmd/helmify examples/app
cat test_data/k8s-operator-kustomize.output | go run ./cmd/helmify examples/operator
- name: Check that chart examples were commited
run: |
if [[ -n "$(git status --porcelain)" ]]; then
# Capture the list of uncommitted files
UNCOMMITTED_FILES=$(git status --porcelain)
echo "::error::Chart examples generation step has uncommitted changes: $UNCOMMITTED_FILES
Please run following commands and commit the results:
- \`cat test_data/sample-app.yaml | go run ./cmd/helmify examples/app\`
- \`cat test_data/k8s-operator-kustomize.output | go run ./cmd/helmify examples/operator\`"
exit 1
else
echo "Chart examples generation check passed. No uncommitted changes."
fi
# Dry-run generated charts in cluster
- name: Install k8s cluster
uses: helm/[email protected]
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ Usage:
| -cert-manager-version | Allows the user to specify cert-manager subchart version. Only useful with cert-manager-as-subchart. (default "v1.12.2") | `helmify -cert-manager-version=v1.12.2` |
| -cert-manager-install-crd | Allows the user to install cert-manager CRD as part of the cert-manager subchart.(default "true") | `helmify -cert-manager-install-crd` |
| -preserve-ns | Allows users to use the object's original namespace instead of adding all the resources to a common namespace. (default "false") | `helmify -preserve-ns` |
| -add-webhook-option | Adds an option to enable/disable webhook installation | `helmify -add-webhook-option`|
## Status
Supported k8s resources:
- Deployment, DaemonSet, StatefulSet
Expand Down Expand Up @@ -162,3 +163,26 @@ go test ./...
Beside unit-tests, project contains e2e test `pkg/app/app_e2e_test.go`.
It's a go test, which uses `test_data/*` to generate a chart in temporary directory.
Then runs `helm lint --strict` to check if generated chart is valid.

## Contribute

Following rules will help changes to be accepted faster:
- For more than one-line bugfixes consider creating an issue with bug description or feature request
- For feature request try to think about and cover following topics (when applicable):
- Motivation: why feature is needed? Which problem does it solve? What is current workaround?
- Backward-compatibility: existing users expect that after upgrading helmify version their existing generated charts wont be changed without consent.
- For bugfix PR consider adding example to [/test_data](./test_data/) source yamls reproducing bug.

### Contribution flow

Check list before submitting PR:
1. Run `go fmt ./...`
2. Run tests `go test ./...`
3. Update chart examples:
```shell
cat test_data/sample-app.yaml | go run ./cmd/helmify examples/app
```
```shell
cat test_data/k8s-operator-kustomize.output | go run ./cmd/helmify examples/operator
```
4. In case of long commit history (more than 3) squash local commits into one
5 changes: 5 additions & 0 deletions cmd/helmify/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func (i *arrayFlags) Set(value string) error {
// ReadFlags command-line flags into app config.
func ReadFlags() config.Config {
files := arrayFlags{}
optionalSecrets := arrayFlags{}
result := config.Config{}
var h, help, version, crd, preservens bool
flag.BoolVar(&h, "h", false, "Print help. Example: helmify -h")
Expand All @@ -69,7 +70,10 @@ func ReadFlags() config.Config {
flag.BoolVar(&result.FilesRecursively, "r", false, "Scan dirs from -f option recursively")
flag.BoolVar(&result.OriginalName, "original-name", false, "Use the object's original name instead of adding the chart's release name as the common prefix.")
flag.Var(&files, "f", "File or directory containing k8s manifests")
flag.Var(&optionalSecrets, "optional-secrets", "List of secrets to be templated as optional (their values will not be required).")
flag.BoolVar(&preservens, "preserve-ns", false, "Use the object's original namespace instead of adding all the resources to a common namespace")
flag.BoolVar(&result.AddWebhookOption, "add-webhook-option", false, "Allows the user to add webhook option in values.yaml")

flag.Parse()
if h || help {
fmt.Print(helpText)
Expand All @@ -92,5 +96,6 @@ func ReadFlags() config.Config {
result.PreserveNs = true
}
result.Files = files
result.OptionalSecrets = optionalSecrets
return result
}
10 changes: 9 additions & 1 deletion examples/app/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ spec:
secretKeyRef:
key: VAR2
name: {{ include "app.fullname" . }}-my-secret-vars
- name: APP_NAME
valueFrom:
fieldRef:
fieldPath: metadata.labels['app.kubernetes.io/name']
- name: INSTANCE_NAME
valueFrom:
fieldRef:
fieldPath: metadata.labels['app.kubernetes.io/instance']
- name: KUBERNETES_CLUSTER_DOMAIN
value: {{ quote .Values.kubernetesClusterDomain }}
image: {{ .Values.myapp.app.image.repository }}:{{ .Values.myapp.app.image.tag
Expand Down Expand Up @@ -78,7 +86,7 @@ spec:
- command:
- /bin/sh
- -c
- echo Initializing container...
- echo 'Initializing container...'
env:
- name: KUBERNETES_CLUSTER_DOMAIN
value: {{ quote .Values.kubernetesClusterDomain }}
Expand Down
16 changes: 16 additions & 0 deletions examples/app/templates/myapp-lb-service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "app.fullname" . }}-myapp-lb-service
labels:
app: myapp
{{- include "app.labels" . | nindent 4 }}
spec:
type: {{ .Values.myappLbService.type }}
selector:
app: myapp
{{- include "app.selectorLabels" . | nindent 4 }}
ports:
{{- .Values.myappLbService.ports | toYaml | nindent 2 }}
loadBalancerSourceRanges:
{{- .Values.myappLbService.loadBalancerSourceRanges | toYaml | nindent 2 }}
4 changes: 2 additions & 2 deletions examples/app/templates/myapp-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ spec:
type: {{ .Values.myappService.type }}
selector:
app: myapp
{{- include "app.selectorLabels" . | nindent 4 }}
{{- include "app.selectorLabels" . | nindent 4 }}
ports:
{{- .Values.myappService.ports | toYaml | nindent 2 }}
{{- .Values.myappService.ports | toYaml | nindent 2 }}
4 changes: 2 additions & 2 deletions examples/app/templates/nginx.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ spec:
type: {{ .Values.nginx.type }}
selector:
app: nginx
{{- include "app.selectorLabels" . | nindent 4 }}
{{- include "app.selectorLabels" . | nindent 4 }}
ports:
{{- .Values.nginx.ports | toYaml | nindent 2 }}
{{- .Values.nginx.ports | toYaml | nindent 2 }}
8 changes: 8 additions & 0 deletions examples/app/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ myapp:
tag: v0.8.0
replicas: 3
revisionHistoryLimit: 5
myappLbService:
loadBalancerSourceRanges:
- 10.0.0.0/8
ports:
- name: https
port: 8443
targetPort: https
type: LoadBalancer
myappPdb:
minAvailable: 2
myappService:
Expand Down
4 changes: 2 additions & 2 deletions examples/operator/templates/metrics-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ spec:
type: {{ .Values.metricsService.type }}
selector:
control-plane: controller-manager
{{- include "operator.selectorLabels" . | nindent 4 }}
{{- include "operator.selectorLabels" . | nindent 4 }}
ports:
{{- .Values.metricsService.ports | toYaml | nindent 2 }}
{{- .Values.metricsService.ports | toYaml | nindent 2 }}
4 changes: 2 additions & 2 deletions examples/operator/templates/webhook-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ spec:
type: {{ .Values.webhookService.type }}
selector:
control-plane: controller-manager
{{- include "operator.selectorLabels" . | nindent 4 }}
{{- include "operator.selectorLabels" . | nindent 4 }}
ports:
{{- .Values.webhookService.ports | toYaml | nindent 2 }}
{{- .Values.webhookService.ports | toYaml | nindent 2 }}
7 changes: 7 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package config

import (
"fmt"

"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/util/validation"
)
Expand Down Expand Up @@ -40,6 +41,12 @@ type Config struct {
OriginalName bool
// PreserveNs retains the namespaces on the Kubernetes manifests
PreserveNs bool
// OptionalSecrets - list of secrets that are optional and should only be generated if values are given
OptionalSecrets []string
// AddWebhookOption enables the generation of a webhook option in values.yamlß
AddWebhookOption bool
// OptionalSecrets - list of secrets that are optional and should only be generated if values are given
OptionalSecrets []string
}

func (c *Config) Validate() error {
Expand Down
14 changes: 9 additions & 5 deletions pkg/helmify/values.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package helmify

import (
"dario.cat/mergo"
"fmt"
"strconv"
"strings"

"dario.cat/mergo"

"github.com/iancoleman/strcase"

"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
Expand Down Expand Up @@ -71,12 +72,15 @@ func (v *Values) AddYaml(value interface{}, indent int, newLine bool, name ...st

// AddSecret - adds empty value to values and returns its helm template representation {{ required "<valueName>" .Values.<valueName> }}.
// Set toBase64=true for Secret data to be base64 encoded and set false for Secret stringData.
func (v *Values) AddSecret(toBase64 bool, name ...string) (string, error) {
func (v *Values) AddSecret(toBase64 bool, optionalSecret bool, name ...string) (string, error) {
name = toCamelCase(name)
nameStr := strings.Join(name, ".")
err := unstructured.SetNestedField(*v, "", name...)
if err != nil {
return "", fmt.Errorf("%w: unable to set value: %v", err, nameStr)
var err error = nil
if !optionalSecret {
err = unstructured.SetNestedField(*v, "", name...)
if err != nil {
return "", fmt.Errorf("%w: unable to set value: %v", err, nameStr)
}
}
res := fmt.Sprintf(`{{ required "%[1]s is required" .Values.%[1]s`, nameStr)
if toBase64 {
Expand Down
4 changes: 2 additions & 2 deletions pkg/helmify/values_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ func TestValues_Add(t *testing.T) {
func TestValues_AddSecret(t *testing.T) {
t.Run("add base64 enc secret", func(t *testing.T) {
testVal := Values{}
res, err := testVal.AddSecret(true, "a", "b")
res, err := testVal.AddSecret(true, true, "a", "b")
assert.NoError(t, err)
assert.Contains(t, res, "b64enc")
})
t.Run("add not encoded secret", func(t *testing.T) {
testVal := Values{}
res, err := testVal.AddSecret(false, "a", "b")
res, err := testVal.AddSecret(false, true, "a", "b")
assert.NoError(t, err)
assert.NotContains(t, res, "b64enc")
})
Expand Down
35 changes: 34 additions & 1 deletion pkg/processor/deployment/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package deployment
import (
"fmt"
"io"
"regexp"
"strings"
"text/template"

Expand Down Expand Up @@ -128,8 +129,11 @@ func (d deployment) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstr
if err != nil {
return true, nil, err
}
if appMeta.Config().AddWebhookOption {
spec = addWebhookOption(spec)
}

spec = strings.ReplaceAll(spec, "'", "")
spec = replaceSingleQuotes(spec)

return true, &result{
values: values,
Expand All @@ -153,6 +157,35 @@ func (d deployment) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstr
}, nil
}

func replaceSingleQuotes(s string) string {
r := regexp.MustCompile(`'({{((.*|.*\n.*))}}.*)'`)
return r.ReplaceAllString(s, "${1}")
}

func addWebhookOption(manifest string) string {
webhookOptionHeader := " {{- if .Values.webhook.enabled }}"
webhookOptionFooter := " {{- end }}"
volumes := ` - name: cert
secret:
defaultMode: 420
secretName: webhook-server-cert`
volumeMounts := ` - mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true`
manifest = strings.ReplaceAll(manifest, volumes, fmt.Sprintf("%s\n%s\n%s",
webhookOptionHeader, volumes, webhookOptionFooter))
manifest = strings.ReplaceAll(manifest, volumeMounts, fmt.Sprintf("%s\n%s\n%s",
webhookOptionHeader, volumeMounts, webhookOptionFooter))

re := regexp.MustCompile(` - containerPort: \d+
name: webhook-server
protocol: TCP`)

manifest = re.ReplaceAllString(manifest, fmt.Sprintf("%s\n%s\n%s", webhookOptionHeader,
re.FindString(manifest), webhookOptionFooter))
return manifest
}

func processReplicas(name string, deployment *appsv1.Deployment, values *helmify.Values) (string, error) {
if deployment.Spec.Replicas == nil {
return "", nil
Expand Down
Loading