├── .go-version
├── tools
├── DEEPCOPY_GEN_VERSION
├── GOLANGCI_LINT_VERSION
├── GORELEASER_VERSION
├── parse-tags.sh
├── boilerplate.go.txt
└── make
│ ├── generate.mk
│ ├── release.mk
│ ├── clean.mk
│ ├── lint.mk
│ ├── test.mk
│ ├── build.mk
│ └── container.mk
├── .shellcheckrc
├── pkg
├── cmd
│ ├── interpolate
│ │ └── testdata
│ │ │ ├── folder
│ │ │ ├── ignored
│ │ │ ├── inner
│ │ │ │ └── inner.yaml
│ │ │ ├── no-interpolation.yaml
│ │ │ └── other-file.yml
│ │ │ ├── results-contained
│ │ │ ├── inner.yaml
│ │ │ ├── no-interpolation.yaml
│ │ │ └── other-file.yml
│ │ │ ├── missing-env.yaml
│ │ │ ├── results
│ │ │ ├── no-interpolation.yaml
│ │ │ ├── other-file.yml
│ │ │ └── file.yaml
│ │ │ ├── file.yaml
│ │ │ ├── stdin
│ │ │ └── output.yaml
│ │ │ └── results-missing-path
│ │ │ └── file.yaml
│ ├── kustomize
│ │ ├── testdata
│ │ │ └── kustomization.yaml
│ │ ├── kustomize.go
│ │ └── kustomize_test.go
│ ├── deploy
│ │ ├── testdata
│ │ │ ├── resources
│ │ │ │ ├── configmap.yaml
│ │ │ │ ├── secret.yaml
│ │ │ │ ├── external-secret.yaml
│ │ │ │ ├── store.yaml
│ │ │ │ ├── cronjob.yml
│ │ │ │ └── deployment.yaml
│ │ │ ├── error-resources
│ │ │ │ ├── configmap.yaml
│ │ │ │ ├── secret.yaml
│ │ │ │ ├── cronjob.yml
│ │ │ │ └── deployment.yaml
│ │ │ └── expectations
│ │ │ │ ├── configmap.yaml
│ │ │ │ ├── store.yaml
│ │ │ │ ├── external-secret.yaml
│ │ │ │ ├── cronjob.yaml
│ │ │ │ ├── job.yaml
│ │ │ │ └── deployment.yaml
│ │ └── convert_inventory.go
│ ├── generate
│ │ ├── testdata
│ │ │ └── configuration.yaml
│ │ └── types.go
│ ├── doc.go
│ ├── root_test.go
│ ├── hydrate
│ │ ├── kustomizationFile.go
│ │ ├── hydrate_test.go
│ │ └── hydrate.go
│ └── root.go
├── extensions
│ ├── testdata
│ │ ├── custom-pollers
│ │ │ ├── secstore-no-status.yaml
│ │ │ ├── extsec-no-status.yaml
│ │ │ ├── secstore-ready-true.yaml
│ │ │ ├── secstore-ready-false.yaml
│ │ │ ├── extsec-ready-false.yaml
│ │ │ ├── extsec-ready-true.yaml
│ │ │ └── extsec-terminating.yaml
│ │ ├── filter
│ │ │ ├── secret.yaml
│ │ │ ├── configmap.yaml
│ │ │ ├── filtered.yaml
│ │ │ ├── unfiltered.yaml
│ │ │ └── deployment.yaml
│ │ ├── externalsecret-mutator
│ │ │ ├── configmap.yaml
│ │ │ ├── wrong-resource.yaml
│ │ │ ├── store.yaml
│ │ │ ├── external-secret-secret-name.yaml
│ │ │ ├── pod.yaml
│ │ │ ├── daemonset.yaml
│ │ │ ├── sts.yaml
│ │ │ ├── expected-sts.yaml
│ │ │ ├── deployment.yaml
│ │ │ ├── expected-daemonset.yaml
│ │ │ ├── expected-pod.yaml
│ │ │ ├── expected-deployment.yaml
│ │ │ ├── external-secret.yaml
│ │ │ └── expected-external-secret.yaml
│ │ ├── dependency-mutator
│ │ │ ├── cm-other-namespace.yaml
│ │ │ ├── configmap.yaml
│ │ │ ├── secret.yaml
│ │ │ ├── wrong-resource.yaml
│ │ │ ├── daemonset.yaml
│ │ │ ├── pod.yaml
│ │ │ ├── expected-daemonset.yaml
│ │ │ ├── sts.yaml
│ │ │ ├── expected-sts.yaml
│ │ │ ├── expected-pod.yaml
│ │ │ ├── deployment.yaml
│ │ │ └── expected-deployment.yaml
│ │ └── deploy-mutator
│ │ │ ├── wrong-resource.yaml
│ │ │ ├── error-remote.yaml
│ │ │ ├── pod.yaml
│ │ │ ├── deployment-smart-remote.yaml
│ │ │ ├── expected-pod.yaml
│ │ │ ├── remote-status.yaml
│ │ │ ├── expected-deployment-smart-remote.yaml
│ │ │ ├── sts.yaml
│ │ │ ├── expected-sts.yaml
│ │ │ ├── daemonset.yaml
│ │ │ ├── expected-daemonset.yaml
│ │ │ ├── deployment.yaml
│ │ │ ├── wrong-image.yaml
│ │ │ ├── deployment-smart.yaml
│ │ │ ├── expected-deployment.yaml
│ │ │ └── expected-deployment-smart.yaml
│ ├── deployoncefilter.go
│ ├── deployoncefilter_test.go
│ ├── externalsecretpoller.go
│ ├── externalsecretpoller_test.go
│ └── utils.go
└── apis
│ └── mlp.mia-platform.eu
│ └── v1
│ ├── doc.go
│ ├── types.go
│ └── zz_generated.deepcopy.go
├── .gitleaksignore
├── tests
└── e2e
│ ├── testdata
│ ├── kind.yaml
│ ├── smart-deploy
│ │ ├── stage1
│ │ │ ├── test.secret.yml
│ │ │ ├── test.configmap.yml
│ │ │ ├── test.service.yml
│ │ │ └── test.deployment.yaml
│ │ └── stage2
│ │ │ ├── test.configmap.yml
│ │ │ ├── test.service.yml
│ │ │ └── test.deployment.yaml
│ └── apply-resources
│ │ ├── literal.configmap.yaml
│ │ ├── opaque.secret.yaml
│ │ ├── docker.secret.yaml
│ │ ├── test.cronjob.yaml
│ │ └── test.deployment.yaml
│ └── main_test.go
├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── new-feature.yml
│ ├── documentation.yml
│ └── bug_report.yml
├── PULL_REQUEST_TEMPLATE.md
├── workflows
│ ├── dependency-review.yaml
│ └── codeql.yaml
└── dependabot.yml
├── Dockerfile
├── .editorconfig
├── .markdownlint.yaml
├── examples
└── example-cm-secret-config.yaml
├── docs
├── 40_hydrate.md
├── 50_interpolate.md
├── 10_overview.md
├── 30_generate.md
└── 20_setup.md
├── main.go
├── .pre-commit-config.yaml
├── .devcontainer
└── devcontainer.json
├── .goreleaser.yaml
├── README.md
├── .gitignore
├── CONTRIBUTING.md
├── .golangci.yaml
├── go.mod
├── Makefile
└── CODE_OF_CONDUCT.md
/.go-version:
--------------------------------------------------------------------------------
1 | 1.25.4
2 |
--------------------------------------------------------------------------------
/tools/DEEPCOPY_GEN_VERSION:
--------------------------------------------------------------------------------
1 | v0.34.2
2 |
--------------------------------------------------------------------------------
/tools/GOLANGCI_LINT_VERSION:
--------------------------------------------------------------------------------
1 | v2.6.2
2 |
--------------------------------------------------------------------------------
/tools/GORELEASER_VERSION:
--------------------------------------------------------------------------------
1 | v2.13.0
2 |
--------------------------------------------------------------------------------
/.shellcheckrc:
--------------------------------------------------------------------------------
1 | external-sources=true
2 |
--------------------------------------------------------------------------------
/pkg/cmd/interpolate/testdata/folder/ignored:
--------------------------------------------------------------------------------
1 | {{TEST_ENV}}
2 |
--------------------------------------------------------------------------------
/.gitleaksignore:
--------------------------------------------------------------------------------
1 | pkg/interpolate/interpolate_test.go:private-key:201
2 |
--------------------------------------------------------------------------------
/pkg/cmd/kustomize/testdata/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 | resources: []
4 |
--------------------------------------------------------------------------------
/pkg/cmd/interpolate/testdata/folder/inner/inner.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: inner
5 | data:
6 | key: value
7 |
--------------------------------------------------------------------------------
/pkg/cmd/interpolate/testdata/results-contained/inner.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: inner
5 | data:
6 | key: value
7 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/custom-pollers/secstore-no-status.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: SecretStore
3 | metadata:
4 | name: store
5 |
--------------------------------------------------------------------------------
/tests/e2e/testdata/kind.yaml:
--------------------------------------------------------------------------------
1 | kind: Cluster
2 | apiVersion: kind.x-k8s.io/v1alpha4
3 | nodes:
4 | - role: control-plane
5 | - role: worker
6 | - role: worker
7 |
--------------------------------------------------------------------------------
/tests/e2e/testdata/smart-deploy/stage1/test.secret.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: test
5 | type: Opaque
6 | data:
7 | key: dmFsdWU=
8 |
--------------------------------------------------------------------------------
/pkg/cmd/interpolate/testdata/missing-env.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: example
5 | type: Opaque
6 | data:
7 | key: {{MISSING_ENV}}
8 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/custom-pollers/extsec-no-status.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: ExternalSecret
3 | metadata:
4 | name: external-secret
5 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/resources/configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: example
5 | data:
6 | key: value
7 | otherKey: otherValue
8 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # All files
2 | * @mia-platform/sig-cli
3 | # Actions must also be checked by security
4 | .github/workflows @mia-platform/sig-cli @mia-platform/sig-security
5 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/error-resources/configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: example
5 | data:
6 | key: value
7 | otherKey: otherValue
8 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/filter/secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: example
5 | annotations:
6 | mia-platform.eu/deploy: always
7 | type: Opaque
8 |
--------------------------------------------------------------------------------
/tests/e2e/testdata/apply-resources/literal.configmap.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | data:
4 | key1: value
5 | key2: value2
6 | kind: ConfigMap
7 | metadata:
8 | name: literal
9 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/error-resources/secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: example
5 | annotations:
6 | mia-platform.eu/deploy: once
7 | type: Opaque
8 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/externalsecret-mutator/configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: example
5 | namespace: externalsecret-test
6 | data:
7 | key: value
8 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/filter/configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: example
5 | labels:
6 | mia-platform.eu/deploy: once
7 | data:
8 | key: value
9 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/dependency-mutator/cm-other-namespace.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: example
5 | namespace: other-ns
6 | data:
7 | config: othervalue
8 |
--------------------------------------------------------------------------------
/tests/e2e/testdata/smart-deploy/stage1/test.configmap.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | data:
4 | request-size.conf: client_max_body_size 100m;
5 | metadata:
6 | name: api-gateway-server
7 |
--------------------------------------------------------------------------------
/tests/e2e/testdata/smart-deploy/stage2/test.configmap.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | data:
4 | request-size.conf: client_max_body_size 500m;
5 | metadata:
6 | name: api-gateway-server
7 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/filter/filtered.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: filtered
5 | namespace: test
6 | annotations:
7 | mia-platform.eu/deploy: once
8 | type: Opaque
9 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/filter/unfiltered.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: unfiltered
5 | namespace: test
6 | annotations:
7 | mia-platform.eu/deploy: once
8 | type: Opaque
9 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/resources/secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: example
5 | namespace: mlp-deploy-test
6 | annotations:
7 | mia-platform.eu/deploy: once
8 | type: Opaque
9 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/dependency-mutator/configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: example
5 | namespace: test
6 | data:
7 | config: value
8 | binaryData:
9 | bconfig: //0=
10 |
--------------------------------------------------------------------------------
/pkg/cmd/generate/testdata/configuration.yaml:
--------------------------------------------------------------------------------
1 | config-maps:
2 | - name: "literal"
3 | data:
4 | - from: "literal"
5 | key: key
6 | value: value
7 | - from: "literal"
8 | key: otherKey
9 | value: value
10 |
--------------------------------------------------------------------------------
/tests/e2e/testdata/apply-resources/opaque.secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | type: Opaque
4 | metadata:
5 | annotations:
6 | mia-platform.eu/deploy: always
7 | name: opaque
8 | data:
9 | key: dmFsdWU=
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: true
2 | contact_links:
3 | - name: Mia-Platform Community Support
4 | url: https://github.com/mia-platform/community/discussions
5 | about: Please ask and answer questions here.
6 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/expectations/configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: example
5 | namespace: mlp-deploy-test
6 | annotations: {}
7 | data:
8 | key: value
9 | otherKey: otherValue
10 |
--------------------------------------------------------------------------------
/pkg/cmd/interpolate/testdata/folder/no-interpolation.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: example
5 | spec:
6 | selector:
7 | app: example
8 | ports:
9 | - port: 80
10 | targetPort: 8080
11 |
--------------------------------------------------------------------------------
/pkg/cmd/interpolate/testdata/results/no-interpolation.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: example
5 | spec:
6 | selector:
7 | app: example
8 | ports:
9 | - port: 80
10 | targetPort: 8080
11 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/wrong-resource.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: example
5 | spec:
6 | selector:
7 | app: example
8 | ports:
9 | - port: 80
10 | targetPort: 8080
11 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/dependency-mutator/secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: example
5 | namespace: test
6 | type: Opaque
7 | data:
8 | data: dmFsdWU=
9 | stringData:
10 | otherData: value
11 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/dependency-mutator/wrong-resource.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: example
5 | spec:
6 | selector:
7 | app: example
8 | ports:
9 | - port: 80
10 | targetPort: 8080
11 |
--------------------------------------------------------------------------------
/pkg/cmd/interpolate/testdata/results-contained/no-interpolation.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: example
5 | spec:
6 | selector:
7 | app: example
8 | ports:
9 | - port: 80
10 | targetPort: 8080
11 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/externalsecret-mutator/wrong-resource.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: example
5 | spec:
6 | selector:
7 | app: example
8 | ports:
9 | - port: 80
10 | targetPort: 8080
11 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/custom-pollers/secstore-ready-true.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: SecretStore
3 | metadata:
4 | name: store
5 | status:
6 | conditions:
7 | - type: Ready
8 | status: "True"
9 | message: "custom message"
10 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/custom-pollers/secstore-ready-false.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: SecretStore
3 | metadata:
4 | name: store
5 | status:
6 | conditions:
7 | - type: Ready
8 | status: "False"
9 | message: "custom message"
10 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/custom-pollers/extsec-ready-false.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: ExternalSecret
3 | metadata:
4 | name: external-secret
5 | status:
6 | conditions:
7 | - type: Ready
8 | status: "False"
9 | message: "custom message"
10 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/custom-pollers/extsec-ready-true.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: ExternalSecret
3 | metadata:
4 | name: external-secret
5 | status:
6 | conditions:
7 | - type: Ready
8 | status: "True"
9 | message: "custom message"
10 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/custom-pollers/extsec-terminating.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: ExternalSecret
3 | metadata:
4 | name: external-secret
5 | status:
6 | conditions:
7 | - type: Deleted
8 | status: "True"
9 | message: "custom message"
10 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/error-remote.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: error
5 | labels:
6 | name: error
7 | spec:
8 | containers:
9 | - name: error
10 | image: busybox:v1.0.0
11 | resources:
12 | limits:
13 | memory: "128Mi"
14 | cpu: "500m"
15 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1
2 | FROM docker.io/library/alpine:3.23.2@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62
3 |
4 | ARG TARGETPLATFORM
5 | ARG CMD_NAME
6 | ENV COMMAND_NAME=${CMD_NAME}
7 |
8 | COPY ${TARGETPLATFORM}/${CMD_NAME} /usr/local/bin/
9 |
10 | CMD ["/bin/sh", "-c", "${COMMAND_NAME}"]
11 |
--------------------------------------------------------------------------------
/tools/parse-tags.sh:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env bash
2 |
3 | set -o pipefail
4 | set -o errexit
5 | set -o nounset
6 |
7 | TAG_TO_PARSE="${1}"
8 |
9 | if [[ ${TAG_TO_PARSE} =~ ^v[0-9]+\.[0-9]\.[0-9]+$ ]]; then
10 | awk -F'.' '{ print $1, $1 "." $2, $1 "." $2 "." $3 }' <<< "${TAG_TO_PARSE//v/}"
11 | exit 0
12 | fi
13 |
14 | echo "${TAG_TO_PARSE}"
15 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = tab
5 | indent_size = tab
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 | max_line_length = 120
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
15 | [*.{yaml,yml}]
16 | indent_style = space
17 | tab_width = 2
18 |
--------------------------------------------------------------------------------
/pkg/cmd/interpolate/testdata/folder/other-file.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: example
5 | data:
6 | key: "{{JSON_SINGLELINE_ENV}}"
7 | key2: {{MULTILINE_STRING}}
8 | key3: {{STRING_ESCAPED_ENV}}
9 | key4: "{{DOLLAR_ENV}}otherstring"
10 | key5: "otherstring{{MLP_SIMPLE_ENV}}"
11 | key6: {{DOLLAR_ENV}}
12 |
--------------------------------------------------------------------------------
/.markdownlint.yaml:
--------------------------------------------------------------------------------
1 | default: true
2 | MD003:
3 | style: atx
4 | MD010: false
5 | MD013:
6 | line_length: 120
7 | heading_line_length: 80
8 | code_blocks: false
9 | tables: false
10 | headings: true
11 | strict: false
12 | stern: false
13 | MD024:
14 | siblings_only: true
15 | MD029:
16 | style: one
17 | MD033:
18 | allowed_elements:
19 | - center
20 | - picture
21 | - source
22 | - img
23 | MD046:
24 | style: fenced
25 |
--------------------------------------------------------------------------------
/tests/e2e/testdata/apply-resources/docker.secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | data:
4 | .dockerconfigjson: eyJhdXRocyI6eyJleGFtcGxlLmNvbSI6eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwicGFzc3dvcmQiOiJwYXNzd29yZCIsImVtYWlsIjoiZW1haWxAZXhhbXBsZS5jb20iLCJhdXRoIjoiZFhObGNtNWhiV1U2Y0dGemMzZHZjbVE9In19fQ==
5 | metadata:
6 | annotations:
7 | mia-platform.eu/deploy: once
8 | creationTimestamp: null
9 | name: docker
10 | type: kubernetes.io/dockerconfigjson
11 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/resources/external-secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: ExternalSecret
3 | metadata:
4 | name: external-secret
5 | namespace: mlp-deploy-test
6 | spec:
7 | refreshInterval: 1h
8 | secretStoreRef:
9 | name: secret-store
10 | target:
11 | creationPolicy: Owner
12 | data:
13 | - secretKey: secret-key
14 | remoteRef:
15 | key: provider-key
16 | version: provider-key-version
17 | property: provider-key-property
18 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/filter/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: example
5 | annotations:
6 | mia-platform.eu/deploy: once
7 | spec:
8 | selector:
9 | matchLabels:
10 | app: example
11 | template:
12 | metadata:
13 | labels:
14 | app: example
15 | spec:
16 | containers:
17 | - name: example
18 | image: busybox
19 | resources:
20 | limits:
21 | memory: "128Mi"
22 | cpu: "500m"
23 |
--------------------------------------------------------------------------------
/tests/e2e/testdata/apply-resources/test.cronjob.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: CronJob
3 | metadata:
4 | name: test
5 | annotations:
6 | mia-platform.eu/autocreate: 'true'
7 | spec:
8 | schedule: "*/5 * * * *"
9 | jobTemplate:
10 | spec:
11 | template:
12 | spec:
13 | containers:
14 | - name: hello
15 | image: busybox
16 | args:
17 | - /bin/sh
18 | - -c
19 | - date; sleep 5
20 | restartPolicy: OnFailure
21 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/resources/store.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: SecretStore
3 | metadata:
4 | name: secret-store
5 | namespace: mlp-deploy-test
6 | spec:
7 | provider:
8 | aws:
9 | service: SecretsManager
10 | region: us-east-1
11 | auth:
12 | secretRef:
13 | accessKeyIDSecretRef:
14 | name: awssm-secret
15 | key: access-key
16 | secretAccessKeySecretRef:
17 | name: awssm-secret
18 | key: secret-access-key
19 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/externalsecret-mutator/store.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: SecretStore
3 | metadata:
4 | name: secret-store
5 | namespace: externalsecret-test
6 | spec:
7 | provider:
8 | aws:
9 | service: SecretsManager
10 | region: us-east-1
11 | auth:
12 | secretRef:
13 | accessKeyIDSecretRef:
14 | name: awssm-secret
15 | key: access-key
16 | secretAccessKeySecretRef:
17 | name: awssm-secret
18 | key: secret-access-key
19 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/expectations/store.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: SecretStore
3 | metadata:
4 | name: secret-store
5 | namespace: mlp-deploy-test
6 | annotations: {}
7 | spec:
8 | provider:
9 | aws:
10 | service: SecretsManager
11 | region: us-east-1
12 | auth:
13 | secretRef:
14 | accessKeyIDSecretRef:
15 | name: awssm-secret
16 | key: access-key
17 | secretAccessKeySecretRef:
18 | name: awssm-secret
19 | key: secret-access-key
20 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/externalsecret-mutator/external-secret-secret-name.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: ExternalSecret
3 | metadata:
4 | name: external-secret2
5 | namespace: externalsecret-test
6 | spec:
7 | refreshInterval: 1h
8 | secretStoreRef:
9 | name: secret-store
10 | kind: SecretStore
11 | target:
12 | name: custom-secret-name
13 | creationPolicy: Owner
14 | data:
15 | - secretKey: secret-key
16 | remoteRef:
17 | key: provider-key
18 | version: provider-key-version
19 | property: provider-key-property
20 |
--------------------------------------------------------------------------------
/tests/e2e/testdata/apply-resources/test.deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: test
5 | labels:
6 | app: test
7 | annotations:
8 | key: value
9 | spec:
10 | replicas: 2
11 | selector:
12 | matchLabels:
13 | app: test
14 | strategy: {}
15 | template:
16 | metadata:
17 | labels:
18 | app: test
19 | spec:
20 | containers:
21 | - image: nginx
22 | name: nginx
23 | resources: {}
24 | volumes:
25 | - name: configmap
26 | configMap:
27 | name: literal
28 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/error-resources/cronjob.yml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: CronJob
3 | metadata:
4 | name: example
5 | spec:
6 | schedule: "*/5 * * * *"
7 | jobTemplate:
8 | spec:
9 | template:
10 | spec:
11 | containers:
12 | - name: example
13 | image: busybox
14 | args:
15 | - /bin/sh
16 | - -c
17 | - date; sleep 120
18 | env:
19 | - name: ENV
20 | valueFrom:
21 | configMapKeyRef:
22 | key: key
23 | name: example
24 | restartPolicy: OnFailure
25 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/dependency-mutator/daemonset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: DaemonSet
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | name: example
10 | template:
11 | metadata:
12 | annotations:
13 | existing: annotation
14 | labels:
15 | name: example
16 | spec:
17 | containers:
18 | - name: example
19 | image: busybox
20 | volumes:
21 | - name: varlog
22 | hostPath:
23 | path: /var/log
24 | - name: example
25 | secret:
26 | secretName: example
27 | optional: true
28 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/error-resources/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: example
5 | spec:
6 | selector:
7 | matchLabels:
8 | app: example
9 | template:
10 | metadata:
11 | annotations:
12 | mia-platform.eu/dependencies-checksum: predefined-value
13 | labels:
14 | app: example
15 | spec:
16 | containers:
17 | - name: example
18 | image: nginx:latest
19 | resources:
20 | limits:
21 | memory: "128Mi"
22 | cpu: "500m"
23 | volumes:
24 | - name: example
25 | configMap:
26 | name: example
27 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/dependency-mutator/pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: example
5 | namespace: test
6 | labels:
7 | name: example
8 | spec:
9 | initContainers:
10 | - name: init
11 | image: busybox
12 | env:
13 | - name: ENV
14 | valueFrom:
15 | secretKeyRef:
16 | key: otherData
17 | name: example
18 | - name: ENV2
19 | valueFrom:
20 | configMapKeyRef:
21 | key: example
22 | name: missing
23 | containers:
24 | - name: example
25 | image: busybox
26 | resources:
27 | limits:
28 | memory: "128Mi"
29 | cpu: "500m"
30 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: example
5 | namespace: test
6 | labels:
7 | name: example
8 | spec:
9 | initContainers:
10 | - name: init
11 | image: busybox:v1.0.0
12 | env:
13 | - name: ENV
14 | valueFrom:
15 | secretKeyRef:
16 | key: otherkey
17 | name: example
18 | - name: ENV2
19 | valueFrom:
20 | configMapKeyRef:
21 | key: example
22 | name: missing
23 | containers:
24 | - name: example
25 | image: busybox
26 | resources:
27 | limits:
28 | memory: "128Mi"
29 | cpu: "500m"
30 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/deployment-smart-remote.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | template:
11 | metadata:
12 | labels:
13 | app: example
14 | spec:
15 | containers:
16 | - name: example
17 | image: busybox:1.35.0@sha256:5be7104a4306abe768359a5379e6050ef69a29e9a5f99fcf7f46d5f7e9ba29a2
18 | resources:
19 | limits:
20 | memory: "128Mi"
21 | cpu: "500m"
22 | volumes:
23 | - name: volume
24 | configMap:
25 | name: example
26 |
--------------------------------------------------------------------------------
/tools/boilerplate.go.txt:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/resources/cronjob.yml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: CronJob
3 | metadata:
4 | name: example
5 | annotations:
6 | mia-platform.eu/autocreate: 'true'
7 | spec:
8 | schedule: "*/5 * * * *"
9 | jobTemplate:
10 | spec:
11 | template:
12 | spec:
13 | containers:
14 | - name: example
15 | image: busybox
16 | args:
17 | - /bin/sh
18 | - -c
19 | - date; sleep 120
20 | env:
21 | - name: ENV
22 | valueFrom:
23 | configMapKeyRef:
24 | key: key
25 | name: example
26 | restartPolicy: OnFailure
27 |
--------------------------------------------------------------------------------
/tests/e2e/testdata/smart-deploy/stage1/test.service.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | annotations:
5 | mia-platform.eu/version: 8.4.0
6 | labels:
7 | app: test
8 | app.kubernetes.io/component: custom
9 | app.kubernetes.io/managed-by: mia-platform
10 | app.kubernetes.io/name: test
11 | app.kubernetes.io/part-of: test-mlp-kustomize-2
12 | app.kubernetes.io/version: latest
13 | mia-platform.eu/stage: 'DEV'
14 | mia-platform.eu/tenant: kustomize-tenant
15 | name: test
16 | spec:
17 | ports:
18 | - name: http
19 | nodePort: null
20 | port: 80
21 | protocol: TCP
22 | targetPort: 3000
23 | selector:
24 | app: test
25 | type: ClusterIP
26 |
--------------------------------------------------------------------------------
/tests/e2e/testdata/smart-deploy/stage2/test.service.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | annotations:
5 | mia-platform.eu/version: 8.4.0
6 | labels:
7 | app: test
8 | app.kubernetes.io/component: custom
9 | app.kubernetes.io/managed-by: mia-platform
10 | app.kubernetes.io/name: test
11 | app.kubernetes.io/part-of: test-mlp-kustomize-2
12 | app.kubernetes.io/version: latest
13 | mia-platform.eu/stage: 'DEV'
14 | mia-platform.eu/tenant: kustomize-tenant
15 | name: test
16 | spec:
17 | ports:
18 | - name: http
19 | nodePort: null
20 | port: 80
21 | protocol: TCP
22 | targetPort: 3000
23 | selector:
24 | app: test
25 | type: ClusterIP
26 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/expectations/external-secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: ExternalSecret
3 | metadata:
4 | name: external-secret
5 | namespace: mlp-deploy-test
6 | annotations:
7 | config.kubernetes.io/depends-on: external-secrets.io/namespaces/mlp-deploy-test/SecretStore/secret-store
8 | mia-platform.eu/deploy-checksum: a2d1ace0489d09c0ca26a1ab8a8bc9b11e4365cb4f904c434565a59119f3eb15
9 | spec:
10 | refreshInterval: 1h
11 | secretStoreRef:
12 | name: secret-store
13 | target:
14 | creationPolicy: Owner
15 | data:
16 | - secretKey: secret-key
17 | remoteRef:
18 | key: provider-key
19 | version: provider-key-version
20 | property: provider-key-property
21 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/externalsecret-mutator/pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: example
5 | namespace: externalsecret-test
6 | labels:
7 | name: example
8 | spec:
9 | initContainers:
10 | - name: init
11 | image: busybox
12 | env:
13 | - name: ENV
14 | valueFrom:
15 | secretKeyRef:
16 | key: otherkey
17 | name: external-secret
18 | containers:
19 | - name: example
20 | image: busybox
21 | resources:
22 | limits:
23 | memory: "128Mi"
24 | cpu: "500m"
25 | volumes:
26 | - name: volume
27 | secret:
28 | secretName: external-secret
29 | - name: volume
30 | secret:
31 | secretName: custom-secret-name
32 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/expectations/cronjob.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: CronJob
3 | metadata:
4 | name: example
5 | namespace: mlp-deploy-test
6 | annotations:
7 | mia-platform.eu/autocreate: 'true'
8 | spec:
9 | jobTemplate:
10 | spec:
11 | template:
12 | spec:
13 | containers:
14 | - args:
15 | - /bin/sh
16 | - '-c'
17 | - date; sleep 120
18 | env:
19 | - name: ENV
20 | valueFrom:
21 | configMapKeyRef:
22 | key: key
23 | name: example
24 | image: busybox
25 | name: example
26 | restartPolicy: OnFailure
27 | schedule: '*/5 * * * *'
28 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/expectations/job.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: Job
3 | metadata:
4 | name: example
5 | namespace: mlp-deploy-test
6 | annotations:
7 | cronjob.kubernetes.io/instantiate: manual
8 | creationTimestamp: null
9 | spec:
10 | template:
11 | metadata:
12 | creationTimestamp: null
13 | spec:
14 | containers:
15 | - args:
16 | - /bin/sh
17 | - '-c'
18 | - date; sleep 120
19 | env:
20 | - name: ENV
21 | valueFrom:
22 | configMapKeyRef:
23 | key: key
24 | name: example
25 | image: busybox
26 | name: example
27 | resources: {}
28 | restartPolicy: OnFailure
29 | status: {}
30 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/expected-pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: example
5 | namespace: test
6 | labels:
7 | name: example
8 | annotations:
9 | mia-platform.eu/deploy-checksum: test-identifier
10 | spec:
11 | initContainers:
12 | - name: init
13 | image: busybox:v1.0.0
14 | env:
15 | - name: ENV
16 | valueFrom:
17 | secretKeyRef:
18 | key: otherkey
19 | name: example
20 | - name: ENV2
21 | valueFrom:
22 | configMapKeyRef:
23 | key: example
24 | name: missing
25 | containers:
26 | - name: example
27 | image: busybox
28 | resources:
29 | limits:
30 | memory: "128Mi"
31 | cpu: "500m"
32 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/remote-status.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | template:
11 | metadata:
12 | annotations:
13 | mia-platform.eu/deploy-checksum: remote-identifier
14 | labels:
15 | app: example
16 | spec:
17 | containers:
18 | - name: example
19 | image: busybox:1.35.0@sha256:5be7104a4306abe768359a5379e6050ef69a29e9a5f99fcf7f46d5f7e9ba29a2
20 | resources:
21 | limits:
22 | memory: "128Mi"
23 | cpu: "500m"
24 | volumes:
25 | - name: volume
26 | configMap:
27 | name: example
28 |
--------------------------------------------------------------------------------
/examples/example-cm-secret-config.yaml:
--------------------------------------------------------------------------------
1 | config-map:
2 | - name: "pippo"
3 | data:
4 | - from: "literal|file"
5 | file: ./path
6 | key: key
7 | value: value
8 | - from: literal
9 | key: key1
10 | value: value1
11 | - from: literal
12 | key: key2
13 | value: {{PIPPO}}
14 |
15 | secrets:
16 | - name: "pippo"
17 | when: "always|once"
18 | tls:
19 | cert: path
20 | key: path
21 | docker:
22 | username: {{DOCKER_USERNAME}}
23 | password: {{DOCKER_USERNAME}}
24 | email: {{DOCKER_USERNAME}}
25 | server: {{DOCKER_USERNAME}}
26 | data:
27 | - from: "literal|file"
28 | file: ./path
29 | key: key
30 | value: {{MLP}}
31 |
--------------------------------------------------------------------------------
/pkg/cmd/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | // Package cmd provides all the functions describing mlp's commands
17 | package cmd
18 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/externalsecret-mutator/daemonset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: DaemonSet
3 | metadata:
4 | name: example
5 | namespace: externalsecret-test
6 | spec:
7 | selector:
8 | matchLabels:
9 | name: example
10 | template:
11 | metadata:
12 | annotations:
13 | existing: annotation
14 | labels:
15 | name: example
16 | spec:
17 | initContainers:
18 | - name: example
19 | image: busybox
20 | containers:
21 | - name: example
22 | image: busybox:v1.0.0
23 | volumes:
24 | - name: varlog
25 | hostPath:
26 | path: /var/log
27 | - name: example
28 | secret:
29 | secretName: custom-secret-name
30 | optional: true
31 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/expected-deployment-smart-remote.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | template:
11 | metadata:
12 | annotations:
13 | mia-platform.eu/deploy-checksum: remote-identifier
14 | labels:
15 | app: example
16 | spec:
17 | containers:
18 | - name: example
19 | image: busybox:1.35.0@sha256:5be7104a4306abe768359a5379e6050ef69a29e9a5f99fcf7f46d5f7e9ba29a2
20 | resources:
21 | limits:
22 | memory: "128Mi"
23 | cpu: "500m"
24 | volumes:
25 | - name: volume
26 | configMap:
27 | name: example
28 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/dependency-mutator/expected-daemonset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: DaemonSet
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | name: example
10 | template:
11 | metadata:
12 | annotations:
13 | existing: annotation
14 | mia-platform.eu/dependencies-checksum: 0a7f9b4a49ce906ecfaba03aa45ef6512ecb33639d434107b43473b16d0c1afb
15 | labels:
16 | name: example
17 | spec:
18 | containers:
19 | - name: example
20 | image: busybox
21 | volumes:
22 | - name: varlog
23 | hostPath:
24 | path: /var/log
25 | - name: example
26 | secret:
27 | secretName: example
28 | optional: true
29 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/sts.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: StatefulSet
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | serviceName: example
11 | replicas: 2
12 | template:
13 | metadata:
14 | annotations:
15 | existing: annotation
16 | labels:
17 | app: example
18 | spec:
19 | containers:
20 | - name: example
21 | image: busybox
22 | volumes:
23 | - name: volume
24 | secret:
25 | secretName: missing
26 | volumeClaimTemplates:
27 | - metadata:
28 | name: www
29 | spec:
30 | accessModes:
31 | - ReadWriteOnce
32 | resources:
33 | requests:
34 | storage: 1Gi
35 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/dependency-mutator/sts.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: StatefulSet
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | serviceName: example
11 | replicas: 2
12 | template:
13 | metadata:
14 | annotations:
15 | existing: annotation
16 | labels:
17 | app: example
18 | spec:
19 | containers:
20 | - name: example
21 | image: busybox
22 | volumes:
23 | - name: volume
24 | secret:
25 | secretName: missing
26 | volumeClaimTemplates:
27 | - metadata:
28 | name: www
29 | spec:
30 | accessModes:
31 | - ReadWriteOnce
32 | resources:
33 | requests:
34 | storage: 1Gi
35 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/expected-sts.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: StatefulSet
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | serviceName: example
11 | replicas: 2
12 | template:
13 | metadata:
14 | annotations:
15 | existing: annotation
16 | labels:
17 | app: example
18 | spec:
19 | containers:
20 | - name: example
21 | image: busybox
22 | volumes:
23 | - name: volume
24 | secret:
25 | secretName: missing
26 | volumeClaimTemplates:
27 | - metadata:
28 | name: www
29 | spec:
30 | accessModes:
31 | - ReadWriteOnce
32 | resources:
33 | requests:
34 | storage: 1Gi
35 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/dependency-mutator/expected-sts.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: StatefulSet
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | serviceName: example
11 | replicas: 2
12 | template:
13 | metadata:
14 | annotations:
15 | existing: annotation
16 | labels:
17 | app: example
18 | spec:
19 | containers:
20 | - name: example
21 | image: busybox
22 | volumes:
23 | - name: volume
24 | secret:
25 | secretName: missing
26 | volumeClaimTemplates:
27 | - metadata:
28 | name: www
29 | spec:
30 | accessModes:
31 | - ReadWriteOnce
32 | resources:
33 | requests:
34 | storage: 1Gi
35 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/dependency-mutator/expected-pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: example
5 | namespace: test
6 | labels:
7 | name: example
8 | annotations:
9 | mia-platform.eu/dependencies-checksum: ec4cee1e7cd6503727710ff3f36d85576177196688ea2871efae51f9de3c65ea
10 | spec:
11 | initContainers:
12 | - name: init
13 | image: busybox
14 | env:
15 | - name: ENV
16 | valueFrom:
17 | secretKeyRef:
18 | key: otherData
19 | name: example
20 | - name: ENV2
21 | valueFrom:
22 | configMapKeyRef:
23 | key: example
24 | name: missing
25 | containers:
26 | - name: example
27 | image: busybox
28 | resources:
29 | limits:
30 | memory: "128Mi"
31 | cpu: "500m"
32 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/daemonset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: DaemonSet
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | name: example
10 | template:
11 | metadata:
12 | annotations:
13 | existing: annotation
14 | labels:
15 | name: example
16 | spec:
17 | initContainers:
18 | - name: example
19 | image: busybox@sha256:9ae97d36d26566ff84e8893c64a6dc4fe8ca6d1144bf5b87b2b85a32def253c7
20 | containers:
21 | - name: example
22 | image: busybox:v1.0.0
23 | volumes:
24 | - name: varlog
25 | hostPath:
26 | path: /var/log
27 | - name: example
28 | secret:
29 | secretName: example
30 | optional: true
31 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/externalsecret-mutator/sts.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: StatefulSet
3 | metadata:
4 | name: example
5 | namespace: externalsecret-test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | serviceName: example
11 | replicas: 2
12 | template:
13 | metadata:
14 | annotations:
15 | existing: annotation
16 | labels:
17 | app: example
18 | spec:
19 | containers:
20 | - name: example
21 | image: busybox
22 | volumes:
23 | - name: volume
24 | secret:
25 | secretName: missing
26 | volumeClaimTemplates:
27 | - metadata:
28 | name: www
29 | spec:
30 | accessModes:
31 | - ReadWriteOnce
32 | resources:
33 | requests:
34 | storage: 1Gi
35 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/expected-daemonset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: DaemonSet
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | name: example
10 | template:
11 | metadata:
12 | annotations:
13 | existing: annotation
14 | labels:
15 | name: example
16 | spec:
17 | initContainers:
18 | - name: example
19 | image: busybox@sha256:9ae97d36d26566ff84e8893c64a6dc4fe8ca6d1144bf5b87b2b85a32def253c7
20 | containers:
21 | - name: example
22 | image: busybox:v1.0.0
23 | volumes:
24 | - name: varlog
25 | hostPath:
26 | path: /var/log
27 | - name: example
28 | secret:
29 | secretName: example
30 | optional: true
31 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/externalsecret-mutator/expected-sts.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: StatefulSet
3 | metadata:
4 | name: example
5 | namespace: externalsecret-test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | serviceName: example
11 | replicas: 2
12 | template:
13 | metadata:
14 | annotations:
15 | existing: annotation
16 | labels:
17 | app: example
18 | spec:
19 | containers:
20 | - name: example
21 | image: busybox
22 | volumes:
23 | - name: volume
24 | secret:
25 | secretName: missing
26 | volumeClaimTemplates:
27 | - metadata:
28 | name: www
29 | spec:
30 | accessModes:
31 | - ReadWriteOnce
32 | resources:
33 | requests:
34 | storage: 1Gi
35 |
--------------------------------------------------------------------------------
/tests/e2e/testdata/smart-deploy/stage2/test.deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | creationTimestamp: null
5 | labels:
6 | app: test
7 | name: test
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: test
13 | strategy: {}
14 | template:
15 | metadata:
16 | creationTimestamp: null
17 | labels:
18 | app: test
19 | spec:
20 | containers:
21 | - image: nginx
22 | name: nginx
23 | resources: {}
24 | volumeMounts:
25 | - mountPath: /etc/nginx/conf.d
26 | name: api-gateway-server
27 | readOnly: true
28 | volumes:
29 | - configMap:
30 | defaultMode: 420
31 | name: api-gateway-server
32 | name: api-gateway-server
33 | status: {}
34 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
7 |
8 | ## What this PR is for?
9 |
10 |
13 |
14 | **Which issue(s) this PR fixes:**
15 |
19 | - Fixes #
20 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/resources/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: example
5 | spec:
6 | selector:
7 | matchLabels:
8 | app: example
9 | template:
10 | metadata:
11 | annotations:
12 | mia-platform.eu/dependencies-checksum: predefined-value
13 | labels:
14 | app: example
15 | spec:
16 | containers:
17 | - name: example
18 | image: nginx:latest
19 | resources:
20 | limits:
21 | memory: "128Mi"
22 | cpu: "500m"
23 | env:
24 | - name: ENV
25 | valueFrom:
26 | secretKeyRef:
27 | key: secret-key
28 | name: external-secret
29 | volumes:
30 | - name: example
31 | configMap:
32 | name: example
33 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/dependency-mutator/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | template:
11 | metadata:
12 | labels:
13 | app: example
14 | spec:
15 | initContainers:
16 | - name: example
17 | image: busybox
18 | env:
19 | - name: ENV
20 | valueFrom:
21 | secretKeyRef:
22 | key: data
23 | name: example
24 | containers:
25 | - name: example
26 | image: busybox
27 | resources:
28 | limits:
29 | memory: "128Mi"
30 | cpu: "500m"
31 | volumes:
32 | - name: volume
33 | configMap:
34 | name: example
35 |
--------------------------------------------------------------------------------
/pkg/apis/mlp.mia-platform.eu/v1/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | // Package v1 implements the v1 apiVersion of mlp generate configuration
17 | //
18 | // +k8s:deepcopy-gen=package
19 | package v1
20 |
--------------------------------------------------------------------------------
/tools/make/generate.mk:
--------------------------------------------------------------------------------
1 | # Copyright Mia srl
2 | # SPDX-License-Identifier: Apache-2.0
3 |
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 |
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | ##@ Deepcopy Goals
17 |
18 | .PHONY: generate-deps
19 | generate-deps:
20 |
21 | .PHONY: generate
22 | generate: generate-deps
23 | go generate -x -ldflags "$(GO_LDFLAGS)" ./...
24 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | template:
11 | metadata:
12 | labels:
13 | app: example
14 | spec:
15 | initContainers:
16 | - name: example
17 | image: busybox:v1.0.0
18 | env:
19 | - name: ENV
20 | valueFrom:
21 | secretKeyRef:
22 | key: key
23 | name: example
24 | key: key
25 | containers:
26 | - name: example
27 | image: busybox
28 | resources:
29 | limits:
30 | memory: "128Mi"
31 | cpu: "500m"
32 | volumes:
33 | - name: volume
34 | configMap:
35 | name: example
36 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/externalsecret-mutator/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: example
5 | namespace: externalsecret-test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | template:
11 | metadata:
12 | labels:
13 | app: example
14 | spec:
15 | initContainers:
16 | - name: example
17 | image: busybox
18 | env:
19 | - name: ENV
20 | valueFrom:
21 | secretKeyRef:
22 | key: data
23 | name: external-secret
24 | containers:
25 | - name: example
26 | image: busybox
27 | resources:
28 | limits:
29 | memory: "128Mi"
30 | cpu: "500m"
31 | volumes:
32 | - name: volume
33 | configMap:
34 | name: example
35 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/wrong-image.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: StatefulSet
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | serviceName: example
11 | replicas: 2
12 | template:
13 | metadata:
14 | annotations:
15 | existing: annotation
16 | labels:
17 | app: example
18 | spec:
19 | containers:
20 | - name: example
21 | image: busybox:sha256:5be7104a4306abe768359a5379e6050ef69a29e9a5f99fcf7f46d5f7e9ba29a2
22 | volumes:
23 | - name: volume
24 | secret:
25 | secretName: missing
26 | volumeClaimTemplates:
27 | - metadata:
28 | name: www
29 | spec:
30 | accessModes:
31 | - ReadWriteOnce
32 | resources:
33 | requests:
34 | storage: 1Gi
35 |
--------------------------------------------------------------------------------
/.github/workflows/dependency-review.yaml:
--------------------------------------------------------------------------------
1 | name: Dependency Review
2 | on:
3 | pull_request:
4 | branches:
5 | - main
6 | paths-ignore:
7 | - "**/*.md"
8 | - docs/**
9 | - examples/**
10 |
11 | permissions: {}
12 |
13 | jobs:
14 | dependency-review:
15 | name: Dependencies Review
16 | runs-on: ubuntu-latest
17 | permissions:
18 | contents: read
19 | pull-requests: write
20 | steps:
21 | - name: Checkout Repository
22 | uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
23 | with:
24 | show-progress: false
25 | - name: Dependency Review
26 | uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2
27 | with:
28 | fail-on-severity: high
29 | fail-on-scopes: development,runtime,unknown
30 | comment-summary-in-pr: on-failure
31 |
--------------------------------------------------------------------------------
/pkg/cmd/interpolate/testdata/results/other-file.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: example
5 | data:
6 | key: "{\"type\":\"type\",\"project_id\":\"id\",\"private_key_id\":\"key\",\"private_key\":\"-----BEGIN CERTIFICATE-----\nXXXXXXXXXXXXXXXXXXXXXXXXX\nYYYYYYYYYYYYYYY/4YYYYYYYYY\n-----END CERTIFICATE-----\n\",\"client_email\":\"email@example.com\",\"client_id\":\"client-id\",\"auth_uri\":\"https://example.com/auth\",\"token_uri\":\"https://example.com/token\",\"auth_provider_x509_cert_url\":\"https://example.com/certs\",\"client_x509_cert_url\":\"https://example.com/certs/fooo%40bar\"}"
7 | key2: -----BEGIN CERTIFICATE-----\nXXXXXXXXXXXXXXXXXXXXXXXXX\nYYYYYYYYYYYYYYY/4YYYYYYYYY\nZZZZZZZZZZZZZZZZZZZZZZZZZZ\n-----END CERTIFICATE-----
8 | key3: env\\first\line
9 | key4: "$contains$dollars$otherstring"
10 | key5: "otherstringtest"
11 | key6: $contains$dollars$
12 |
--------------------------------------------------------------------------------
/tests/e2e/testdata/smart-deploy/stage1/test.deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | creationTimestamp: null
5 | labels:
6 | app: test
7 | name: test
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: test
13 | strategy: {}
14 | template:
15 | metadata:
16 | creationTimestamp: null
17 | labels:
18 | app: test
19 | annotations:
20 | mia-platform.eu/deploy-checksum: ""
21 | spec:
22 | containers:
23 | - image: nginx
24 | name: nginx
25 | resources: {}
26 | volumeMounts:
27 | - mountPath: /etc/nginx/conf.d
28 | name: api-gateway-server
29 | readOnly: true
30 | volumes:
31 | - configMap:
32 | defaultMode: 420
33 | name: api-gateway-server
34 | name: api-gateway-server
35 | status: {}
36 |
--------------------------------------------------------------------------------
/pkg/cmd/interpolate/testdata/results-contained/other-file.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: example
5 | data:
6 | key: "{\"type\":\"type\",\"project_id\":\"id\",\"private_key_id\":\"key\",\"private_key\":\"-----BEGIN CERTIFICATE-----\nXXXXXXXXXXXXXXXXXXXXXXXXX\nYYYYYYYYYYYYYYY/4YYYYYYYYY\n-----END CERTIFICATE-----\n\",\"client_email\":\"email@example.com\",\"client_id\":\"client-id\",\"auth_uri\":\"https://example.com/auth\",\"token_uri\":\"https://example.com/token\",\"auth_provider_x509_cert_url\":\"https://example.com/certs\",\"client_x509_cert_url\":\"https://example.com/certs/fooo%40bar\"}"
7 | key2: -----BEGIN CERTIFICATE-----\nXXXXXXXXXXXXXXXXXXXXXXXXX\nYYYYYYYYYYYYYYY/4YYYYYYYYY\nZZZZZZZZZZZZZZZZZZZZZZZZZZ\n-----END CERTIFICATE-----
8 | key3: env\\first\line
9 | key4: "$contains$dollars$otherstring"
10 | key5: "otherstringtest"
11 | key6: $contains$dollars$
12 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/externalsecret-mutator/expected-daemonset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: DaemonSet
3 | metadata:
4 | name: example
5 | namespace: externalsecret-test
6 | annotations:
7 | config.kubernetes.io/depends-on: external-secrets.io/namespaces/externalsecret-test/ExternalSecret/external-secret2
8 | spec:
9 | selector:
10 | matchLabels:
11 | name: example
12 | template:
13 | metadata:
14 | annotations:
15 | existing: annotation
16 | labels:
17 | name: example
18 | spec:
19 | initContainers:
20 | - name: example
21 | image: busybox
22 | containers:
23 | - name: example
24 | image: busybox:v1.0.0
25 | volumes:
26 | - name: varlog
27 | hostPath:
28 | path: /var/log
29 | - name: example
30 | secret:
31 | secretName: custom-secret-name
32 | optional: true
33 |
--------------------------------------------------------------------------------
/docs/40_hydrate.md:
--------------------------------------------------------------------------------
1 | # Hydration Logic
2 |
3 | The `hydrate` subcommand is an helper to fill kustomization configuration files with resources and patches.
4 |
5 | For doing so without launching multiple commands with different parameters, and leaving the user being able to add
6 | specific patches that needs custom targeting will only add files that conform to specific regex and we will skip files
7 | that are already present in the relative section in the file.
8 |
9 | The first regex will match any file that ends with `.patch.yaml` or `.patch.yml` and the files `patch.yaml`
10 | and `patch.yml`. These files will be assumed that they contains a strategic-merge-style patch and they will be
11 | added to the `patches` section of the kustomize file.
12 |
13 | The other regex will match every file that has the `yaml` or `yml` extensions that has not been matched in the previous
14 | regex.
15 | These files will be added to the `resources` section.
16 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/deployment-smart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | template:
11 | metadata:
12 | labels:
13 | app: example
14 | spec:
15 | initContainers:
16 | - name: example
17 | image: busybox:stable
18 | env:
19 | - name: ENV
20 | valueFrom:
21 | secretKeyRef:
22 | key: key
23 | name: example
24 | key: key
25 | containers:
26 | - name: example
27 | image: busybox:1.35.0@sha256:5be7104a4306abe768359a5379e6050ef69a29e9a5f99fcf7f46d5f7e9ba29a2
28 | resources:
29 | limits:
30 | memory: "128Mi"
31 | cpu: "500m"
32 | volumes:
33 | - name: volume
34 | configMap:
35 | name: example
36 |
--------------------------------------------------------------------------------
/pkg/cmd/interpolate/testdata/file.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: test
5 | namespace: {{SIMPLE_ENV}}
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: test
10 | template:
11 | replicas: {{NUMBER_ENV}}
12 | metadata:
13 | labels:
14 | app: test
15 | spec:
16 | containers:
17 | - name: test
18 | image: nginx:latest
19 | env:
20 | - name: env1
21 | value: '{{MULTILINE_STRING_ESCAPED_ENV}}'
22 | - name: env2
23 | value: '{{SPECIAL_JSON_ENV}}'
24 | - name: env3
25 | value: "{{JSON_MULTILINE_ENV}}"
26 | - name: env4
27 | value: "abc{{JSON_ESCAPED_ENV}}def"
28 | - name: env5
29 | value: '{{HTML}}'
30 | - name: env6
31 | value: "{{NUMBER_ENV}}"
32 | resources:
33 | limits:
34 | memory: "128Mi"
35 | cpu: "500m"
36 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/expected-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | template:
11 | metadata:
12 | annotations:
13 | mia-platform.eu/deploy-checksum: test-identifier
14 | labels:
15 | app: example
16 | spec:
17 | initContainers:
18 | - name: example
19 | image: busybox:v1.0.0
20 | env:
21 | - name: ENV
22 | valueFrom:
23 | secretKeyRef:
24 | key: key
25 | name: example
26 | key: key
27 | containers:
28 | - name: example
29 | image: busybox
30 | resources:
31 | limits:
32 | memory: "128Mi"
33 | cpu: "500m"
34 | volumes:
35 | - name: volume
36 | configMap:
37 | name: example
38 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/externalsecret-mutator/expected-pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: example
5 | namespace: externalsecret-test
6 | annotations:
7 | config.kubernetes.io/depends-on: external-secrets.io/namespaces/externalsecret-test/ExternalSecret/external-secret,external-secrets.io/namespaces/externalsecret-test/ExternalSecret/external-secret2
8 | labels:
9 | name: example
10 | spec:
11 | initContainers:
12 | - name: init
13 | image: busybox
14 | env:
15 | - name: ENV
16 | valueFrom:
17 | secretKeyRef:
18 | key: otherkey
19 | name: external-secret
20 | containers:
21 | - name: example
22 | image: busybox
23 | resources:
24 | limits:
25 | memory: "128Mi"
26 | cpu: "500m"
27 | volumes:
28 | - name: volume
29 | secret:
30 | secretName: external-secret
31 | - name: volume
32 | secret:
33 | secretName: custom-secret-name
34 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/new-feature.yml:
--------------------------------------------------------------------------------
1 | name: New Feature
2 | description: Describe a new feature that you want implemented
3 | title: "[Feature]: "
4 | labels:
5 | - enhancement
6 | - needs triage
7 | body:
8 | - type: markdown
9 | attributes:
10 | value: "## Thank you for contributing to our project!"
11 | - type: markdown
12 | attributes:
13 | value: Thanks for taking the time to fill out this feature request.
14 | - id: new-feature
15 | type: textarea
16 | attributes:
17 | label: Describe the feature
18 | description: |
19 | Please describe what you want to be implemented and why.
20 | validations:
21 | required: true
22 | - id: code-of-conduct
23 | type: checkboxes
24 | attributes:
25 | label: Code of Conduct
26 | description: By submitting this feature, you agree to follow our [Code of Conduct](./CODE_OF_CONDUCT.md)
27 | options:
28 | - label: I agree to follow this project’s Code of Conduct
29 | required: true
30 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/dependency-mutator/expected-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | template:
11 | metadata:
12 | annotations:
13 | mia-platform.eu/dependencies-checksum: e6639472ab29288cafccc49c310dcf7b21109602c2db25e34b10de1041389043
14 | labels:
15 | app: example
16 | spec:
17 | initContainers:
18 | - name: example
19 | image: busybox
20 | env:
21 | - name: ENV
22 | valueFrom:
23 | secretKeyRef:
24 | key: data
25 | name: example
26 | containers:
27 | - name: example
28 | image: busybox
29 | resources:
30 | limits:
31 | memory: "128Mi"
32 | cpu: "500m"
33 | volumes:
34 | - name: volume
35 | configMap:
36 | name: example
37 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/externalsecret-mutator/expected-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: example
5 | namespace: externalsecret-test
6 | annotations:
7 | config.kubernetes.io/depends-on: external-secrets.io/namespaces/externalsecret-test/ExternalSecret/external-secret
8 | spec:
9 | selector:
10 | matchLabels:
11 | app: example
12 | template:
13 | metadata:
14 | labels:
15 | app: example
16 | spec:
17 | initContainers:
18 | - name: example
19 | image: busybox
20 | env:
21 | - name: ENV
22 | valueFrom:
23 | secretKeyRef:
24 | key: data
25 | name: external-secret
26 | containers:
27 | - name: example
28 | image: busybox
29 | resources:
30 | limits:
31 | memory: "128Mi"
32 | cpu: "500m"
33 | volumes:
34 | - name: volume
35 | configMap:
36 | name: example
37 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package main
17 |
18 | import (
19 | "os"
20 |
21 | "github.com/mia-platform/mlp/v2/pkg/cmd"
22 |
23 | // Import to initialize client auth plugins.
24 | _ "k8s.io/client-go/plugin/pkg/client/auth"
25 | )
26 |
27 | func main() {
28 | rootCmd := cmd.NewRootCommand()
29 | if err := rootCmd.Execute(); err != nil {
30 | os.Exit(1)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/deploy-mutator/expected-deployment-smart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: example
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: example
10 | template:
11 | metadata:
12 | annotations:
13 | mia-platform.eu/deploy-checksum: test-identifier
14 | labels:
15 | app: example
16 | spec:
17 | initContainers:
18 | - name: example
19 | image: busybox:stable
20 | env:
21 | - name: ENV
22 | valueFrom:
23 | secretKeyRef:
24 | key: key
25 | name: example
26 | key: key
27 | containers:
28 | - name: example
29 | image: busybox:1.35.0@sha256:5be7104a4306abe768359a5379e6050ef69a29e9a5f99fcf7f46d5f7e9ba29a2
30 | resources:
31 | limits:
32 | memory: "128Mi"
33 | cpu: "500m"
34 | volumes:
35 | - name: volume
36 | configMap:
37 | name: example
38 |
--------------------------------------------------------------------------------
/pkg/cmd/interpolate/testdata/results/file.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: test
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: test
10 | template:
11 | replicas: 4
12 | metadata:
13 | labels:
14 | app: test
15 | spec:
16 | containers:
17 | - name: test
18 | image: nginx:latest
19 | env:
20 | - name: env1
21 | value: 'env\\first\line\nenv\tsecondline\nenvthirdline\n'
22 | - name: env2
23 | value: '{ "foo": "bar\ntaz" }'
24 | - name: env3
25 | value: "{\n \"first\": \"field\",\n \"second\": \"field\",\n \"third\": \"field\",\n \"fourth\": \"field\"\n }"
26 | - name: env4
27 | value: "abc{ \"foo\": \"bar\" }def"
28 | - name: env5
29 | value: 'env with spaces and "'
30 | - name: env6
31 | value: "4"
32 | resources:
33 | limits:
34 | memory: "128Mi"
35 | cpu: "500m"
36 |
--------------------------------------------------------------------------------
/pkg/cmd/interpolate/testdata/stdin/output.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: test
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: test
10 | template:
11 | replicas: 4
12 | metadata:
13 | labels:
14 | app: test
15 | spec:
16 | containers:
17 | - name: test
18 | image: nginx:latest
19 | env:
20 | - name: env1
21 | value: 'env\\first\line\nenv\tsecondline\nenvthirdline\n'
22 | - name: env2
23 | value: '{ "foo": "bar\ntaz" }'
24 | - name: env3
25 | value: "{\n \"first\": \"field\",\n \"second\": \"field\",\n \"third\": \"field\",\n \"fourth\": \"field\"\n }"
26 | - name: env4
27 | value: "abc{ \"foo\": \"bar\" }def"
28 | - name: env5
29 | value: 'env with spaces and "'
30 | - name: env6
31 | value: "4"
32 | resources:
33 | limits:
34 | memory: "128Mi"
35 | cpu: "500m"
36 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/documentation.yml:
--------------------------------------------------------------------------------
1 | name: Documenation
2 | description: Request a missing documentation or the correction of a wrong one
3 | title: "[Docs]: "
4 | labels:
5 | - documentation
6 | - needs triage
7 | body:
8 | - type: markdown
9 | attributes:
10 | value: "## Thank you for contributing to our project!"
11 | - type: markdown
12 | attributes:
13 | value: Thanks for taking the time to fill out this documenation request.
14 | - id: documentation
15 | type: textarea
16 | attributes:
17 | label: Documentation Revision
18 | description: |
19 | Please describe what you want to be documented or the docs that must be corrected and why.
20 | validations:
21 | required: true
22 | - id: code-of-conduct
23 | type: checkboxes
24 | attributes:
25 | label: Code of Conduct
26 | description: By submitting this documentation revision, you agree to follow our [Code of Conduct](./CODE_OF_CONDUCT.md)
27 | options:
28 | - label: I agree to follow this project’s Code of Conduct
29 | required: true
30 |
--------------------------------------------------------------------------------
/pkg/cmd/interpolate/testdata/results-missing-path/file.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: test
5 | namespace: test
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: test
10 | template:
11 | replicas: 4
12 | metadata:
13 | labels:
14 | app: test
15 | spec:
16 | containers:
17 | - name: test
18 | image: nginx:latest
19 | env:
20 | - name: env1
21 | value: 'env\\first\line\nenv\tsecondline\nenvthirdline\n'
22 | - name: env2
23 | value: '{ "foo": "bar\ntaz" }'
24 | - name: env3
25 | value: "{\n \"first\": \"field\",\n \"second\": \"field\",\n \"third\": \"field\",\n \"fourth\": \"field\"\n }"
26 | - name: env4
27 | value: "abc{ \"foo\": \"bar\" }def"
28 | - name: env5
29 | value: 'env with spaces and "'
30 | - name: env6
31 | value: "4"
32 | resources:
33 | limits:
34 | memory: "128Mi"
35 | cpu: "500m"
36 |
--------------------------------------------------------------------------------
/pkg/cmd/root_test.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package cmd
17 |
18 | import (
19 | "testing"
20 |
21 | "github.com/stretchr/testify/assert"
22 | )
23 |
24 | func TestRootCommand(t *testing.T) {
25 | t.Parallel()
26 |
27 | cmd := NewRootCommand()
28 | assert.NotNil(t, cmd)
29 | }
30 |
31 | func TestVersionCommand(t *testing.T) {
32 | t.Parallel()
33 |
34 | cmd := versionCommand()
35 | assert.NotNil(t, cmd)
36 | assert.NoError(t, cmd.Execute())
37 | }
38 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/testdata/expectations/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: example
5 | namespace: mlp-deploy-test
6 | annotations:
7 | config.kubernetes.io/depends-on: external-secrets.io/namespaces/mlp-deploy-test/ExternalSecret/external-secret
8 | spec:
9 | selector:
10 | matchLabels:
11 | app: example
12 | template:
13 | metadata:
14 | annotations:
15 | mia-platform.eu/dependencies-checksum: "e668e6cbb6e786b4b46b853136cfc9fac4effe474dbef3a8420339cc353b13d1"
16 | mia-platform.eu/deploy-checksum: "a2d1ace0489d09c0ca26a1ab8a8bc9b11e4365cb4f904c434565a59119f3eb15"
17 | labels:
18 | app: example
19 | spec:
20 | containers:
21 | - image: nginx:latest
22 | name: example
23 | resources:
24 | limits:
25 | cpu: 500m
26 | memory: 128Mi
27 | env:
28 | - name: ENV
29 | valueFrom:
30 | secretKeyRef:
31 | key: secret-key
32 | name: external-secret
33 | volumes:
34 | - name: example
35 | configMap:
36 | name: example
37 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | updates:
4 | # keep up to date the github actions
5 | - package-ecosystem: github-actions
6 | directory: /
7 | schedule:
8 | interval: monthly
9 | timezone: Europe/Rome
10 | groups:
11 | minor-actions-dependencies:
12 | update-types:
13 | - minor
14 | - patch
15 | commit-message:
16 | include: scope
17 | prefix: ci
18 |
19 | # keep up to date the base docker image
20 | - package-ecosystem: docker
21 | directory: /
22 | schedule:
23 | interval: daily
24 | time: "07:00"
25 | timezone: Europe/Rome
26 | commit-message:
27 | include: scope
28 | prefix: build
29 |
30 | # enable go dependencies security updates
31 | - directory: /
32 | open-pull-requests-limit: 0
33 | package-ecosystem: gomod
34 | rebase-strategy: auto
35 | schedule:
36 | interval: daily
37 | time: "07:00"
38 | timezone: Europe/Rome
39 | commit-message:
40 | include: scope
41 | prefix: chore
42 |
43 | # keep up to date devcontainers
44 | - package-ecosystem: devcontainers
45 | directory: "/"
46 | schedule:
47 | interval: monthly
48 | timezone: Europe/Rome
49 | commit-message:
50 | include: scope
51 | prefix: build
52 |
--------------------------------------------------------------------------------
/tools/make/release.mk:
--------------------------------------------------------------------------------
1 | # Copyright Mia srl
2 | # SPDX-License-Identifier: Apache-2.0
3 |
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 |
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | ##@ Release Goals
17 |
18 | SNAPSHOT_RELEASE?= 1
19 | GORELEASER_SNAPSHOT:=
20 |
21 | ifeq ($(SNAPSHOT_RELEASE), 1)
22 | GORELEASER_SNAPSHOT=--snapshot
23 | endif
24 |
25 | .PHONY: goreleaser/release
26 | goreleaser/release:
27 | $(GORELEASER_PATH) release $(GORELEASER_SNAPSHOT) --clean --config=.goreleaser.yaml
28 |
29 | goreleaser/check:
30 | $(GORELEASER_PATH) check --config=.goreleaser.yaml
31 |
32 | .PHONY: release-deps
33 | release-deps: $(GORELEASER_PATH)
34 |
35 | .PHONY: ci-release
36 | ci-release: release-deps goreleaser/release
37 |
38 | .PHONY: goreleaser-check
39 | goreleaser-check: release-deps goreleaser/check
40 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | # See https://pre-commit.com for more information
2 | # See https://pre-commit.com/hooks.html for more hooks
3 | repos:
4 | - repo: local
5 | hooks:
6 | - id: run-lint
7 | name: Run make lint
8 | entry: make lint
9 | language: system
10 | pass_filenames: false
11 | - id: run-tests
12 | name: Run make test
13 | entry: make test
14 | language: system
15 | pass_filenames: false
16 | - repo: https://github.com/pre-commit/pre-commit-hooks
17 | rev: v6.0.0
18 | hooks:
19 | - id: check-case-conflict
20 | name: Check filename case conflicts
21 | - id: check-merge-conflict
22 | name: Check that no merge conflict marker exists
23 | - id: check-executables-have-shebangs
24 | name: Check that executable files have shebangs
25 | - id: check-shebang-scripts-are-executable
26 | name: Check that files with shebangs are executable
27 | - id: end-of-file-fixer
28 | name: Makes sure files end in a newline and only a newline
29 | - id: trailing-whitespace
30 | name: Trims trailing whitespace
31 | args: [--markdown-linebreak-ext=md] # add exception for markdown linebreaks
32 | - repo: https://github.com/gitleaks/gitleaks
33 | rev: v8.30.0
34 | hooks:
35 | - id: gitleaks
36 | name: Protect and discover secrets using Gitleaks
37 |
--------------------------------------------------------------------------------
/tools/make/clean.mk:
--------------------------------------------------------------------------------
1 | # Copyright Mia srl
2 | # SPDX-License-Identifier: Apache-2.0
3 |
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 |
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | ##@ Lint Goals
17 |
18 | .PHONY: clean
19 | clean:
20 |
21 | .PHONY: clean/coverage
22 | clean: clean/coverage
23 | clean/coverage:
24 | $(info Clean coverage file...)
25 | rm -fr coverage.txt
26 |
27 | .PHONY: clean/bin
28 | clean: clean/bin
29 | clean/bin:
30 | $(info Clean artifacts files...)
31 | rm -fr $(OUTPUT_DIR)
32 |
33 | .PHONY: clean/tools
34 | clean/tools:
35 | $(info Clean tools folder...)
36 | [ -d $(TOOLS_BIN)/k8s ] && chmod +w $(TOOLS_BIN)/k8s/* || true
37 | rm -fr $(TOOLS_BIN)
38 |
39 | .PHONY: clean/go
40 | clean/go:
41 | $(info Clean golang cache...)
42 | go clean -cache
43 |
44 | .PHONY: clean-all
45 | clean-all: clean clean/tools clean/go
46 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/externalsecret-mutator/external-secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: ExternalSecret
3 | metadata:
4 | name: external-secret
5 | namespace: externalsecret-test
6 | spec:
7 | refreshInterval: 1h
8 | secretStoreRef:
9 | name: secret-store
10 | target:
11 | creationPolicy: Owner
12 | data:
13 | - secretKey: secret-key
14 | remoteRef:
15 | key: provider-key
16 | version: provider-key-version
17 | property: provider-key-property
18 | sourceRef:
19 | # point to a SecretStore that should be used to fetch a secret.
20 | # must be defined if no spec.secretStoreRef is defined.
21 | storeRef:
22 | name: secret-store
23 | kind: ClusterSecretStore
24 | - secretKey:
25 | remoteRef:
26 | key: provider-key2
27 | version: provider-key-version2
28 | property: provider-key-property2
29 | sourceRef:
30 | generatorRef:
31 | apiVersion: generators.external-secrets.io/v1alpha1
32 | kind: ECRAuthorizationToken
33 | name: "my-ecr"
34 | dataFrom:
35 | - sourceRef:
36 | # point to a SecretStore that should be used to fetch a secret.
37 | # must be defined if no spec.secretStoreRef is defined.
38 | storeRef:
39 | name: secret-store
40 | kind: SecretStore
41 | - sourceRef:
42 | generatorRef:
43 | apiVersion: generators.external-secrets.io/v1alpha1
44 | kind: ECRAuthorizationToken
45 | name: "my-ecr"
46 |
--------------------------------------------------------------------------------
/pkg/cmd/generate/types.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package generate
17 |
18 | // dockerConfigJSON represents a local docker auth config file
19 | // for pulling images.
20 | type dockerConfigJSON struct {
21 | Auths dockerConfig `json:"auths" datapolicy:"token"`
22 | }
23 |
24 | // dockerConfig represents the config file used by the docker CLI.
25 | // This config that represents the credentials that should be used
26 | // when pulling images from specific image repositories.
27 | type dockerConfig map[string]dockerConfigEntry
28 |
29 | // dockerConfigEntry holds the user information that grant the access to docker registry
30 | type dockerConfigEntry struct {
31 | Username string `json:"username,omitempty"`
32 | Password string `json:"password,omitempty" datapolicy:"password"`
33 | Email string `json:"email,omitempty"`
34 | Auth string `json:"auth,omitempty" datapolicy:"token"`
35 | }
36 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yaml:
--------------------------------------------------------------------------------
1 | name: Code Scanning
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | branches:
8 | - main
9 | paths:
10 | - '**/*.go'
11 | - 'go.mod'
12 | - 'go.sum'
13 | - '.github/workflows/codeql.yml'
14 | schedule:
15 | - cron: 0 5 * * 1 # Run at 05:00 UTC every Monday
16 |
17 | permissions: {}
18 |
19 | # This allows a subsequently queued workflow run to interrupt previous runs
20 | concurrency:
21 | group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
22 | cancel-in-progress: true
23 |
24 | jobs:
25 | codeql:
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 | steps:
32 | - name: Checkout repository
33 | uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
34 | with:
35 | show-progress: false
36 | - name: Setup Golang
37 | uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
38 | with:
39 | go-version-file: go.mod
40 | - name: Initialize CodeQL
41 | uses: github/codeql-action/init@fe4161a26a8629af62121b670040955b330f9af2 # v4.31.6
42 | with:
43 | languages: go
44 | build-mode: manual
45 | - name: Run Build
46 | run: go build .
47 | - name: Perform CodeQL Analysis
48 | uses: github/codeql-action/analyze@fe4161a26a8629af62121b670040955b330f9af2 # v4.31.6
49 | with:
50 | category: "/language:go"
51 |
--------------------------------------------------------------------------------
/docs/50_interpolate.md:
--------------------------------------------------------------------------------
1 | # Interpolatation Template
2 |
3 | `mlp` is supporting a templating engine that is used in some of the commands to been able to use environment values
4 | as sources of part of the manifests generated by Mia-Platform Console.
5 |
6 | For now the templating engine is only supporting the substitution of values contained in environment variables
7 | using their direct name encompassed between `{{}}` without spaces. Like for example:
8 |
9 | ```yaml
10 | apiVersion: v1
11 | kind: Secret
12 | metadata:
13 | name: example
14 | type: Opaque
15 | data:
16 | key: {{ENVIRONMENT_NAME}}
17 | ```
18 |
19 | The commands that support the interpolation template will also support to set a series of prefixes to prepend to the
20 | environment variable name to allow overrides and default values. The rule is that every prefixes will be check in
21 | order before the actual variables and if a value is found the call chain is stopped.
22 | For example if you set two prefixes like `TEST_` and `PRODUCTION_` for the previous yaml, the interpolation engine
23 | will check the existence of `TEST_ENVIRONMENT_NAME`, `PRODUCTION_ENVIRONMENT_NAME` and `ENVIRONMENT_NAME` in order
24 | and substitute the first of this three variables that contains a value.
25 |
26 | By default this interpolation will set the value on a single line putting the `\n` character explicitly for every
27 | new line found in the value.
28 | If the interpolation sequence is found surrounded by the `"` or `'` character we will also escape the content contained
29 | in the environment for you so that the resulting string will be a valid double or single quoted string.
30 |
--------------------------------------------------------------------------------
/pkg/extensions/testdata/externalsecret-mutator/expected-external-secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: ExternalSecret
3 | metadata:
4 | name: external-secret
5 | namespace: externalsecret-test
6 | annotations:
7 | config.kubernetes.io/depends-on: external-secrets.io/namespaces/externalsecret-test/SecretStore/secret-store
8 | spec:
9 | refreshInterval: 1h
10 | secretStoreRef:
11 | name: secret-store
12 | target:
13 | creationPolicy: Owner
14 | data:
15 | - secretKey: secret-key
16 | remoteRef:
17 | key: provider-key
18 | version: provider-key-version
19 | property: provider-key-property
20 | sourceRef:
21 | # point to a SecretStore that should be used to fetch a secret.
22 | # must be defined if no spec.secretStoreRef is defined.
23 | storeRef:
24 | name: secret-store
25 | kind: ClusterSecretStore
26 | - secretKey:
27 | remoteRef:
28 | key: provider-key2
29 | version: provider-key-version2
30 | property: provider-key-property2
31 | sourceRef:
32 | generatorRef:
33 | apiVersion: generators.external-secrets.io/v1alpha1
34 | kind: ECRAuthorizationToken
35 | name: "my-ecr"
36 | dataFrom:
37 | - sourceRef:
38 | # point to a SecretStore that should be used to fetch a secret.
39 | # must be defined if no spec.secretStoreRef is defined.
40 | storeRef:
41 | name: secret-store
42 | kind: SecretStore
43 | - sourceRef:
44 | generatorRef:
45 | apiVersion: generators.external-secrets.io/v1alpha1
46 | kind: ECRAuthorizationToken
47 | name: "my-ecr"
48 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Bug Report
2 | description: Report a bug that you have experienced
3 | title: "[Bug]: "
4 | labels:
5 | - bug
6 | - needs triage
7 | body:
8 | - type: markdown
9 | attributes:
10 | value: "## Thank you for contributing to our project!"
11 | - type: markdown
12 | attributes:
13 | value: Thanks for taking the time to fill out this bug report.
14 | - id: version
15 | type: input
16 | attributes:
17 | label: What version or versions you have tested?
18 | description: Add one or more version where the bug is present
19 | placeholder: ex. 1.0.0 or 1.0.0,1.0.1
20 | - id: operating-systems
21 | type: checkboxes
22 | attributes:
23 | label: Which operating systems have you used?
24 | description: You may select more than one.
25 | options:
26 | - label: macOS
27 | - label: Windows
28 | - label: Linux
29 | - id: expectation
30 | type: textarea
31 | attributes:
32 | label: What did you expect to happen?
33 | description: |
34 | Describe what did you expect to happen if this bug wasn’t there.
35 | validations:
36 | required: true
37 | - id: problem
38 | type: textarea
39 | attributes:
40 | label: What happened instead?
41 | description: |
42 | Please describe what happened and provide every detail you can for reproducing it.
43 | validations:
44 | required: true
45 | - id: code-of-conduct
46 | type: checkboxes
47 | attributes:
48 | label: Code of Conduct
49 | description: By submitting this issue, you agree to follow our [Code of Conduct](./CODE_OF_CONDUCT.md)
50 | options:
51 | - label: I agree to follow this project’s Code of Conduct
52 | required: true
53 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Go",
3 | "image": "golang:1.25.4",
4 | "features": {
5 | "ghcr.io/devcontainers/features/common-utils:2": {
6 | "installZsh": "true",
7 | "username": "vscode",
8 | "userUid": "1000",
9 | "userGid": "1000",
10 | "upgradePackages": "true"
11 | },
12 | "ghcr.io/devcontainers/features/go:1": {
13 | "version": "none",
14 | "golangciLintVersion": "2.6.2"
15 | },
16 | "ghcr.io/devcontainers/features/git:1": {
17 | "version": "latest",
18 | "ppa": "false"
19 | }
20 | },
21 | "runArgs": [
22 | "--cap-add=SYS_PTRACE",
23 | "--security-opt",
24 | "seccomp=unconfined"
25 | ],
26 | "customizations": {
27 | "vscode": {
28 | "settings": {
29 | "files.eol": "\n",
30 | "files.trimFinalNewlines": true,
31 | "files.insertFinalNewline": true,
32 | "files.trimTrailingWhitespace": false,
33 | "go.gopath": "/go",
34 | "go.useLanguageServer": true,
35 | "go.toolsManagement.checkForUpdates": "local",
36 | "go.buildFlags": ["-tags=conformance,integration"],
37 | "go.lintTool": "golangci-lint",
38 | "go.lintFlags": [
39 | "--path-mode=abs",
40 | "--fast-only"
41 | ],
42 | "go.formatTool": "custom",
43 | "go.alternateTools": {
44 | "customFormatter": "golangci-lint"
45 | },
46 | "go.formatFlags": [
47 | "fmt",
48 | "--stdin"
49 | ]
50 | },
51 | "extensions": [
52 | "redhat.vscode-yaml",
53 | "timonwong.shellcheck",
54 | "editorconfig.editorconfig",
55 | "davidanson.vscode-markdownlint"
56 | ]
57 | },
58 | "codespaces": {
59 | "openFiles": [
60 | "README.md",
61 | "CONTRIBUTING.md"
62 | ]
63 | }
64 | },
65 | "remoteUser": "vscode"
66 | }
67 |
--------------------------------------------------------------------------------
/tools/make/lint.mk:
--------------------------------------------------------------------------------
1 | # Copyright Mia srl
2 | # SPDX-License-Identifier: Apache-2.0
3 |
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 |
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | ##@ Lint Goals
17 |
18 | # if not already installed in the system install a pinned version in tools folder
19 | GOLANGCI_PATH:= $(shell command -v golangci-lint 2> /dev/null)
20 | ifndef GOLANGCI_PATH
21 | GOLANGCI_PATH:=$(TOOLS_BIN)/golangci-lint
22 | endif
23 |
24 | .PHONY: lint
25 | lint:
26 |
27 | .PHONY: lint-deps
28 | lint-deps:
29 |
30 | .PHONY: golangci-lint
31 | lint: golangci-lint
32 | golangci-lint: $(GOLANGCI_PATH)
33 | $(info Running golangci-lint with .golangci.yaml config file...)
34 | $(GOLANGCI_PATH) run --config=.golangci.yaml
35 |
36 | lint-deps: $(GOLANGCI_PATH)
37 | $(TOOLS_BIN)/golangci-lint: $(TOOLS_DIR)/GOLANGCI_LINT_VERSION
38 | $(eval GOLANGCI_LINT_VERSION:= $(shell cat $<))
39 | mkdir -p $(TOOLS_BIN)
40 | $(info Installing golangci-lint $(GOLANGCI_LINT_VERSION) bin in $(TOOLS_BIN))
41 | GOBIN=$(TOOLS_BIN) go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)
42 |
43 | .PHONY: gomod-lint
44 | lint: gomod-lint
45 | gomod-lint:
46 | $(info Running go mod tidy)
47 | # Always keep this version to latest -1 version of Go
48 | go mod tidy -compat=1.24
49 |
50 | .PHONY: ci-lint
51 | ci-lint: lint
52 | # Block the lint during ci if the go.mod and go.sum will be changed by go mod tidy
53 | git diff --exit-code go.mod;
54 | git diff --exit-code go.sum;
55 |
--------------------------------------------------------------------------------
/.goreleaser.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 | dist: bin
3 |
4 | builds:
5 | - main: "{{ .Env.BUILD_PATH }}"
6 | binary: >-
7 | {{ .Os }}/
8 | {{- .Arch }}/
9 | {{- with .Arm }}v{{ . }}/{{ end }}
10 | {{- .ProjectName }}
11 | no_unique_dist_dir: true
12 | env:
13 | - CGO_ENABLED=0
14 | flags:
15 | - -trimpath
16 | ldflags:
17 | - -s
18 | - -w
19 | - -X {{ .Env.VERSION_MODULE_NAME }}.Version={{ .Version }}
20 | - -X {{ .Env.VERSION_MODULE_NAME }}.BuildDate={{ .Date }}
21 | goos:
22 | - linux
23 | - darwin
24 | goarch:
25 | - amd64
26 | - arm
27 | - arm64
28 | - "386"
29 | goarm:
30 | - "6"
31 | - "7"
32 |
33 | archives:
34 | - formats:
35 | - binary
36 | name_template: >-
37 | {{ .Binary }}-
38 | {{- .Os }}-
39 | {{- .Arch }}{{ with .Arm }}v{{ . }}{{ end }}
40 | {{- with .Mips }}-{{ . }}{{ end }}
41 | {{- if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}
42 |
43 | checksum:
44 | name_template: checksums.txt
45 |
46 | snapshot:
47 | version_template: "{{ .ShortCommit }}"
48 |
49 | changelog:
50 | sort: asc
51 | groups:
52 | - title: Features
53 | regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
54 | order: 0
55 | - title: Bug Fixes
56 | regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
57 | order: 1
58 | - title: Others
59 | order: 999
60 |
61 | brews:
62 | - name: "{{ .Env.CMDNAME }}"
63 | repository:
64 | owner: mia-platform
65 | name: homebrew-tap
66 |
67 | commit_author:
68 | name: bot-targa
69 | email: github@mia-platform.eu
70 |
71 | commit_msg_template: "Brew formula update for {{ .ProjectName }} version {{ .Tag }}"
72 | directory: Formula
73 |
74 | install: |
75 | bin.install "{{ .ArtifactName }}" => "{{ .Env.CMDNAME }}"
76 |
77 | chmod 0555, bin/"{{ .Env.CMDNAME }}" # generate_completions_from_executable fails otherwise
78 | generate_completions_from_executable(bin/"{{ .Env.CMDNAME }}", "completion")
79 |
80 | homepage: "https://www.mia-platform.eu"
81 | description: "{{ .Env.DESCRIPTION }}"
82 |
83 | license: "Apache-2.0"
84 | skip_upload: auto
85 |
86 | # yaml-language-server: $schema=https://goreleaser.com/static/schema.json
87 | # vim: set ts=2 sw=2 tw=0 fo=cnqoj
88 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mlp
2 |
3 |
4 |
5 | [![Build Status][github-actions-svg]][github-actions]
6 | [![Go Report Card][go-report-card]][go-report-card-link]
7 | [![GoDoc][godoc-svg]][godoc-link]
8 |
9 |
10 |
11 | `mlp` is a command line tool responsible for creating, updating and deleting kubernetes resources based on files
12 | generated by Mia-Platform Console.
13 | The main subcommands that the tool has are:
14 |
15 | - `interpolate`: fill placeholders in kubernetes files with the values of ENV variables
16 | - `generate`: create files for kubernetes `ConfigMap` and `Secret` based on files and/or ENV values
17 | - `deploy`: create and/or update resources in a kubernetes namespace with the intepolated/generated files
18 | - `kustomize`: run kustomize build
19 | - `hydrate`: helper to fill kustomization.yml with resources and patches
20 | - `completion`: generate the autocompletion
21 |
22 | ## To Start Using `mlp`
23 |
24 | Read the documentation [here](./docs/10_overview.md).
25 |
26 | ## To Start Developing `mlp`
27 |
28 | To start developing the CLI you must have this requirements:
29 |
30 | - golang 1.22
31 | - make
32 |
33 | Once you have pulled the code locally, you can build the code with make:
34 |
35 | ```sh
36 | make build
37 | ```
38 |
39 | `make` will download all the dependencies needed and will build the binary for your current system that you can find
40 | in the `bin` folder.
41 |
42 | To build the docker image locally run:
43 |
44 | ```sh
45 | make docker-build
46 | ```
47 |
48 | ## Testing `mlp`
49 |
50 | To run the tests use the command:
51 |
52 | ```sh
53 | make test
54 | ```
55 |
56 | Or add the `DEBUG_TEST` flag to run the test with debug mode enabled:
57 |
58 | ```sh
59 | make test DEBUG_TEST=1
60 | ```
61 |
62 | Before sending a PR be sure that all the linter pass with success:
63 |
64 | ```sh
65 | make lint
66 | ```
67 |
68 | [github-actions]: https://github.com/mia-platform/mlp/actions
69 | [github-actions-svg]: https://github.com/mia-platform/mlp/workflows/Continuous%20Integration%20Pipeline/badge.svg
70 | [godoc-svg]: https://godoc.org/github.com/mia-platform/mlp?status.svg
71 | [godoc-link]: https://godoc.org/github.com/mia-platform/mlp
72 | [go-report-card]: https://goreportcard.com/badge/github.com/mia-platform/mlp
73 | [go-report-card-link]: https://goreportcard.com/report/github.com/mia-platform/mlp
74 |
--------------------------------------------------------------------------------
/pkg/extensions/deployoncefilter.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package extensions
17 |
18 | import (
19 | "context"
20 |
21 | "github.com/mia-platform/jpl/pkg/client/cache"
22 | "github.com/mia-platform/jpl/pkg/filter"
23 | "github.com/mia-platform/jpl/pkg/resource"
24 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
25 | )
26 |
27 | // deployOnceFilter will implement a filter that will remove a Secret or ConfigMap if they have a value
28 | // of deployFilterValue in the deployFilterAnnotation and the resource metadata is found in the remote inventory.
29 | // In any other cases the resources are kept.
30 | type deployOnceFilter struct{}
31 |
32 | // NewDeployOnceFilter return a new filter for avoiding to apply a resource more than once in its lifetime
33 | func NewDeployOnceFilter() filter.Interface {
34 | return &deployOnceFilter{}
35 | }
36 |
37 | // Filter implement filter.Interface interface
38 | func (f *deployOnceFilter) Filter(obj *unstructured.Unstructured, getter cache.RemoteResourceGetter) (bool, error) {
39 | objGK := obj.GroupVersionKind().GroupKind()
40 |
41 | switch objGK {
42 | case configMapGK, secretGK:
43 | annotations := obj.GetAnnotations()
44 | if annotations == nil {
45 | return false, nil
46 | }
47 |
48 | if value, found := annotations[deployFilterAnnotation]; !found || value != deployFilterValue {
49 | return false, nil
50 | }
51 | default:
52 | return false, nil
53 | }
54 |
55 | remoteObj, err := getter.Get(context.Background(), resource.ObjectMetadataFromUnstructured(obj))
56 | return remoteObj != nil, err
57 | }
58 |
59 | // keep it to always check if deployOnceFilter implement correctly the filter.Interface interface
60 | var _ filter.Interface = &deployOnceFilter{}
61 |
--------------------------------------------------------------------------------
/docs/10_overview.md:
--------------------------------------------------------------------------------
1 | # mlp CLI
2 |
3 | `mlp`is a Command Line Interface that is responsible for creating, updating and deleting Kubernetes resources
4 | based on files generated by Mia-Platform Console.
5 | It can be viewed as a superpowered `kubectl apply` command that introduce additional functionalities to simply sending
6 | patches to the api-server.
7 |
8 | `mlp` can prune resources that are not present anymore between different deploys because it keeps an inventory
9 | of all the resources that has applied the last time.
10 | It can force new deployment rollout even if there are no differences between deploys, running Jobs immediately from
11 | CronJob definitions, and it will add annotations to workload resources about their Secrets and ConfigMaps dependencies.
12 | The cli will also automatically watch the progression of the applied resources and it will report what and how many
13 | resources failed to reach a ready or successfull state.
14 |
15 | In addition `mlp` can also generate ConfigMaps or Secrets via a dedicate configuration file using a combination of
16 | environment variabiles, literal values and files, giving the user the ability to not commiting sensitive data and giving
17 | the ability to use different configuration for different runtime environments.
18 |
19 | ## Functionalities
20 |
21 | - `deploy`: the main command, is used for creating, updating and pruning resources in a kubernetes
22 | environment using the resource files created by the Mia-Platform Console
23 | - `generate`: create kubernetes `ConfigMap` and `Secret` based on a configuration file
24 | - `hydrate`: is an helper function for configuring correctly the kustomization files inside the target folder
25 | with all the files and patches found
26 | - `interpolate`: will run through all the files passed and run through a templating function for render the final
27 | manifests
28 | - `kustomize`: is the same command of `kustomize build` and can be used if you project is using the kustomize structure
29 | to render the resources to pass to the `interpolate` command
30 |
31 | For more information about the various options available to the various commands you can always run
32 | `mlp --help` to see the helpers.
33 |
34 | ## Guides
35 |
36 | Below, you can find additional documentation for `mlp`:
37 |
38 | - [Setup](./20_setup.md)
39 | - [Generation Configuration](./30_generate.md)
40 | - [Hydration Logic](./40_hydrate.md)
41 | - [Interpolatation Template](./50_interpolate.md)
42 |
--------------------------------------------------------------------------------
/pkg/apis/mlp.mia-platform.eu/v1/types.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package v1
17 |
18 | import (
19 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20 | "k8s.io/apimachinery/pkg/runtime/schema"
21 | )
22 |
23 | const (
24 | GroupName = "mlp.mia-platform.eu"
25 |
26 | DataFromFile = "file"
27 | DataFromLiteral = "literal"
28 | )
29 |
30 | var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}
31 |
32 | type GenerateConfiguration struct {
33 | metav1.TypeMeta `json:",inline" yaml:",inline"`
34 |
35 | Secrets []SecretSpec `json:"secrets,omitempty" yaml:"secrets,omitempty"`
36 |
37 | //nolint:tagliatelle
38 | ConfigMaps []ConfigMapSpec `json:"config-maps,omitempty" yaml:"config-maps,omitempty"`
39 | }
40 |
41 | // SecretSpec contains secret configurations
42 | type SecretSpec struct {
43 | Name string `json:"name" yaml:"name"`
44 | When string `json:"when" yaml:"when"`
45 | TLS *TLS `json:"tls" yaml:"tls"`
46 | Docker *DockerConfig `json:"docker" yaml:"docker"`
47 | Data []Data `json:"data" yaml:"data"`
48 | }
49 |
50 | type ConfigMapSpec struct {
51 | Name string `json:"name" yaml:"name"`
52 | Data []Data `json:"data" yaml:"data"`
53 | }
54 |
55 | type TLS struct {
56 | Cert *TLSData `json:"cert" yaml:"cert"`
57 | Key *TLSData `json:"key" yaml:"key"`
58 | }
59 |
60 | type TLSData struct {
61 | From string `json:"from" yaml:"from"`
62 | File string `json:"file" yaml:"file"`
63 | Value string `json:"value" yaml:"value"`
64 | }
65 |
66 | type DockerConfig struct {
67 | Username string `json:"username" yaml:"username"`
68 | Password string `json:"password" yaml:"password"`
69 | Email string `json:"email" yaml:"email"`
70 | Server string `json:"server" yaml:"server"`
71 | }
72 |
73 | type Data struct {
74 | From string `json:"from" yaml:"from"`
75 | File string `json:"file" yaml:"file"`
76 | Key string `json:"key" yaml:"key"`
77 | Value string `json:"value" yaml:"value"`
78 | }
79 |
--------------------------------------------------------------------------------
/tools/make/test.mk:
--------------------------------------------------------------------------------
1 | # Copyright Mia srl
2 | # SPDX-License-Identifier: Apache-2.0
3 |
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 |
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | ##@ Go Tests Goals
17 |
18 | DEBUG_TEST?=
19 | ifeq ($(DEBUG_TEST),1)
20 | GO_TEST_DEBUG_FLAG:= -v
21 | else
22 | GO_TEST_DEBUG_FLAG:=
23 | endif
24 |
25 | .PHONY: test/unit
26 | test/unit:
27 | $(info Running tests...)
28 | go test $(GO_TEST_DEBUG_FLAG) -race ./...
29 |
30 | .PHONY: test/integration/setup test/integration test/integration/teardown
31 | test/integration/setup:
32 | test/integration:
33 | $(info Running integration tests...)
34 | go test $(GO_TEST_DEBUG_FLAG) -tags=integration -race ./...
35 | test/integration/teardown:
36 |
37 | .PHONY: test/coverage
38 | test/coverage:
39 | $(info Running tests with coverage on...)
40 | go test $(GO_TEST_DEBUG_FLAG) -race -coverprofile=coverage.txt -covermode=atomic ./...
41 |
42 | .PHONY: test/integration/coverage
43 | test/integration/coverage:
44 | $(info Running ci tests with coverage on...)
45 | go test $(GO_TEST_DEBUG_FLAG) -tags=integration -race -coverprofile=coverage.txt -covermode=atomic ./...
46 |
47 | .PHONY: test/conformance test/conformance/setup test/conformance/teardown
48 | test/conformance/setup:
49 | test/conformance:
50 | $(info Running conformance tests...)
51 | go test $(GO_TEST_DEBUG_FLAG) -tags=conformance -race -count=1 $(CONFORMANCE_TEST_PATH)
52 | test/conformance/teardown:
53 |
54 | test/show/coverage:
55 | go tool cover -func=coverage.txt
56 |
57 | .PHONY: test
58 | test: test/unit
59 |
60 | .PHONY: test-coverage
61 | test-coverage: test/coverage
62 |
63 | .PHONY: test-integration
64 | test-integration: test/integration/setup test/integration test/integration/teardown
65 |
66 | .PHONY: test-integration-coverage
67 | test-integration-coverage: test/integration/setup test/integration/coverage test/integration/teardown
68 |
69 | .PHONY: test-conformance
70 | test-conformance: test/conformance/setup test/conformance test/conformance/teardown
71 |
72 | .PHONY: show-coverage
73 | show-coverage: test-coverage test/show/coverage
74 |
75 | .PHONY: show-integration-coverage
76 | show-integration-coverage: test-integration-coverage test/show/coverage
77 |
--------------------------------------------------------------------------------
/tools/make/build.mk:
--------------------------------------------------------------------------------
1 | # Copyright Mia srl
2 | # SPDX-License-Identifier: Apache-2.0
3 |
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 |
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | ##@ Go Builds Goals
17 |
18 | .PHONY: build
19 | build:
20 |
21 | # if not already installed in the system install a pinned version in tools folder
22 | GORELEASER_PATH:= $(shell command -v goreleaser 2> /dev/null)
23 | ifndef GORELEASER_PATH
24 | GORELEASER_PATH:= $(TOOLS_BIN)/goreleaser
25 | endif
26 |
27 | ifeq ($(IS_LIBRARY), 1)
28 |
29 | BUILD_DATE:= $(shell date -u "+%Y-%m-%d")
30 | GO_LDFLAGS+= -s -w
31 |
32 | ifdef VERSION_MODULE_NAME
33 | GO_LDFLAGS+= -X $(VERSION_MODULE_NAME).Version=$(VERSION)
34 | GO_LDFLAGS+= -X $(VERSION_MODULE_NAME).BuildDate=$(BUILD_DATE)
35 | endif
36 |
37 | .PHONY: go/build/%
38 | go/build/%:
39 | $(eval OS:= $(word 1,$(subst /, ,$*)))
40 | $(eval ARCH:= $(word 2,$(subst /, ,$*)))
41 | $(eval ARM:= $(word 3,$(subst /, ,$*)))
42 | $(info Building image for $(OS) $(ARCH) $(ARM))
43 |
44 | GOOS=$(OS) GOARCH=$(ARCH) GOARM=$(ARM) CGO_ENABLED=0 go build -trimpath \
45 | -ldflags "$(GO_LDFLAGS)" $(BUILD_PATH)
46 |
47 | else
48 |
49 | .PHONY: go/build/%
50 | go/build/%:
51 | $(eval OS:= $(word 1,$(subst /, ,$*)))
52 | $(eval ARCH:= $(word 2,$(subst /, ,$*)))
53 | $(eval ARM:= $(word 3,$(subst /, ,$*)))
54 | $(info Building image for $(OS) $(ARCH) $(ARM))
55 |
56 | GOOS=$(OS) GOARCH=$(ARCH) GOARM=$(ARM) $(GORELEASER_PATH) build \
57 | --single-target --snapshot --clean --config=.goreleaser.yaml
58 |
59 | .PHONY: go/build/multiarch
60 | go/build/multiarch:
61 | $(GORELEASER_PATH) build --snapshot --clean --config=.goreleaser.yaml
62 |
63 | .PHONY: build-deps
64 | build-deps:
65 |
66 | build-deps: $(GORELEASER_PATH)
67 |
68 | build: build-deps
69 |
70 | .PHONY: build-multiarch
71 | build-multiarch: $(GORELEASER_PATH) go/build/multiarch
72 |
73 | endif
74 |
75 | .PHONY: build
76 | build: go/build/$(GOOS)/$(GOARCH)/$(GOARM)
77 |
78 | $(TOOLS_BIN)/goreleaser: $(TOOLS_DIR)/GORELEASER_VERSION
79 | $(eval GORELEASER_VERSION:= $(shell cat $<))
80 | mkdir -p $(TOOLS_BIN)
81 | $(info Installing goreleaser $(GORELEASER_VERSION) bin in $(TOOLS_BIN))
82 | GOBIN=$(TOOLS_BIN) go install github.com/goreleaser/goreleaser/v2@$(GORELEASER_VERSION)
83 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Custom gitignore rules
2 |
3 | bin/
4 | coverage.txt
5 |
6 | # End of Custom gitignore rules
7 |
8 | # Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,linux,windows,macos,go
9 | # Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,linux,windows,macos,go
10 |
11 | ### Go ###
12 | # If you prefer the allow list template instead of the deny list, see community template:
13 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
14 | #
15 | # Binaries for programs and plugins
16 | *.exe
17 | *.exe~
18 | *.dll
19 | *.so
20 | *.dylib
21 |
22 | # Test binary, built with `go test -c`
23 | *.test
24 |
25 | # Output of the go coverage tool, specifically when used with LiteIDE
26 | *.out
27 |
28 | # Dependency directories (remove the comment below to include it)
29 | # vendor/
30 |
31 | # Go workspace file
32 | go.work
33 |
34 | ### Linux ###
35 | *~
36 |
37 | # temporary files which can be created if a process still has a handle open of a deleted file
38 | .fuse_hidden*
39 |
40 | # KDE directory preferences
41 | .directory
42 |
43 | # Linux trash folder which might appear on any partition or disk
44 | .Trash-*
45 |
46 | # .nfs files are created when an open file is removed but is still being accessed
47 | .nfs*
48 |
49 | ### macOS ###
50 | # General
51 | .DS_Store
52 | .AppleDouble
53 | .LSOverride
54 |
55 | # Icon must end with two \r
56 | Icon
57 |
58 |
59 | # Thumbnails
60 | ._*
61 |
62 | # Files that might appear in the root of a volume
63 | .DocumentRevisions-V100
64 | .fseventsd
65 | .Spotlight-V100
66 | .TemporaryItems
67 | .Trashes
68 | .VolumeIcon.icns
69 | .com.apple.timemachine.donotpresent
70 |
71 | # Directories potentially created on remote AFP share
72 | .AppleDB
73 | .AppleDesktop
74 | Network Trash Folder
75 | Temporary Items
76 | .apdisk
77 |
78 | ### macOS Patch ###
79 | # iCloud generated files
80 | *.icloud
81 |
82 | ### VisualStudioCode ###
83 | .vscode/*
84 | !.vscode/settings.json
85 | !.vscode/tasks.json
86 | !.vscode/launch.json
87 | !.vscode/extensions.json
88 | !.vscode/*.code-snippets
89 |
90 | # Local History for Visual Studio Code
91 | .history/
92 |
93 | # Built Visual Studio Code Extensions
94 | *.vsix
95 |
96 | ### VisualStudioCode Patch ###
97 | # Ignore all local history of files
98 | .history
99 | .ionide
100 |
101 | ### Windows ###
102 | # Windows thumbnail cache files
103 | Thumbs.db
104 | Thumbs.db:encryptable
105 | ehthumbs.db
106 | ehthumbs_vista.db
107 |
108 | # Dump file
109 | *.stackdump
110 |
111 | # Folder config file
112 | [Dd]esktop.ini
113 |
114 | # Recycle Bin used on file shares
115 | $RECYCLE.BIN/
116 |
117 | # Windows Installer files
118 | *.cab
119 | *.msi
120 | *.msix
121 | *.msm
122 | *.msp
123 |
124 | # Windows shortcuts
125 | *.lnk
126 |
127 | # End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,linux,windows,macos,go
128 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | We welcome contributions from the community. Please read the following guidelines carefully to
4 | maximize the chances of your PR being merged.
5 |
6 | ## Communication
7 |
8 | - If you want to work on some open issue, please reach out to us via a comment on it before starting your work to
9 | check if someone else is already working on it.
10 | - Before working on a new feature please open an issue for the feature request and partecipate in the discussion for
11 | reaching an agreement on its design and utility for the project.
12 | - Small patches, bug fixes and documentation typos fixing don’t need prior communication.
13 |
14 | ## Inclusive Language
15 |
16 | Every PR, issue, code and documentation must be inclusive to all and must adhere to the following guidance:
17 |
18 | - Every documentation should follow an inclusive style. A nice writeup has been done by google in its [Google Developer
19 | Documentation Style Guide].
20 | - Every contribution will be covered by our [Code of Conduct](./CODE_OF_CONDUCT.md) so read it carefully.
21 | - We will follow and will amend this list with the best practice and guidance that will emerge in the industry in the
22 | future and more comments and correction can be made during review by the mantainers.
23 |
24 | ## Opening a PR
25 |
26 | - Fork the repo
27 | - Read the [README.md](./README.md) file and the others documentation files, if presents, for guidances on how to setup
28 | the project locally and running the tests.
29 | - If your PR is adding codes it must also contains test that will cover it, try to cover 100% of your added code if
30 | possibile. During the review be ready to explain why you cannot reach that percentage.
31 | - We will not merge PR with failing tests or that will lower the coverage of the existing ones.
32 | - When you open a PR please follow the indication that are provided in the template and provide all the relevant
33 | information
34 | - Your PR title should be descriptive.
35 | - If your PR is co-authored or based on an earlier PR from another contributor,
36 | please attribute them with `Co-authored-by: name `.
37 | See [GitHub’s multiple author guidance] for further details.
38 |
39 | ## Commit Message Styling
40 |
41 | Every commit in this repository must follow the guidelines provided by [Conventional commits].
42 | The following *types* are allowed:
43 |
44 | 1. `fix:` a commit that fixes a bug.
45 | 1. `feat:` a commit that adds new functionality.
46 | 1. `docs:` a commit that adds or improves the documentation.
47 | 1. `test:` a commit that adds unit tests.
48 | 1. `ci:` a commit that improves the pipelines or the integration mechanisms.
49 | 1. `style:` a commit that changes the code or documentation format and/or style without modifying the implementation.
50 | 1. `chore:` a catch-all type for any other commits. Generally used for commits that do not add or improve
51 | functionalities to code or documentation.
52 |
53 | [Google Developer Documentation Style Guide]: https://developers.google.com/style/inclusive-documentation
54 | [GitHub’s multiple author guidance]: https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors
55 | [Conventional commits]: https://www.conventionalcommits.org/en/v1.0.0/
56 |
--------------------------------------------------------------------------------
/pkg/cmd/hydrate/kustomizationFile.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package hydrate
17 |
18 | import (
19 | "bytes"
20 | "fmt"
21 | "path/filepath"
22 |
23 | "sigs.k8s.io/kustomize/api/konfig"
24 | "sigs.k8s.io/kustomize/api/types"
25 | "sigs.k8s.io/kustomize/kyaml/filesys"
26 | yaml "sigs.k8s.io/yaml/goyaml.v3"
27 | )
28 |
29 | // kustomizationFile is a struct for incapsulating operations on a kustomization file
30 | type kustomizationFile struct {
31 | path string
32 | fSys filesys.FileSystem
33 | }
34 |
35 | // newKustomizationFile returns a new instance for the given FileSystem and path
36 | func newKustomizationFile(fSys filesys.FileSystem, path string) (*kustomizationFile, error) {
37 | kf := &kustomizationFile{fSys: fSys}
38 | err := kf.validate(path)
39 | if err != nil {
40 | return nil, err
41 | }
42 | return kf, nil
43 | }
44 |
45 | // GetPath return the full path of the kustomization file including its name
46 | func (kf *kustomizationFile) GetPath() string {
47 | return kf.path
48 | }
49 |
50 | // validate will validate that only one kustomization file exists at path and is not a folder
51 | func (kf *kustomizationFile) validate(path string) error {
52 | match := 0
53 | var paths []string
54 | for _, kfilename := range konfig.RecognizedKustomizationFileNames() {
55 | fullpath := filepath.Join(path, kfilename)
56 | if kf.fSys.Exists(fullpath) {
57 | match++
58 | paths = append(paths, fullpath)
59 | }
60 | }
61 |
62 | switch match {
63 | case 0:
64 | return fmt.Errorf("missing kustomization file %q", konfig.DefaultKustomizationFileName())
65 | case 1:
66 | kf.path = paths[0]
67 | default:
68 | return fmt.Errorf("found multiple kustomization file: %v", path)
69 | }
70 |
71 | if kf.fSys.IsDir(kf.path) {
72 | return fmt.Errorf("%s should be a file", kf.path)
73 | }
74 | return nil
75 | }
76 |
77 | // read will return the Kustomization struct from file
78 | func (kf *kustomizationFile) read() (*types.Kustomization, error) {
79 | data, err := kf.fSys.ReadFile(kf.path)
80 | if err != nil {
81 | return nil, err
82 | }
83 |
84 | var k types.Kustomization
85 | if err := k.Unmarshal(data); err != nil {
86 | return nil, err
87 | }
88 |
89 | k.FixKustomization()
90 |
91 | return &k, nil
92 | }
93 |
94 | // write will save the data in the kustomization structure overriding the previous content
95 | func (kf *kustomizationFile) write(kustomization *types.Kustomization) error {
96 | buffer := new(bytes.Buffer)
97 | encoder := yaml.NewEncoder(buffer)
98 | encoder.SetIndent(2)
99 | encoder.CompactSeqIndent()
100 |
101 | if err := encoder.Encode(kustomization); err != nil {
102 | return err
103 | }
104 |
105 | if err := encoder.Close(); err != nil {
106 | return err
107 | }
108 |
109 | return kf.fSys.WriteFile(kf.path, buffer.Bytes())
110 | }
111 |
--------------------------------------------------------------------------------
/pkg/extensions/deployoncefilter_test.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package extensions
17 |
18 | import (
19 | "errors"
20 | "path/filepath"
21 | "testing"
22 |
23 | "github.com/mia-platform/jpl/pkg/client/cache"
24 | "github.com/mia-platform/jpl/pkg/resource"
25 | jpltesting "github.com/mia-platform/jpl/pkg/testing"
26 | "github.com/stretchr/testify/assert"
27 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
28 | )
29 |
30 | func TestFilter(t *testing.T) {
31 | t.Parallel()
32 |
33 | testdata := filepath.Join("testdata", "filter")
34 | filtered := jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "filtered.yaml"))
35 |
36 | tests := map[string]struct {
37 | object *unstructured.Unstructured
38 | getter cache.RemoteResourceGetter
39 | expected bool
40 | expectedError string
41 | }{
42 | "no filtering if no config map or secret": {
43 | object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "deployment.yaml")),
44 | },
45 | "no filtering if config map use labels and not annotations": {
46 | object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "configmap.yaml")),
47 | },
48 | "no filtering if annotations with other value": {
49 | object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "secret.yaml")),
50 | },
51 | "filtering if annotation is present and remote object is found": {
52 | object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "filtered.yaml")),
53 | getter: &testGetter{
54 | availableObjects: map[resource.ObjectMetadata]*unstructured.Unstructured{
55 | resource.ObjectMetadataFromUnstructured(filtered): filtered,
56 | },
57 | },
58 | expected: true,
59 | },
60 | "no filtering if annotation is present but no remote object is found": {
61 | object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "filtered.yaml")),
62 | getter: &testGetter{
63 | availableObjects: map[resource.ObjectMetadata]*unstructured.Unstructured{},
64 | },
65 | },
66 | "error getting remote object": {
67 | object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "filtered.yaml")),
68 | getter: &testGetter{
69 | availableObjects: map[resource.ObjectMetadata]*unstructured.Unstructured{},
70 | errors: map[resource.ObjectMetadata]error{
71 | resource.ObjectMetadataFromUnstructured(filtered): errors.New("error on load"),
72 | },
73 | },
74 | expectedError: "error on load",
75 | },
76 | }
77 |
78 | for name, test := range tests {
79 | t.Run(name, func(t *testing.T) {
80 | t.Parallel()
81 |
82 | filter := NewDeployOnceFilter()
83 | filtered, err := filter.Filter(test.object, test.getter)
84 | switch len(test.expectedError) {
85 | case 0:
86 | assert.NoError(t, err)
87 | default:
88 | assert.ErrorContains(t, err, test.expectedError)
89 | }
90 | assert.Equal(t, test.expected, filtered)
91 | })
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/.golangci.yaml:
--------------------------------------------------------------------------------
1 | version: "2"
2 | run:
3 | build-tags:
4 | - conformance
5 | - integration
6 | linters:
7 | default: none
8 | enable:
9 | - asasalint
10 | - asciicheck
11 | - bidichk
12 | - bodyclose
13 | - canonicalheader
14 | - contextcheck
15 | - copyloopvar
16 | - decorder
17 | - dogsled
18 | - durationcheck
19 | - embeddedstructfieldcheck
20 | - errcheck
21 | - errname
22 | - errorlint
23 | - exptostd
24 | - fatcontext
25 | - gocheckcompilerdirectives
26 | - goconst
27 | - gocritic
28 | - gocyclo
29 | - godoclint
30 | - goheader
31 | - gosec
32 | - gosmopolitan
33 | - govet
34 | - iface
35 | - ineffassign
36 | - intrange
37 | - iotamixing
38 | - misspell
39 | - mnd
40 | - nolintlint
41 | - perfsprint
42 | - prealloc
43 | - predeclared
44 | - protogetter
45 | - revive
46 | - staticcheck
47 | - tagliatelle
48 | - testifylint
49 | - thelper
50 | - tparallel
51 | - unconvert
52 | - unparam
53 | - unused
54 | - usestdlibvars
55 | - usetesting
56 | - whitespace
57 | - zerologlint
58 | settings:
59 | gocyclo:
60 | min-complexity: 15
61 | goheader:
62 | values:
63 | const:
64 | MY COMPANY: Mia srl
65 | template: |-
66 | Copyright {{ MY COMPANY }}
67 | SPDX-License-Identifier: Apache-2.0
68 |
69 | Licensed under the Apache License, Version 2.0 (the "License");
70 | you may not use this file except in compliance with the License.
71 | You may obtain a copy of the License at
72 |
73 | http://www.apache.org/licenses/LICENSE-2.0
74 |
75 | Unless required by applicable law or agreed to in writing, software
76 | distributed under the License is distributed on an "AS IS" BASIS,
77 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
78 | See the License for the specific language governing permissions and
79 | limitations under the License.
80 | mnd:
81 | checks:
82 | - case
83 | - condition
84 | - return
85 | revive:
86 | rules:
87 | - name: var-naming
88 | arguments:
89 | - [] # allow list
90 | - [] # block list
91 | - - skip-package-name-checks: true # options
92 | tagliatelle:
93 | case:
94 | rules:
95 | yaml: camel
96 | use-field-name: true
97 | testifylint:
98 | disable:
99 | - require-error
100 | go-require:
101 | ignore-http-handlers: true
102 | unparam:
103 | check-exported: false
104 | usetesting:
105 | context-background: true
106 | context-todo: true
107 | os-chdir: true
108 | os-mkdir-temp: true
109 | os-setenv: true
110 | os-temp-dir: true
111 | os-create-temp: true
112 | exclusions:
113 | generated: lax
114 | presets:
115 | - comments
116 | - common-false-positives
117 | - legacy
118 | - std-error-handling
119 | rules:
120 | - linters:
121 | - errcheck
122 | - goconst
123 | - gocyclo
124 | - gosec
125 | - tagliatelle
126 | path: _test\.go
127 | paths:
128 | - third_party$
129 | - builtin$
130 | - examples$
131 | formatters:
132 | enable:
133 | - gofmt
134 | - goimports
135 | settings:
136 | gofmt:
137 | simplify: true
138 | goimports:
139 | local-prefixes:
140 | - github.com/mia-platform/mlp
141 | exclusions:
142 | generated: lax
143 | paths:
144 | - third_party$
145 | - builtin$
146 | - examples$
147 | - zz_generated
148 |
--------------------------------------------------------------------------------
/pkg/extensions/externalsecretpoller.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package extensions
17 |
18 | import (
19 | extsecv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
20 | "github.com/mia-platform/jpl/pkg/poller"
21 | corev1 "k8s.io/api/core/v1"
22 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
23 | "k8s.io/apimachinery/pkg/runtime"
24 | )
25 |
26 | func ExternalSecretStatusCheckers() poller.CustomStatusCheckers {
27 | return poller.CustomStatusCheckers{
28 | extsecGK: externalSecretStatusChecker,
29 | extSecStoreGK: secretStoreStatusChecker,
30 | }
31 | }
32 |
33 | // externalSecretStatusChecker contains the logic for checking if an ExternalSecret has finished to sync its data
34 | func externalSecretStatusChecker(object *unstructured.Unstructured) (*poller.Result, error) {
35 | externalSecret := new(extsecv1.ExternalSecret)
36 | if err := runtime.DefaultUnstructuredConverter.FromUnstructured(object.Object, externalSecret); err != nil {
37 | return nil, err
38 | }
39 |
40 | for _, condition := range externalSecret.Status.Conditions {
41 | switch condition.Type {
42 | case extsecv1.ExternalSecretReady:
43 | switch condition.Status {
44 | case corev1.ConditionTrue:
45 | return &poller.Result{
46 | Status: poller.StatusCurrent,
47 | Message: condition.Message,
48 | }, nil
49 | case corev1.ConditionFalse:
50 | return &poller.Result{
51 | Status: poller.StatusInProgress,
52 | Message: condition.Message,
53 | }, nil
54 | case corev1.ConditionUnknown:
55 | return &poller.Result{
56 | Status: poller.StatusInProgress,
57 | Message: condition.Message,
58 | }, nil
59 | }
60 | case extsecv1.ExternalSecretDeleted:
61 | if condition.Status == corev1.ConditionTrue {
62 | return &poller.Result{
63 | Status: poller.StatusTerminating,
64 | Message: condition.Message,
65 | }, nil
66 | }
67 | }
68 | }
69 |
70 | return &poller.Result{
71 | Status: poller.StatusInProgress,
72 | Message: "ExternalSecret sync is in progress",
73 | }, nil
74 | }
75 |
76 | // secretStoreStatusChecker contains the logic for checking if an SecretStore has
77 | func secretStoreStatusChecker(object *unstructured.Unstructured) (*poller.Result, error) {
78 | secretStore := new(extsecv1.SecretStore)
79 | if err := runtime.DefaultUnstructuredConverter.FromUnstructured(object.Object, secretStore); err != nil {
80 | return nil, err
81 | }
82 |
83 | for _, condition := range secretStore.Status.Conditions {
84 | if condition.Type != extsecv1.SecretStoreReady {
85 | continue
86 | }
87 | switch condition.Status {
88 | case corev1.ConditionTrue:
89 | return &poller.Result{
90 | Status: poller.StatusCurrent,
91 | Message: condition.Message,
92 | }, nil
93 | case corev1.ConditionFalse:
94 | return &poller.Result{
95 | Status: poller.StatusInProgress,
96 | Message: condition.Message,
97 | }, nil
98 | case corev1.ConditionUnknown:
99 | return &poller.Result{
100 | Status: poller.StatusInProgress,
101 | Message: condition.Message,
102 | }, nil
103 | }
104 | }
105 |
106 | return &poller.Result{
107 | Status: poller.StatusInProgress,
108 | Message: "SecretStore is in progress",
109 | }, nil
110 | }
111 |
--------------------------------------------------------------------------------
/tests/e2e/main_test.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | //go:build conformance
17 |
18 | //nolint:thelper
19 | package e2e
20 |
21 | import (
22 | "context"
23 | "fmt"
24 | "os"
25 | "path/filepath"
26 | "strings"
27 | "testing"
28 |
29 | "sigs.k8s.io/e2e-framework/pkg/env"
30 | "sigs.k8s.io/e2e-framework/pkg/envconf"
31 | "sigs.k8s.io/e2e-framework/pkg/envfuncs"
32 | "sigs.k8s.io/e2e-framework/support/kind"
33 | )
34 |
35 | type namespaceCtxKey string
36 |
37 | var (
38 | testenv env.Environment
39 | )
40 |
41 | func TestMain(m *testing.M) {
42 | testenv = env.New()
43 |
44 | // Specifying a run ID so that multiple runs wouldn't collide.
45 | runID := envconf.RandomName("ns", 4)
46 |
47 | kindClusterName := "mlp-e2e-tests"
48 | kindImageName := "kindest/node:v1.32.5@sha256:e3b2327e3a5ab8c76f5ece68936e4cafaa82edf58486b769727ab0b3b97a5b0d"
49 | if nameFromEnv, found := os.LookupEnv("KIND_NODE_IMAGE"); found {
50 | kindImageName = nameFromEnv
51 | }
52 |
53 | imageOpts := kind.WithImage(kindImageName)
54 |
55 | kindConfigPath := filepath.Join("testdata", "kind.yaml")
56 | crdsPath := filepath.Join("testdata", "crds")
57 |
58 | // Use pre-defined environment funcs to create a kind cluster prior to test run
59 | testenv.Setup(
60 | envfuncs.CreateClusterWithConfig(kind.NewProvider(), kindClusterName, kindConfigPath, imageOpts),
61 | envfuncs.SetupCRDs(crdsPath, "*"),
62 | )
63 |
64 | testenv.BeforeEachTest(func(ctx context.Context, cfg *envconf.Config, t *testing.T) (context.Context, error) {
65 | return createNSForTest(ctx, t, cfg, runID)
66 | })
67 |
68 | testenv.AfterEachTest(func(ctx context.Context, cfg *envconf.Config, t *testing.T) (context.Context, error) {
69 | return deleteNSForTest(ctx, t, cfg)
70 | })
71 |
72 | // Use pre-defined environment funcs to teardown kind cluster after tests
73 | testenv.Finish(
74 | // envfuncs.ExportClusterLogs(kindClusterName, "logs"),
75 | envfuncs.TeardownCRDs(crdsPath, "*"),
76 | // envfuncs.DestroyCluster(kindClusterName),
77 | )
78 |
79 | // launch package tests
80 | os.Exit(testenv.Run(m))
81 | }
82 |
83 | // CreateNSForTest creates a random namespace with the runID as a prefix. It is stored in the context
84 | // so that the deleteNSForTest routine can look it up and delete it.
85 | func createNSForTest(ctx context.Context, t *testing.T, cfg *envconf.Config, runID string) (context.Context, error) {
86 | ns := envconf.RandomName(runID, 10)
87 | t.Logf("Creating NS %q for test %q", ns, t.Name())
88 | ctx = context.WithValue(ctx, getNamespaceKey(t), ns)
89 |
90 | return envfuncs.CreateNamespace(ns)(ctx, cfg)
91 | }
92 |
93 | // DeleteNSForTest looks up the namespace corresponding to the given test and deletes it.
94 | func deleteNSForTest(ctx context.Context, t *testing.T, cfg *envconf.Config) (context.Context, error) {
95 | ns := fmt.Sprint(ctx.Value(getNamespaceKey(t)))
96 | t.Logf("Deleting NS %q for test %q", ns, t.Name())
97 | return envfuncs.DeleteNamespace(ns)(ctx, cfg)
98 | }
99 |
100 | // GetNamespaceKey returns the context key for a given test
101 | func getNamespaceKey(t *testing.T) namespaceCtxKey {
102 | // When we pass t.Name() from inside an `assess` step, the name is in the form TestName/Features/Assess
103 | if strings.Contains(t.Name(), "/") {
104 | return namespaceCtxKey(strings.Split(t.Name(), "/")[0])
105 | }
106 |
107 | // When pass t.Name() from inside a `testenv.BeforeEachTest` function, the name is just TestName
108 | return namespaceCtxKey(t.Name())
109 | }
110 |
--------------------------------------------------------------------------------
/docs/30_generate.md:
--------------------------------------------------------------------------------
1 | # Generation Configuration
2 |
3 | The `generate` command is used to generate secrets and configmaps from a configuration `.yaml`, interpolating it
4 | with environment variables when necessary and saves the generated files in a specified directory.
5 |
6 | The configuration file supports environment variable interpolation following the regular expression `{{[A-Z0-9_]+}}`.
7 | The interpolation works in the same way described in the [interpolate](./50_interpolate.md) guide.
8 | The file has a `secrets` section where the keys `tls`,`docker`, and`data` are mutually exclusive and a
9 | `config-maps` section where the only section supported is `data`.
10 |
11 | An configuration file example can be like this:
12 |
13 | ```yaml
14 | secrets:
15 | - name: tls-secret
16 | when: once
17 | tls:
18 | cert:
19 | from: literal
20 | value: value
21 | key:
22 | from: file
23 | file: /path/to/file
24 | value: value
25 | - name: docker-pull-secret
26 | when: always
27 | docker:
28 | username: username
29 | password: password
30 | email: emal@example.com
31 | server: example.com
32 | - name: secret-name
33 | when: always
34 | data:
35 | - from: file
36 | file: ./path/to/file
37 | key: key
38 | config-maps:
39 | - name: config-map-name
40 | when: always
41 | data:
42 | - from: literal
43 | key: key
44 | value: value
45 | ```
46 |
47 | ## Details
48 |
49 | We will going more in depth on the meaning and possible values of the various sections of the configuration file.
50 |
51 | ## `secrets` and `config-maps`
52 |
53 | These sections are the more obvious ones, and they are used to indicate what type of Kubernetes resource you want to
54 | generate.
55 | `secrets` will generate one or more `Secret` resource and `config-maps` will generate one or more `ConfigMap`.
56 |
57 | ## `when`
58 |
59 | The `when` option will accept only the value of `once` and `always`. Omitting the key or setting to another value
60 | will fallback on the `always`value.
61 |
62 | This key is used during apply time, and if set to `once` will generate a configuration with a particular annotation
63 | that will apply the generated file only if a resources of the same kind, name and namespace is not already present
64 | on the remote Kubernetes cluster.
65 | This option can be useful for generating resources with placeholder values that will be updated from other tools but
66 | are necessary for the correct rollout of other resources that might depends of them. A tipical application
67 | can be a `Secret` resource of `tls` type that contains a tls that will be handled by an external tools for keeping it
68 | updated before the end of the valid timestamp.
69 |
70 | ## `data`
71 |
72 | The `data` block is the only valid block for a `ConfigMap` resource and one of the valid one for the `Secret`
73 | resource.
74 | This block is used for setting one or more key in the resource using literal values or files.
75 |
76 | The `from` key can be one of `literal` or `file` and is used to select where to find the value used to popolate `key`
77 | in the final resource. If the value is `literal` the `value` key is used and its values is used; in the other case the
78 | `file` key is used as path to find the file to load for the value. The path can be absolute or relative to the folder
79 | where the command will be launched.
80 |
81 | ## `docker`
82 |
83 | The `docker` block is a special block valid only for `secrets` and will generate a Kubernete `Secret` of type
84 | `kubernetes.io/dockerconfigjson` that can be used as an `ImagePullSecret` for setting authorization to one or more
85 | docker registries.
86 | The four keys `username`, `password`, `email` and `server` are used for generate the json configuration and must
87 | contains a valid authorized user for the given `server` url of a remote repository.
88 |
89 | ## `tls`
90 |
91 | The `tls` block is the last supported type of `secrets` and will generate a Kubernetes `Secret` of type
92 | `kubernetes.io/tls`. This secret is usually used to set a certificate/private key pair for a TLS connection like
93 | exposing an HTTPS connection for an `Ingress` the keys of the object are always `tls.crt` for the certificate and
94 | `tls.key` for the private key.
95 |
96 | The values can be passed by file or directly in the configuration, but we highly recommend to use files for avoiding
97 | to accidentally leak sensible data.
98 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/mia-platform/mlp/v2
2 |
3 | go 1.25
4 |
5 | toolchain go1.25.4
6 |
7 | require (
8 | github.com/MakeNowJust/heredoc/v2 v2.0.1
9 | github.com/blang/semver/v4 v4.0.0
10 | github.com/distribution/reference v0.6.0
11 | github.com/external-secrets/external-secrets v0.19.2
12 | github.com/go-logr/logr v1.4.3
13 | github.com/go-logr/stdr v1.2.2
14 | github.com/mia-platform/jpl v0.9.0
15 | github.com/spf13/cobra v1.10.1
16 | github.com/spf13/pflag v1.0.10
17 | github.com/stretchr/testify v1.11.1
18 | k8s.io/api v0.33.4
19 | k8s.io/apimachinery v0.33.4
20 | k8s.io/cli-runtime v0.33.4
21 | k8s.io/client-go v0.33.4
22 | k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d
23 | sigs.k8s.io/e2e-framework v0.6.0
24 | sigs.k8s.io/kustomize/api v0.20.1
25 | sigs.k8s.io/kustomize/kyaml v0.20.1
26 | sigs.k8s.io/yaml v1.5.0
27 | )
28 |
29 | require (
30 | github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
31 | github.com/beorn7/perks v1.0.1 // indirect
32 | github.com/cespare/xxhash/v2 v2.3.0 // indirect
33 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
34 | github.com/emicklei/go-restful/v3 v3.12.2 // indirect
35 | github.com/evanphx/json-patch/v5 v5.9.11 // indirect
36 | github.com/fsnotify/fsnotify v1.9.0 // indirect
37 | github.com/fxamacker/cbor/v2 v2.9.0 // indirect
38 | github.com/go-errors/errors v1.4.2 // indirect
39 | github.com/go-openapi/jsonpointer v0.21.1 // indirect
40 | github.com/go-openapi/jsonreference v0.21.0 // indirect
41 | github.com/go-openapi/swag v0.23.1 // indirect
42 | github.com/gogo/protobuf v1.3.2 // indirect
43 | github.com/google/btree v1.1.3 // indirect
44 | github.com/google/gnostic-models v0.7.0 // indirect
45 | github.com/google/go-cmp v0.7.0 // indirect
46 | github.com/google/uuid v1.6.0 // indirect
47 | github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
48 | github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
49 | github.com/inconshreveable/mousetrap v1.1.0 // indirect
50 | github.com/josharian/intern v1.0.0 // indirect
51 | github.com/json-iterator/go v1.1.12 // indirect
52 | github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
53 | github.com/mailru/easyjson v0.9.0 // indirect
54 | github.com/moby/spdystream v0.5.0 // indirect
55 | github.com/moby/term v0.5.0 // indirect
56 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
57 | github.com/modern-go/reflect2 v1.0.2 // indirect
58 | github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
59 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
60 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
61 | github.com/opencontainers/go-digest v1.0.0 // indirect
62 | github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
63 | github.com/pkg/errors v0.9.1 // indirect
64 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
65 | github.com/prometheus/client_golang v1.22.0 // indirect
66 | github.com/prometheus/client_model v0.6.2 // indirect
67 | github.com/prometheus/common v0.65.0 // indirect
68 | github.com/prometheus/procfs v0.17.0 // indirect
69 | github.com/vladimirvivien/gexe v0.4.1 // indirect
70 | github.com/x448/float16 v0.8.4 // indirect
71 | github.com/xlab/treeprint v1.2.0 // indirect
72 | go.opentelemetry.io/otel v1.37.0 // indirect
73 | go.opentelemetry.io/otel/trace v1.37.0 // indirect
74 | go.yaml.in/yaml/v2 v2.4.2 // indirect
75 | go.yaml.in/yaml/v3 v3.0.4 // indirect
76 | golang.org/x/net v0.42.0 // indirect
77 | golang.org/x/oauth2 v0.30.0 // indirect
78 | golang.org/x/sync v0.16.0 // indirect
79 | golang.org/x/sys v0.34.0 // indirect
80 | golang.org/x/term v0.33.0 // indirect
81 | golang.org/x/text v0.27.0 // indirect
82 | golang.org/x/time v0.12.0 // indirect
83 | gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
84 | google.golang.org/protobuf v1.36.6 // indirect
85 | gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
86 | gopkg.in/inf.v0 v0.9.1 // indirect
87 | gopkg.in/yaml.v3 v3.0.1 // indirect
88 | k8s.io/apiextensions-apiserver v0.33.4 // indirect
89 | k8s.io/component-base v0.33.4 // indirect
90 | k8s.io/klog/v2 v2.130.1 // indirect
91 | k8s.io/kube-openapi v0.0.0-20250701173324-9bd5c66d9911 // indirect
92 | sigs.k8s.io/controller-runtime v0.21.0 // indirect
93 | sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
94 | sigs.k8s.io/randfill v1.0.0 // indirect
95 | sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
96 | )
97 |
--------------------------------------------------------------------------------
/pkg/cmd/root.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package cmd
17 |
18 | import (
19 | "context"
20 | "fmt"
21 | "log"
22 | "runtime"
23 |
24 | "github.com/MakeNowJust/heredoc/v2"
25 | "github.com/go-logr/logr"
26 | "github.com/go-logr/stdr"
27 | "github.com/spf13/cobra"
28 | "github.com/spf13/pflag"
29 | "k8s.io/cli-runtime/pkg/genericclioptions"
30 |
31 | "github.com/mia-platform/mlp/v2/pkg/cmd/deploy"
32 | "github.com/mia-platform/mlp/v2/pkg/cmd/generate"
33 | "github.com/mia-platform/mlp/v2/pkg/cmd/hydrate"
34 | "github.com/mia-platform/mlp/v2/pkg/cmd/interpolate"
35 | "github.com/mia-platform/mlp/v2/pkg/cmd/kustomize"
36 | )
37 |
38 | var (
39 | // Version is dynamically set by the ci or overridden by the Makefile.
40 | Version = "DEV"
41 | // BuildDate is dynamically set at build time by the cli or overridden in the Makefile.
42 | BuildDate = "" // YYYY-MM-DD
43 | )
44 |
45 | const (
46 | cmdShort = "mlp can deploy a Mia-Platform application on a Kubernetes cluster"
47 | cmdLong = `mlp can deploy a Mia-Platform application on a Kubernetes cluster.
48 |
49 | Handle resource files generated by Mia-Platform Console for correct deployment
50 | on Kubernetes. It provides additional capabilities on top of traditional
51 | "kubectl apply" like resource inventory, resource generation and force redeploy.`
52 | cmdExamples = `$ mlp interpolate
53 | $ mlp deploy -f ./folder
54 | $ mlp generate --env-prefix DEV_`
55 |
56 | versionCmdShort = "Show mlp version"
57 | versionCmdLong = "Show mlp version"
58 |
59 | verboseFlagName = "verbose"
60 | verboseFlagShortName = "v"
61 | verboseUsage = "setting logging verbosity; use number between 0 and 10"
62 | )
63 |
64 | type Flags struct {
65 | verbosity int
66 | }
67 |
68 | func NewRootCommand() *cobra.Command {
69 | flags := &Flags{}
70 | cmd := &cobra.Command{
71 | Use: "mlp",
72 |
73 | Short: heredoc.Doc(cmdShort),
74 | Long: heredoc.Doc(cmdLong),
75 | Example: heredoc.Doc(cmdExamples),
76 |
77 | SilenceErrors: true,
78 | Version: versionString(),
79 |
80 | Args: cobra.NoArgs,
81 | ValidArgsFunction: cobra.NoFileCompletions,
82 | PersistentPreRun: func(*cobra.Command, []string) {
83 | stdr.SetVerbosity(flags.verbosity)
84 | },
85 | }
86 |
87 | flags.AddFlags(cmd.PersistentFlags())
88 |
89 | logger := stdr.New(log.Default())
90 | cmd.SetContext(logr.NewContext(context.Background(), logger))
91 |
92 | cmd.AddCommand(
93 | deploy.NewCommand(genericclioptions.NewConfigFlags(true)),
94 | generate.NewCommand(),
95 | hydrate.NewCommand(),
96 | interpolate.NewCommand(),
97 | kustomize.NewCommand(),
98 | versionCommand(),
99 | )
100 |
101 | return cmd
102 | }
103 |
104 | // AddFlags set the connection between Flags property to command line flags
105 | func (f *Flags) AddFlags(flags *pflag.FlagSet) {
106 | flags.IntVarP(&f.verbosity, verboseFlagName, verboseFlagShortName, f.verbosity, verboseUsage)
107 | }
108 |
109 | // versionCommand return the command for printing the version string, like --version flag
110 | func versionCommand() *cobra.Command {
111 | cmd := &cobra.Command{
112 | Use: "version",
113 |
114 | Short: heredoc.Doc(versionCmdShort),
115 | Long: heredoc.Doc(versionCmdLong),
116 |
117 | SilenceErrors: true,
118 | Args: cobra.NoArgs,
119 | ValidArgsFunction: cobra.NoFileCompletions,
120 | Run: func(cobra *cobra.Command, _ []string) {
121 | cobra.Println(versionString())
122 | },
123 | }
124 |
125 | return cmd
126 | }
127 |
128 | // versionString format a complete version string to output to the user
129 | func versionString() string {
130 | version := Version
131 |
132 | if BuildDate != "" {
133 | version = fmt.Sprintf("%s (%s)", version, BuildDate)
134 | }
135 |
136 | return fmt.Sprintf("%s, Go Version: %s", version, runtime.Version())
137 | }
138 |
--------------------------------------------------------------------------------
/pkg/cmd/kustomize/kustomize.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package kustomize
17 |
18 | import (
19 | "context"
20 | "errors"
21 | "io"
22 |
23 | "github.com/MakeNowJust/heredoc/v2"
24 | "github.com/go-logr/logr"
25 | "github.com/spf13/cobra"
26 | "github.com/spf13/pflag"
27 | "sigs.k8s.io/kustomize/api/krusty"
28 | "sigs.k8s.io/kustomize/kyaml/filesys"
29 | )
30 |
31 | const (
32 | cmdUsage = "kustomize DIR"
33 | cmdShort = "Build a kustomization target from a directory"
34 | cmdLong = `Build a set of KRM resources using a 'kustomization.yaml' file.
35 | The DIR argument must be a path to a directory containing a
36 | 'kustomization.yaml' file.
37 | If DIR is omitted, '.' is assumed.
38 | `
39 | cmdExamples = `# Build the current working directory
40 | mlp kustomize
41 |
42 | # Build a specific path
43 | mlp kustomize /home/config/project
44 |
45 | # Save output to a file
46 | mlp kustomize --output /home/config/build-results.yaml
47 | `
48 |
49 | outputFlagName = "output"
50 | outputFlagShort = "o"
51 | outputFlagUsage = "If specified, write output to the file at this path"
52 | outputIsADirectoryError = "output path is a directory instead of a file"
53 | )
54 |
55 | // Flags contains all the flags for the `kustomize` command. They will be converted to Options
56 | // that contains all runtime options for the command.
57 | type Flags struct {
58 | outputPath string
59 | }
60 |
61 | // Options have the data required to perform the kustomize operation
62 | type Options struct {
63 | inputPath string
64 | outputPath string
65 | fSys filesys.FileSystem
66 | writer io.Writer
67 | }
68 |
69 | // NewCommand return the command for build a kustomization target from a directory
70 | func NewCommand() *cobra.Command {
71 | flags := &Flags{}
72 | cmd := &cobra.Command{
73 | Use: cmdUsage,
74 | Short: heredoc.Doc(cmdShort),
75 | Long: heredoc.Doc(cmdLong),
76 | Example: heredoc.Doc(cmdExamples),
77 |
78 | Args: cobra.RangeArgs(0, 1),
79 |
80 | Run: func(cmd *cobra.Command, args []string) {
81 | o, err := flags.ToOptions(args, filesys.MakeFsOnDisk(), cmd.OutOrStderr())
82 | cobra.CheckErr(err)
83 | cobra.CheckErr(o.Run(cmd.Context()))
84 | },
85 | }
86 |
87 | flags.AddFlags(cmd.Flags())
88 |
89 | return cmd
90 | }
91 |
92 | // AddFlags set the connection between Flags property to command line flags
93 | func (f *Flags) AddFlags(set *pflag.FlagSet) {
94 | set.StringVarP(&f.outputPath, outputFlagName, outputFlagShort, "", outputFlagUsage)
95 | }
96 |
97 | // ToOptions transform the command flags in command runtime arguments
98 | func (f *Flags) ToOptions(args []string, fSys filesys.FileSystem, writer io.Writer) (*Options, error) {
99 | var inputPath string
100 | switch len(args) {
101 | case 0:
102 | inputPath = filesys.SelfDir
103 | default:
104 | inputPath = args[0]
105 | }
106 |
107 | if len(f.outputPath) > 0 && fSys.IsDir(f.outputPath) {
108 | return nil, errors.New(outputIsADirectoryError)
109 | }
110 |
111 | return &Options{
112 | inputPath: inputPath,
113 | outputPath: f.outputPath,
114 | fSys: fSys,
115 | writer: writer,
116 | }, nil
117 | }
118 |
119 | // Run execute the kustomize command
120 | func (o *Options) Run(ctx context.Context) error {
121 | logger := logr.FromContextOrDiscard(ctx)
122 |
123 | logger.V(5).Info("reading kustomize files", "path", o.inputPath)
124 | kustomizer := krusty.MakeKustomizer(krusty.MakeDefaultOptions())
125 | resourceMap, err := kustomizer.Run(o.fSys, o.inputPath)
126 | if err != nil {
127 | return err
128 | }
129 |
130 | yaml, err := resourceMap.AsYaml()
131 | if err != nil {
132 | return err
133 | }
134 |
135 | if len(o.outputPath) > 0 {
136 | logger.V(5).Info("writing accumulated data", "path", o.outputPath)
137 | return o.fSys.WriteFile(o.outputPath, yaml)
138 | }
139 |
140 | _, err = o.writer.Write(yaml)
141 | return err
142 | }
143 |
--------------------------------------------------------------------------------
/pkg/cmd/kustomize/kustomize_test.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package kustomize
17 |
18 | import (
19 | "bytes"
20 | "errors"
21 | "path/filepath"
22 | "testing"
23 |
24 | "github.com/stretchr/testify/assert"
25 | "github.com/stretchr/testify/require"
26 | "sigs.k8s.io/kustomize/kyaml/filesys"
27 | )
28 |
29 | func TestCommand(t *testing.T) {
30 | t.Parallel()
31 |
32 | cmd := NewCommand()
33 | assert.NotNil(t, cmd)
34 |
35 | buffer := new(bytes.Buffer)
36 | cmd.SetArgs([]string{"testdata"})
37 | cmd.SetOut(buffer)
38 | assert.NoError(t, cmd.Execute())
39 | t.Log(buffer.String())
40 | buffer.Reset()
41 | }
42 |
43 | func TestToOptions(t *testing.T) {
44 | t.Parallel()
45 |
46 | testPath := filepath.Join("test", "path")
47 | fSys := filesys.MakeEmptyDirInMemory()
48 | require.NoError(t, fSys.MkdirAll(testPath))
49 | buffer := new(bytes.Buffer)
50 | tests := map[string]struct {
51 | flags *Flags
52 | args []string
53 | expectedOptions *Options
54 | expectedError string
55 | }{
56 | "create options from flags": {
57 | flags: &Flags{
58 | outputPath: filepath.Join(testPath, "file.txt"),
59 | },
60 | args: []string{"input"},
61 | expectedOptions: &Options{
62 | outputPath: filepath.Join(testPath, "file.txt"),
63 | inputPath: "input",
64 | fSys: fSys,
65 | writer: buffer,
66 | },
67 | },
68 | "create options without args": {
69 | flags: &Flags{
70 | outputPath: filepath.Join(testPath, "file.txt"),
71 | },
72 | expectedOptions: &Options{
73 | outputPath: filepath.Join(testPath, "file.txt"),
74 | inputPath: filesys.SelfDir,
75 | fSys: fSys,
76 | writer: buffer,
77 | },
78 | },
79 | "output path is a dir": {
80 | flags: &Flags{
81 | outputPath: testPath,
82 | },
83 | expectedError: outputIsADirectoryError,
84 | },
85 | }
86 |
87 | for name, test := range tests {
88 | t.Run(name, func(t *testing.T) {
89 | t.Parallel()
90 |
91 | o, err := test.flags.ToOptions(test.args, fSys, buffer)
92 | switch len(test.expectedError) {
93 | case 0:
94 | assert.NoError(t, err)
95 | default:
96 | assert.ErrorContains(t, err, test.expectedError)
97 | }
98 |
99 | assert.Equal(t, test.expectedOptions, o)
100 | })
101 | }
102 | }
103 |
104 | func TestRun(t *testing.T) {
105 | t.Parallel()
106 |
107 | tests := map[string]struct {
108 | options *Options
109 | expectedOutput string
110 | expectedError string
111 | }{
112 | "run correctly": {
113 | options: &Options{
114 | inputPath: "testdata",
115 | outputPath: "",
116 | fSys: filesys.MakeFsOnDisk(),
117 | writer: new(bytes.Buffer),
118 | },
119 | },
120 | "run saving on file": {
121 | options: &Options{
122 | inputPath: "testdata",
123 | outputPath: filepath.Join(t.TempDir(), "output.yaml"),
124 | fSys: filesys.MakeFsOnDisk(),
125 | },
126 | },
127 | "error reading files": {
128 | options: &Options{
129 | inputPath: "testdata",
130 | outputPath: "",
131 | fSys: filesys.MakeEmptyDirInMemory(),
132 | },
133 | expectedError: "not a valid directory",
134 | },
135 | "error during writes": {
136 | options: &Options{
137 | inputPath: "testdata",
138 | outputPath: "",
139 | fSys: filesys.MakeFsOnDisk(),
140 | writer: failWriter{},
141 | },
142 | expectedError: "nope",
143 | },
144 | }
145 |
146 | for name, test := range tests {
147 | t.Run(name, func(t *testing.T) {
148 | t.Parallel()
149 |
150 | err := test.options.Run(t.Context())
151 | switch len(test.expectedError) {
152 | case 0:
153 | assert.NoError(t, err)
154 | default:
155 | assert.ErrorContains(t, err, test.expectedError)
156 | }
157 | })
158 | }
159 | }
160 |
161 | // FailWriter is a writer that always returns an error.
162 | type failWriter struct{}
163 |
164 | // Write implements the Writer interface's Write method and returns an error.
165 | func (failWriter) Write([]byte) (int, error) { return 0, errors.New("nope") }
166 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Copyright Mia srl
2 | # SPDX-License-Identifier: Apache-2.0
3 |
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 |
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | DEBUG_MAKEFILE?=
17 | ifeq ($(DEBUG_MAKEFILE),1)
18 | $(warning ***** executing goal(s) "$(MAKECMDGOALS)")
19 | $(warning ***** $(shell date))
20 | else
21 | # If we're not debugging the Makefile, always hide the commands inside the goals
22 | MAKEFLAGS+= -s
23 | endif
24 |
25 | # It's necessary to set this because some environments don't link sh -> bash.
26 | # Using env is more portable than setting the path directly
27 | SHELL:= /usr/bin/env bash
28 |
29 | .EXPORT_ALL_VARIABLES:
30 |
31 | .SUFFIXES:
32 |
33 | ## Set all variables
34 | ifeq ($(origin PROJECT_DIR),undefined)
35 | PROJECT_DIR:= $(abspath $(shell pwd -P))
36 | endif
37 |
38 | ifeq ($(origin OUTPUT_DIR),undefined)
39 | OUTPUT_DIR:= $(PROJECT_DIR)/bin
40 | endif
41 |
42 | ifeq ($(origin TOOLS_DIR),undefined)
43 | TOOLS_DIR:= $(PROJECT_DIR)/tools
44 | endif
45 |
46 | ifeq ($(origin TOOLS_BIN),undefined)
47 | TOOLS_BIN:= $(TOOLS_DIR)/bin
48 | endif
49 |
50 | # Set here the name of the package you want to build
51 | CMDNAME:= mlp
52 | BUILD_PATH:= .
53 | CONFORMANCE_TEST_PATH:= $(PROJECT_DIR)/tests/e2e
54 | IS_LIBRARY:=
55 |
56 | # enable modules
57 | GO111MODULE:= on
58 | GOOS:= $(shell go env GOOS)
59 | GOARCH:= $(shell go env GOARCH)
60 | GOARM:= $(shell go env GOARM)
61 |
62 | ## Build Variables
63 | GIT_REV:= $(shell git rev-parse --short HEAD 2>/dev/null)
64 | VERSION:= $(shell git describe --tags --exact-match 2>/dev/null || (echo $(GIT_REV) | cut -c1-12))
65 | # insert here the go module where to add the version metadata
66 | VERSION_MODULE_NAME:= github.com/mia-platform/mlp/v2/pkg/cmd
67 |
68 | # supported platforms for container creation, these are a subset of the supported
69 | # platforms of the base image.
70 | # Or if you start from scratch the platforms you want to support in your image
71 | # This link contains the rules on how the strings must be formed https://github.com/containerd/containerd/blob/v1.4.3/platforms/platforms.go#L63
72 | SUPPORTED_PLATFORMS:= linux/386 linux/amd64 linux/arm64 linux/arm/v6 linux/arm/v7
73 | # Default platform for which building the docker image (darwin can run linux images for the same arch)
74 | # as SUPPORTED_PLATFORMS it highly depends on which platform are supported by the base image
75 | DEFAULT_DOCKER_PLATFORM:= linux/$(GOARCH)/$(GOARM)
76 | # List of one or more container registries for tagging the resulting docker images
77 | CONTAINER_REGISTRIES:= docker.io/miaplatform ghcr.io/mia-platform
78 | # The description used on the org.opencontainers.description label of the container
79 | DESCRIPTION:= mlp is a CLI used to interpolate and deploy resource on Kubernetes
80 | # The vendor name used on the org.opencontainers.image.vendor label of the container
81 | VENDOR_NAME:= Mia s.r.l.
82 | # The license used on the org.opencontainers.image.license label of the container
83 | LICENSE:= Apache-2.0
84 | # The documentation url used on the org.opencontainers.image.documentation label of the container
85 | DOCUMENTATION_URL:= https://docs.mia-platform.eu
86 | # The source url used on the org.opencontainers.image.source label of the container
87 | SOURCE_URL:= https://github.com/mia-platform/mlp
88 | BUILDX_CONTEXT?= mlp-build-context
89 |
90 | # Add additional targets that you want to run when calling make without arguments
91 | .PHONY: all
92 | all: lint test
93 |
94 | ## Includes
95 | include tools/make/clean.mk
96 | include tools/make/lint.mk
97 | include tools/make/test.mk
98 | include tools/make/generate.mk
99 | include tools/make/build.mk
100 | include tools/make/container.mk
101 | include tools/make/release.mk
102 |
103 | # Uncomment the correct test suite to run during CI
104 | .PHONY: ci
105 | ci: test-coverage
106 | # ci: test-integration-coverage
107 |
108 | ### Put your custom import, define or goals under here ###
109 |
110 | generate-deps: $(TOOLS_BIN)/deepcopy-gen
111 | $(TOOLS_BIN)/deepcopy-gen: $(TOOLS_DIR)/DEEPCOPY_GEN_VERSION
112 | $(eval DEEPCOPY_GEN_VERSION:= $(shell cat $<))
113 | mkdir -p $(TOOLS_BIN)
114 | $(info Installing deepcopy-gen $(DEEPCOPY_GEN_VERSION) bin in $(TOOLS_BIN))
115 | GOBIN=$(TOOLS_BIN) go install k8s.io/code-generator/cmd/deepcopy-gen@$(DEEPCOPY_GEN_VERSION)
116 |
--------------------------------------------------------------------------------
/pkg/extensions/externalsecretpoller_test.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package extensions
17 |
18 | import (
19 | "path/filepath"
20 | "testing"
21 |
22 | "github.com/mia-platform/jpl/pkg/poller"
23 | jpltesting "github.com/mia-platform/jpl/pkg/testing"
24 | "github.com/stretchr/testify/assert"
25 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
26 | )
27 |
28 | func TestExtendedPoller(t *testing.T) {
29 | t.Parallel()
30 |
31 | customCheckers := ExternalSecretStatusCheckers()
32 | assert.Len(t, customCheckers, 2)
33 | }
34 |
35 | func TestExternalSecretStatusChecker(t *testing.T) {
36 | t.Parallel()
37 |
38 | testdata := filepath.Join("testdata", "custom-pollers")
39 | tests := map[string]struct {
40 | object *unstructured.Unstructured
41 | expectedResult *poller.Result
42 | expectedError string
43 | }{
44 | "resource secret deleted condition is terminating": {
45 | object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "extsec-terminating.yaml")),
46 | expectedResult: &poller.Result{
47 | Status: poller.StatusTerminating,
48 | Message: "custom message",
49 | },
50 | },
51 | "resource with ready condition true is current": {
52 | object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "extsec-ready-true.yaml")),
53 | expectedResult: &poller.Result{
54 | Status: poller.StatusCurrent,
55 | Message: "custom message",
56 | },
57 | },
58 | "resource with ready condition false is in progress": {
59 | object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "extsec-ready-false.yaml")),
60 | expectedResult: &poller.Result{
61 | Status: poller.StatusInProgress,
62 | Message: "custom message",
63 | },
64 | },
65 | "resource without status is in progress": {
66 | object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "extsec-no-status.yaml")),
67 | expectedResult: &poller.Result{
68 | Status: poller.StatusInProgress,
69 | Message: "ExternalSecret sync is in progress",
70 | },
71 | },
72 | }
73 |
74 | for testName, testCase := range tests {
75 | t.Run(testName, func(t *testing.T) {
76 | t.Parallel()
77 |
78 | result, err := externalSecretStatusChecker(testCase.object)
79 | if len(testCase.expectedError) > 0 {
80 | assert.ErrorContains(t, err, testCase.expectedError)
81 | assert.Equal(t, testCase.expectedResult, result)
82 | return
83 | }
84 |
85 | assert.NoError(t, err)
86 | assert.Equal(t, testCase.expectedResult, result)
87 | })
88 | }
89 | }
90 |
91 | func TestSecretStoreStatusChecker(t *testing.T) {
92 | t.Parallel()
93 |
94 | testdata := filepath.Join("testdata", "custom-pollers")
95 | tests := map[string]struct {
96 | object *unstructured.Unstructured
97 | expectedResult *poller.Result
98 | expectedError string
99 | }{
100 | "resource with ready condition true is current": {
101 | object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "secstore-ready-true.yaml")),
102 | expectedResult: &poller.Result{
103 | Status: poller.StatusCurrent,
104 | Message: "custom message",
105 | },
106 | },
107 | "resource with ready condition false is in progress": {
108 | object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "secstore-ready-false.yaml")),
109 | expectedResult: &poller.Result{
110 | Status: poller.StatusInProgress,
111 | Message: "custom message",
112 | },
113 | },
114 | "resource without status is in progress": {
115 | object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "secstore-no-status.yaml")),
116 | expectedResult: &poller.Result{
117 | Status: poller.StatusInProgress,
118 | Message: "SecretStore is in progress",
119 | },
120 | },
121 | }
122 |
123 | for testName, testCase := range tests {
124 | t.Run(testName, func(t *testing.T) {
125 | t.Parallel()
126 |
127 | result, err := secretStoreStatusChecker(testCase.object)
128 | if len(testCase.expectedError) > 0 {
129 | assert.ErrorContains(t, err, testCase.expectedError)
130 | assert.Equal(t, testCase.expectedResult, result)
131 | return
132 | }
133 |
134 | assert.NoError(t, err)
135 | assert.Equal(t, testCase.expectedResult, result)
136 | })
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/pkg/extensions/utils.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package extensions
17 |
18 | import (
19 | "crypto/sha512"
20 | "encoding/hex"
21 | "fmt"
22 | "reflect"
23 |
24 | extsecv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
25 | appsv1 "k8s.io/api/apps/v1"
26 | corev1 "k8s.io/api/core/v1"
27 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
28 | "k8s.io/apimachinery/pkg/runtime"
29 | "k8s.io/apimachinery/pkg/runtime/schema"
30 | "sigs.k8s.io/yaml"
31 | )
32 |
33 | const (
34 | miaPlatformPrefix = "mia-platform.eu/"
35 |
36 | deployFilterAnnotation = miaPlatformPrefix + "deploy"
37 | deployFilterValue = "once"
38 |
39 | deployChecksumAnnotation = miaPlatformPrefix + "deploy-checksum"
40 |
41 | checksumAnnotation = miaPlatformPrefix + "dependencies-checksum"
42 |
43 | DeployAll = "deploy_all"
44 | DeploySmart = "smart_deploy"
45 | )
46 |
47 | var (
48 | configMapGK = corev1.SchemeGroupVersion.WithKind(reflect.TypeOf(corev1.ConfigMap{}).Name()).GroupKind()
49 | secretGK = corev1.SchemeGroupVersion.WithKind(reflect.TypeOf(corev1.Secret{}).Name()).GroupKind()
50 |
51 | extsecGK = extsecv1.SchemeGroupVersion.WithKind(extsecv1.ExtSecretKind).GroupKind()
52 | extSecStoreGK = extsecv1.SchemeGroupVersion.WithKind(extsecv1.SecretStoreKind).GroupKind()
53 |
54 | deployGK = appsv1.SchemeGroupVersion.WithKind(reflect.TypeOf(appsv1.Deployment{}).Name()).GroupKind()
55 | dsGK = appsv1.SchemeGroupVersion.WithKind(reflect.TypeOf(appsv1.DaemonSet{}).Name()).GroupKind()
56 | stsGK = appsv1.SchemeGroupVersion.WithKind(reflect.TypeOf(appsv1.StatefulSet{}).Name()).GroupKind()
57 | podGK = corev1.SchemeGroupVersion.WithKind(reflect.TypeOf(corev1.Pod{}).Name()).GroupKind()
58 | )
59 |
60 | // podFieldsForGroupKind return the pieces of the path for the pod spec and pod annotations for an unstructured
61 | // object described by gvk. This arrays can be used for retrieving information wihout casting the resource.
62 | func podFieldsForGroupKind(gvk schema.GroupVersionKind) ([]string, []string, error) {
63 | podSpecFields := []string{}
64 | annotationsFields := []string{}
65 | var err error
66 | switch gvk.GroupKind() {
67 | case deployGK:
68 | podSpecFields = []string{"spec", "template", "spec"}
69 | annotationsFields = []string{"spec", "template", "metadata", "annotations"}
70 | case dsGK:
71 | podSpecFields = []string{"spec", "template", "spec"}
72 | annotationsFields = []string{"spec", "template", "metadata", "annotations"}
73 | case stsGK:
74 | podSpecFields = []string{"spec", "template", "spec"}
75 | annotationsFields = []string{"spec", "template", "metadata", "annotations"}
76 | case podGK:
77 | podSpecFields = []string{"spec"}
78 | annotationsFields = []string{"metadata", "annotations"}
79 | default:
80 | apiVersion, kind := gvk.ToAPIVersionAndKind()
81 | err = fmt.Errorf("unsupported object type for dependencies mutator: \"%s, %s\"", apiVersion, kind)
82 | }
83 |
84 | return podSpecFields, annotationsFields, err
85 | }
86 |
87 | // podSpecFromUnstructured try to extract a podSpec from obj at fields path
88 | func podSpecFromUnstructured(obj *unstructured.Unstructured, fields []string) (corev1.PodSpec, error) {
89 | var podSpec corev1.PodSpec
90 | unstrPodSpec, _, err := unstructured.NestedMap(obj.Object, fields...)
91 | if err != nil {
92 | return podSpec, err
93 | }
94 |
95 | if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstrPodSpec, &podSpec); err != nil {
96 | return podSpec, err
97 | }
98 |
99 | return podSpec, nil
100 | }
101 |
102 | // annotationsFromUnstructuredFields return the annotation present at fields in the obj or an empty map
103 | func annotationsFromUnstructuredFields(obj *unstructured.Unstructured, fields []string) (map[string]string, error) {
104 | annotations, _, err := unstructured.NestedStringMap(obj.Object, fields...)
105 | if err != nil {
106 | return nil, err
107 | }
108 |
109 | if annotations == nil {
110 | annotations = make(map[string]string)
111 | }
112 |
113 | return annotations, nil
114 | }
115 |
116 | // ChecksumFromData create a Sum512_256 checksum for arbitrary data
117 | func ChecksumFromData(data interface{}) string {
118 | encoded, err := yaml.Marshal(data)
119 | if err != nil {
120 | return ""
121 | }
122 |
123 | shasum := sha512.Sum512_256(encoded)
124 | return hex.EncodeToString(shasum[:])
125 | }
126 |
--------------------------------------------------------------------------------
/tools/make/container.mk:
--------------------------------------------------------------------------------
1 | # Copyright Mia srl
2 | # SPDX-License-Identifier: Apache-2.0
3 |
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 |
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | ##@ Docker Images Goals
17 |
18 | # Force enable buildkit as a build engine
19 | DOCKER_CMD:= DOCKER_BUILDKIT=1 docker
20 | REPO_TAG:= $(shell git describe --tags --exact-match 2>/dev/null || echo latest)
21 | # Making the subst function works with spaces and comas required this hack
22 | COMMA:= ,
23 | EMPTY:=
24 | SPACE:= $(EMPTY) $(EMPTY)
25 | DOCKER_SUPPORTED_PLATFORMS:= $(subst $(SPACE),$(COMMA),$(SUPPORTED_PLATFORMS))
26 | PARSED_TAGS:= $(shell $(TOOLS_DIR)/parse-tags.sh $(REPO_TAG))
27 | IMAGE_TAGS:= $(addprefix --tag , $(foreach REGISTRY, $(CONTAINER_REGISTRIES), $(foreach TAG, $(PARSED_TAGS), $(REGISTRY)/$(CMDNAME):$(TAG))))
28 | CONTAINER_BUILD_DATE:= $(shell date -u "+%Y-%m-%dT%H:%M:%SZ")
29 |
30 | DOCKER_LABELS:= --label "org.opencontainers.image.title=$(CMDNAME)"
31 | DOCKER_LABELS+= --label "org.opencontainers.image.description=$(DESCRIPTION)"
32 | DOCKER_LABELS+= --label "org.opencontainers.image.url=$(SOURCE_URL)"
33 | DOCKER_LABELS+= --label "org.opencontainers.image.source=$(SOURCE_URL)"
34 | DOCKER_LABELS+= --label "org.opencontainers.image.version=$(REPO_TAG)"
35 | DOCKER_LABELS+= --label "org.opencontainers.image.created=$(CONTAINER_BUILD_DATE)"
36 | DOCKER_LABELS+= --label "org.opencontainers.image.revision=$(shell git rev-parse HEAD 2>/dev/null)"
37 | DOCKER_LABELS+= --label "org.opencontainers.image.licenses=$(LICENSE)"
38 | DOCKER_LABELS+= --label "org.opencontainers.image.documentation=$(DOCUMENTATION_URL)"
39 | DOCKER_LABELS+= --label "org.opencontainers.image.vendor=$(VENDOR_NAME)"
40 |
41 | DOCKER_ANNOTATIONS:= --annotation "org.opencontainers.image.title=$(CMDNAME)"
42 | DOCKER_ANNOTATIONS+= --annotation "org.opencontainers.image.description=$(DESCRIPTION)"
43 | DOCKER_ANNOTATIONS+= --annotation "org.opencontainers.image.url=$(SOURCE_URL)"
44 | DOCKER_ANNOTATIONS+= --annotation "org.opencontainers.image.source=$(SOURCE_URL)"
45 | DOCKER_ANNOTATIONS+= --annotation "org.opencontainers.image.version=$(REPO_TAG)"
46 | DOCKER_ANNOTATIONS+= --annotation "org.opencontainers.image.created=$(CONTAINER_BUILD_DATE)"
47 | DOCKER_ANNOTATIONS+= --annotation "org.opencontainers.image.revision=$(shell git rev-parse HEAD 2>/dev/null)"
48 | DOCKER_ANNOTATIONS+= --annotation "org.opencontainers.image.licenses=$(LICENSE)"
49 | DOCKER_ANNOTATIONS+= --annotation "org.opencontainers.image.documentation=$(DOCUMENTATION_URL)"
50 | DOCKER_ANNOTATIONS+= --annotation "org.opencontainers.image.vendor=$(VENDOR_NAME)"
51 |
52 | .PHONY: docker/%/multiarch
53 | docker/%/multiarch:
54 | $(eval ACTION:= $(word 1,$(subst /, , $*)))
55 | $(eval IS_PUSH:= $(filter push,$(ACTION)))
56 | $(eval ADDITIONAL_PARAMETER:= $(if $(IS_PUSH), --push))
57 | $(info Building image for following platforms: $(SUPPORTED_PLATFORMS))
58 | $(DOCKER_CMD) buildx build --platform "$(DOCKER_SUPPORTED_PLATFORMS)" \
59 | --build-arg CMD_NAME=$(CMDNAME) \
60 | --provenance=false \
61 | $(IMAGE_TAGS) \
62 | $(DOCKER_LABELS) \
63 | $(DOCKER_ANNOTATIONS) \
64 | --file ./Dockerfile $(OUTPUT_DIR) $(ADDITIONAL_PARAMETER)
65 |
66 | .PHONY: docker/build/%
67 | docker/build/%:
68 | $(eval OS:= $(word 1,$(subst /, ,$*)))
69 | $(eval ARCH:= $(word 2,$(subst /, ,$*)))
70 | $(eval ARM:= $(word 3,$(subst /, ,$*)))
71 | $(info Building image for $(OS) $(ARCH) $(ARM))
72 | $(DOCKER_CMD) build --platform $* \
73 | --build-arg CMD_NAME=$(CMDNAME) \
74 | $(IMAGE_TAGS) \
75 | $(DOCKER_LABELS) \
76 | $(DOCKER_ANNOTATIONS) \
77 | --file ./Dockerfile $(OUTPUT_DIR)
78 |
79 | .PHONY: docker/setup/multiarch
80 | docker/setup/multiarch:
81 | $(info Setup multiarch emulation...)
82 | docker run --rm --privileged tonistiigi/binfmt:latest --install $(SUPPORTED_PLATFORMS)
83 |
84 | .PHONY: docker/buildx/setup docker/buildx/teardown
85 | docker/buildx/setup:
86 | docker buildx rm $(BUILDX_CONTEXT) 2>/dev/null || :
87 | docker buildx create --use --name $(BUILDX_CONTEXT) --platform "$(DOCKER_SUPPORTED_PLATFORMS)"
88 |
89 | docker/buildx/teardown:
90 | docker buildx rm $(BUILDX_CONTEXT)
91 |
92 | .PHONY: docker-build
93 | docker-build: go/build/$(DEFAULT_DOCKER_PLATFORM) docker/build/$(DEFAULT_DOCKER_PLATFORM)
94 |
95 | .PHONY: docker-setup-multiarch
96 | docker-setup-multiarch: docker/setup/multiarch
97 |
98 | .PHONY: docker-build-multiarch
99 | docker-build-multiarch: build-multiarch docker/buildx/setup docker/build/multiarch docker/buildx/teardown
100 |
101 | .PHONY: ci-docker
102 | ci-docker: docker/push/multiarch
103 |
--------------------------------------------------------------------------------
/pkg/apis/mlp.mia-platform.eu/v1/zz_generated.deepcopy.go:
--------------------------------------------------------------------------------
1 | //go:build !ignore_autogenerated
2 | // +build !ignore_autogenerated
3 |
4 | // Copyright Mia srl
5 | // SPDX-License-Identifier: Apache-2.0
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | // Code generated by deepcopy-gen. DO NOT EDIT.
20 |
21 | package v1
22 |
23 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
24 | func (in *ConfigMapSpec) DeepCopyInto(out *ConfigMapSpec) {
25 | *out = *in
26 | if in.Data != nil {
27 | in, out := &in.Data, &out.Data
28 | *out = make([]Data, len(*in))
29 | copy(*out, *in)
30 | }
31 | return
32 | }
33 |
34 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapSpec.
35 | func (in *ConfigMapSpec) DeepCopy() *ConfigMapSpec {
36 | if in == nil {
37 | return nil
38 | }
39 | out := new(ConfigMapSpec)
40 | in.DeepCopyInto(out)
41 | return out
42 | }
43 |
44 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
45 | func (in *Data) DeepCopyInto(out *Data) {
46 | *out = *in
47 | return
48 | }
49 |
50 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Data.
51 | func (in *Data) DeepCopy() *Data {
52 | if in == nil {
53 | return nil
54 | }
55 | out := new(Data)
56 | in.DeepCopyInto(out)
57 | return out
58 | }
59 |
60 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
61 | func (in *DockerConfig) DeepCopyInto(out *DockerConfig) {
62 | *out = *in
63 | return
64 | }
65 |
66 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerConfig.
67 | func (in *DockerConfig) DeepCopy() *DockerConfig {
68 | if in == nil {
69 | return nil
70 | }
71 | out := new(DockerConfig)
72 | in.DeepCopyInto(out)
73 | return out
74 | }
75 |
76 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
77 | func (in *GenerateConfiguration) DeepCopyInto(out *GenerateConfiguration) {
78 | *out = *in
79 | out.TypeMeta = in.TypeMeta
80 | if in.Secrets != nil {
81 | in, out := &in.Secrets, &out.Secrets
82 | *out = make([]SecretSpec, len(*in))
83 | for i := range *in {
84 | (*in)[i].DeepCopyInto(&(*out)[i])
85 | }
86 | }
87 | if in.ConfigMaps != nil {
88 | in, out := &in.ConfigMaps, &out.ConfigMaps
89 | *out = make([]ConfigMapSpec, len(*in))
90 | for i := range *in {
91 | (*in)[i].DeepCopyInto(&(*out)[i])
92 | }
93 | }
94 | return
95 | }
96 |
97 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenerateConfiguration.
98 | func (in *GenerateConfiguration) DeepCopy() *GenerateConfiguration {
99 | if in == nil {
100 | return nil
101 | }
102 | out := new(GenerateConfiguration)
103 | in.DeepCopyInto(out)
104 | return out
105 | }
106 |
107 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
108 | func (in *SecretSpec) DeepCopyInto(out *SecretSpec) {
109 | *out = *in
110 | if in.TLS != nil {
111 | in, out := &in.TLS, &out.TLS
112 | *out = new(TLS)
113 | (*in).DeepCopyInto(*out)
114 | }
115 | if in.Docker != nil {
116 | in, out := &in.Docker, &out.Docker
117 | *out = new(DockerConfig)
118 | **out = **in
119 | }
120 | if in.Data != nil {
121 | in, out := &in.Data, &out.Data
122 | *out = make([]Data, len(*in))
123 | copy(*out, *in)
124 | }
125 | return
126 | }
127 |
128 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretSpec.
129 | func (in *SecretSpec) DeepCopy() *SecretSpec {
130 | if in == nil {
131 | return nil
132 | }
133 | out := new(SecretSpec)
134 | in.DeepCopyInto(out)
135 | return out
136 | }
137 |
138 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
139 | func (in *TLS) DeepCopyInto(out *TLS) {
140 | *out = *in
141 | if in.Cert != nil {
142 | in, out := &in.Cert, &out.Cert
143 | *out = new(TLSData)
144 | **out = **in
145 | }
146 | if in.Key != nil {
147 | in, out := &in.Key, &out.Key
148 | *out = new(TLSData)
149 | **out = **in
150 | }
151 | return
152 | }
153 |
154 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLS.
155 | func (in *TLS) DeepCopy() *TLS {
156 | if in == nil {
157 | return nil
158 | }
159 | out := new(TLS)
160 | in.DeepCopyInto(out)
161 | return out
162 | }
163 |
164 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
165 | func (in *TLSData) DeepCopyInto(out *TLSData) {
166 | *out = *in
167 | return
168 | }
169 |
170 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSData.
171 | func (in *TLSData) DeepCopy() *TLSData {
172 | if in == nil {
173 | return nil
174 | }
175 | out := new(TLSData)
176 | in.DeepCopyInto(out)
177 | return out
178 | }
179 |
--------------------------------------------------------------------------------
/docs/20_setup.md:
--------------------------------------------------------------------------------
1 | # Setup
2 |
3 | ## Installation
4 |
5 | `mlp` can be installed in different ways, you can choose the one that better fits your needs and the operating system
6 | you are using:
7 |
8 | - [Linux and MacOs](#linux-and-macos)
9 | - [Homebrew](#homebrew)
10 | - [Go](#go)
11 | - [Binary Download](#binary-download)
12 | - [Docker](#docker)
13 | - [Windows (with WSL)](#windows)
14 | - [Shell Autocompletion](#shell-autocompletion)
15 |
16 | ### Linux and MacOs
17 |
18 | #### Homebrew
19 |
20 | If you have [Homebrew] installed on your system `mlp` is only a command away:
21 |
22 | ```sh
23 | brew install mia-platform/tap/mlp
24 | ```
25 |
26 | This method will also automatically setup the shell completions for `bash`, `zsh` and `fish`.
27 |
28 | #### Go
29 |
30 | If you have [Golang] installed with a version >= 1.13 in your system and you have the `$GOPATH`env set, you can
31 | install `mlp` like this:
32 |
33 | ```sh
34 | go install github.com/mia-platform/mlp@v2.5.0
35 | ```
36 |
37 | Or like this if the `install` command is not available
38 |
39 | ```sh
40 | go get -u github.com/mia-platform/mlp@v2.5.0
41 | ```
42 |
43 | #### Binary Download
44 |
45 | You can install `mlp` with the use of `curl` or `wget` and downloading the latest packages available on GitHub
46 | choosing the correct platform and operating system:
47 |
48 | ```sh
49 | curl -fsSL --proto '=https' --tlsv1.2 https://github.com/mia-platform/mlp/releases/download/v2.5.0/mlp-linux-amd64 -o /tmp/mlp
50 | ```
51 |
52 | ```sh
53 | wget -q --https-only --secure-protocol=TLSv1_2 https://github.com/mia-platform/mlp/releases/download/v2.5.0/mlp-linux-amd64 -O /tmp/mlp
54 | ```
55 |
56 | After you have downloaded the file you can validate it against the [checksum] running the command:
57 |
58 | ```sh
59 | sha256sum /tmp/mlp
60 | ```
61 |
62 | After you have validated that the downloaded file is correct, move the binary in your `/usr/local/bin` folder
63 |
64 | ```sh
65 | sudo install -g root -o root /tmp/mlp /usr/local/bin
66 | ```
67 |
68 | #### Docker
69 |
70 | If you want to run the cli in its environment or you want to test the cli you can use the Docker image:
71 |
72 | ```sh
73 | docker run ghcr.io/mia-platform/mlp:v2.5.0
74 | ```
75 |
76 | ### Windows
77 |
78 | `mlp` is not directly compatible with Windows, even if you have Go installed compilation on this OS
79 | is not possible due to current technical restrictions.
80 |
81 | However, it is still possible to use `mlp` with Windows Subsystem for Linux (WSL), as explained here below.
82 |
83 | #### Installation of WSL
84 |
85 | If you don't have WSL on your system, follow the [official guide] to get it.
86 |
87 | Once WSL is installed, to open a Linux bash terminal, press Start+R, enter `bash` in the text box and press OK.
88 |
89 | #### Install `mlp`
90 |
91 | You can now install mlp with any of the methods explained above for Linux,
92 | we suggest the [binary installation](#binary-download) since it's the most straightforward.
93 |
94 | ## Shell Autocompletion
95 |
96 | If you have chosen to use an installation method different from the brew one, you will have to setup the
97 | commands autocompletion for your shell following one of these guides:
98 |
99 | - [`bash`](#bash)
100 | - [`zsh`](#zsh)
101 | - [`fish`](#fish)
102 |
103 | When you update the command remember to relaunch the command for your shell to update the completion definition
104 | and get the latest command and/or flags that has been added.
105 |
106 | ### `bash`
107 |
108 | The `bash` autocompletion needs the [`bash-completion`] package installed on your system.
109 |
110 | **Warning:** for working correctly you need the `bash-completion` V2 that is compatible only with Bash 4.1+,
111 | please be sure to have the correct versions installed on your system before running the command.
112 |
113 | ```sh
114 | mlp completion bash >/usr/local/etc/bash_completion.d/mlp
115 | ```
116 |
117 | After done this you must restart your shell environment or launch `exec bash` for reloading the configurations
118 | and enable the autocompletion.
119 |
120 | ### `zsh`
121 |
122 | For setting up the `zsh` completion, you must enable it. You can use the following command:
123 |
124 | ```sh
125 | echo "autoload -U compinit; compinit" >> ~/.zshrc
126 | ```
127 |
128 | Or use something like [`oh-my-zsh`] that will enable it by default. Once you have done it you can launch the
129 | following command to create the file needed by `zsh`:
130 |
131 | ```sh
132 | mlp completion zsh > /usr/local/share/zsh/site-functions/_mlp
133 | ```
134 |
135 | After done this you must restart your shell environment or launch `exec zsh` for reloading the configurations and
136 | enable the autocompletion.
137 |
138 | ### `fish`
139 |
140 | To enable the autocompletion in `fish` you have to run this command:
141 |
142 | ```sh
143 | mlp completion fish > ~/.config/fish/completions/mlp.fish
144 | ```
145 |
146 | After done this you must restart your shell environment or launch `exec fish` for reloading the configurations and
147 | enable the autocompletion.
148 |
149 | [Homebrew]: https://brew.sh "The Missing Package Manager for macOS (or Linux)"
150 | [Golang]: https://go.dev "Build simple, secure, scalable systems with Go"
151 | [checksum]: https://github.com/mia-platform/mlp/releases/download/v2.5.0/checksums.txt "mlp checksums"
152 | [`bash-completion`]: https://github.com/scop/bash-completion "Programmable completion functions for bash"
153 | [`oh-my-zsh`]: https://ohmyz.sh "Oh My Zsh is a delightful, open source, community-driven
154 | framework for managing your Zsh configuration"
155 | [official guide]: https://learn.microsoft.com/en-us/windows/wsl/install "How to install Linux on Windows with WSL"
156 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, caste, color, religion, or sexual
10 | identity and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the overall
26 | community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or advances of
31 | any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email address,
35 | without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official email address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | [report@mia-platform.eu].
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series of
86 | actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or permanent
93 | ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within the
113 | community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.1, available at
119 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
120 |
121 | Community Impact Guidelines were inspired by
122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
123 |
124 | For answers to common questions about this code of conduct, see the FAQ at
125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
126 | [https://www.contributor-covenant.org/translations][translations].
127 |
128 | [homepage]: https://www.contributor-covenant.org
129 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
130 | [Mozilla CoC]: https://github.com/mozilla/diversity
131 | [FAQ]: https://www.contributor-covenant.org/faq
132 | [translations]: https://www.contributor-covenant.org/translations
133 | [report@mia-platform.eu]: mailto:report@mia-platform.eu
134 |
--------------------------------------------------------------------------------
/pkg/cmd/hydrate/hydrate_test.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package hydrate
17 |
18 | import (
19 | "os"
20 | "path/filepath"
21 | "testing"
22 |
23 | "github.com/stretchr/testify/assert"
24 | "github.com/stretchr/testify/require"
25 | "sigs.k8s.io/kustomize/api/konfig"
26 | "sigs.k8s.io/kustomize/kyaml/filesys"
27 | )
28 |
29 | func TestNewCommand(t *testing.T) {
30 | t.Parallel()
31 |
32 | tmpDir := t.TempDir()
33 | file, err := os.Create(filepath.Join(tmpDir, "Kustomization"))
34 | require.NoError(t, err)
35 | require.NoError(t, file.Close())
36 |
37 | cmd := NewCommand()
38 | assert.NotNil(t, cmd)
39 | cmd.SetArgs([]string{tmpDir})
40 | assert.NoError(t, cmd.Execute())
41 | }
42 |
43 | func TestToOptions(t *testing.T) {
44 | t.Parallel()
45 |
46 | flags := &Flags{}
47 | fSys := filesys.MakeEmptyDirInMemory()
48 | o, err := flags.ToOptions(nil, fSys)
49 | require.NoError(t, err)
50 | assert.Equal(t, &Options{paths: []string{filesys.SelfDir}, fSys: fSys}, o)
51 |
52 | paths := []string{"one", "two"}
53 | o, err = flags.ToOptions(paths, fSys)
54 | require.NoError(t, err)
55 | assert.Equal(t, &Options{paths: paths, fSys: fSys}, o)
56 | }
57 |
58 | func TestRun(t *testing.T) {
59 | t.Parallel()
60 |
61 | expectedConfigurationData := `kind: Kustomization
62 | apiVersion: kustomize.config.k8s.io/v1beta1
63 | metadata:
64 | labels:
65 | app.kubernetes.io/managed-by: mlp
66 | resources:
67 | - Test4.YAML
68 | - test1.yaml
69 | - test2.yml
70 | `
71 | expectedOverlaysData := `kind: Kustomization
72 | apiVersion: kustomize.config.k8s.io/v1beta1
73 | metadata:
74 | labels:
75 | app.kubernetes.io/managed-by: mlp
76 | patches:
77 | - path: path.yaml
78 | target:
79 | group: apps
80 | version: v1
81 | kind: Deployment
82 | - path: test4.patch.yml
83 | - path: config-file.yaml
84 | - path: test2.patch.YAML
85 | resources:
86 | - test2.patch.yaml
87 | - Test1.YAML
88 | - test3.patches.YAML
89 | `
90 |
91 | tests := map[string]struct {
92 | paths []string
93 | expectedError string
94 | }{
95 | "run correctly": {
96 | paths: []string{"configuration", filepath.Join("overlays", "environment")},
97 | },
98 | "wrong path": {
99 | paths: []string{"configurations"},
100 | expectedError: "'configurations' doesn't exist",
101 | },
102 | "missing kustomize file": {
103 | paths: []string{"overlays"},
104 | expectedError: "missing kustomization file",
105 | },
106 | "multiple kustomization files": {
107 | paths: []string{"multiple-files"},
108 | expectedError: "found multiple kustomization file",
109 | },
110 | }
111 |
112 | for name, test := range tests {
113 | t.Run(name, func(t *testing.T) {
114 | t.Parallel()
115 |
116 | fSys := testingInMemoryFSys(t)
117 | options := &Options{
118 | paths: test.paths,
119 | fSys: fSys,
120 | }
121 | err := options.Run(t.Context())
122 | switch len(test.expectedError) {
123 | case 0:
124 | assert.NoError(t, err)
125 | data, err := fSys.ReadFile(filepath.Join("configuration", konfig.DefaultKustomizationFileName()))
126 | require.NoError(t, err)
127 | assert.Equal(t, expectedConfigurationData, string(data))
128 | data, err = fSys.ReadFile(filepath.Join("overlays", "environment", "kustomization.yml"))
129 | require.NoError(t, err)
130 | assert.Equal(t, expectedOverlaysData, string(data))
131 | default:
132 | assert.ErrorContains(t, err, test.expectedError)
133 | }
134 | })
135 | }
136 | }
137 |
138 | func testingInMemoryFSys(t *testing.T) filesys.FileSystem {
139 | t.Helper()
140 | overlaysFolder := filepath.Join("overlays", "environment")
141 | configurationFolder := "configuration"
142 | fSys := filesys.MakeEmptyDirInMemory()
143 |
144 | fSys.Mkdir("multiple-files")
145 | for _, kustomization := range konfig.RecognizedKustomizationFileNames() {
146 | err := fSys.WriteFile(filepath.Join("multiple-files", kustomization), []byte{})
147 | require.NoError(t, err)
148 | }
149 |
150 | fSys.MkdirAll(overlaysFolder)
151 | fSys.Mkdir(configurationFolder)
152 | err := fSys.WriteFile(filepath.Join(configurationFolder, "test1.yaml"), []byte{})
153 | require.NoError(t, err)
154 | err = fSys.WriteFile(filepath.Join(configurationFolder, "test2.yml"), []byte{})
155 | require.NoError(t, err)
156 | err = fSys.WriteFile(filepath.Join(configurationFolder, "test3.json"), []byte{})
157 | require.NoError(t, err)
158 | err = fSys.WriteFile(filepath.Join(configurationFolder, "Test4.YAML"), []byte{})
159 | require.NoError(t, err)
160 | err = fSys.WriteFile(filepath.Join(configurationFolder, konfig.DefaultKustomizationFileName()), []byte{})
161 | require.NoError(t, err)
162 |
163 | err = fSys.WriteFile(filepath.Join(overlaysFolder, "Test1.YAML"), []byte{})
164 | require.NoError(t, err)
165 | err = fSys.WriteFile(filepath.Join(overlaysFolder, "test2.patch.YAML"), []byte{})
166 | require.NoError(t, err)
167 | err = fSys.WriteFile(filepath.Join(overlaysFolder, "test3.patches.YAML"), []byte{})
168 | require.NoError(t, err)
169 | err = fSys.WriteFile(filepath.Join(overlaysFolder, "test4.patch.yml"), []byte{})
170 | require.NoError(t, err)
171 | err = fSys.WriteFile(filepath.Join(overlaysFolder, "config-file.yaml"), []byte{})
172 | require.NoError(t, err)
173 | err = fSys.WriteFile(filepath.Join(overlaysFolder, "kustomization.yml"), []byte(`kind: Kustomization
174 | apiVersion: kustomize.config.k8s.io/v1beta1
175 | resources:
176 | - test2.patch.yaml
177 | patches:
178 | - path: path.yaml
179 | target:
180 | group: apps
181 | version: v1
182 | kind: Deployment
183 | - path: test4.patch.yml
184 | - path: config-file.yaml
185 | `))
186 | require.NoError(t, err)
187 |
188 | return fSys
189 | }
190 |
--------------------------------------------------------------------------------
/pkg/cmd/hydrate/hydrate.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package hydrate
17 |
18 | import (
19 | "cmp"
20 | "context"
21 | "path/filepath"
22 | "regexp"
23 | "slices"
24 | "strings"
25 |
26 | "github.com/MakeNowJust/heredoc/v2"
27 | "github.com/go-logr/logr"
28 | "github.com/spf13/cobra"
29 | "sigs.k8s.io/kustomize/api/types"
30 | "sigs.k8s.io/kustomize/kyaml/filesys"
31 | )
32 |
33 | const (
34 | cmdUsage = "hydrate [DIR]..."
35 | cmdShort = "Generate 'kustomization.yaml' files with content of DIRs"
36 | cmdLong = `Generate 'kustomization.yaml' files with content of DIRs.
37 |
38 | The command will create a new 'kustomization.yaml' file if does not already exists
39 | in the target folders and then will add all the file called '*.patch.yaml' or
40 | '*.patch.yml' as patches and all the rest '*.yaml' or '*.yml' file as resources.
41 | `
42 | cmdExamples = `# hydrate only one folder
43 | mlp hydrate configuration
44 |
45 | # hydrate multiple folders
46 | mlp hydrate configuration overlays/production
47 |
48 | # hydrate current folder
49 | mlp hydrate
50 | `
51 | )
52 |
53 | // Flags contains all the flags for the `hydrate` command. They will be converted to Options
54 | // that contains all runtime options for the command.
55 | type Flags struct{}
56 |
57 | // Options have the data required to perform the hydrate operation
58 | type Options struct {
59 | paths []string
60 | fSys filesys.FileSystem
61 | }
62 |
63 | // NewCommand return the command for generating kustomization files in target folders and populating the resource
64 | // property with the contents alredy present
65 | func NewCommand() *cobra.Command {
66 | flags := &Flags{}
67 | cmd := &cobra.Command{
68 | Use: cmdUsage,
69 | Short: heredoc.Doc(cmdShort),
70 | Long: heredoc.Doc(cmdLong),
71 | Example: heredoc.Doc(cmdExamples),
72 |
73 | Run: func(cmd *cobra.Command, args []string) {
74 | o, err := flags.ToOptions(args, filesys.MakeFsOnDisk())
75 | cobra.CheckErr(err)
76 | cobra.CheckErr(o.Run(cmd.Context()))
77 | },
78 | }
79 |
80 | return cmd
81 | }
82 |
83 | // ToOptions transform the command flags in command runtime arguments
84 | func (*Flags) ToOptions(args []string, fSys filesys.FileSystem) (*Options, error) {
85 | var paths []string
86 | switch len(args) {
87 | case 0:
88 | paths = append(paths, filesys.SelfDir)
89 | default:
90 | paths = args
91 | }
92 |
93 | return &Options{
94 | paths: paths,
95 | fSys: fSys,
96 | }, nil
97 | }
98 |
99 | // Run execute the hydrate command
100 | func (o *Options) Run(ctx context.Context) error {
101 | logger := logr.FromContextOrDiscard(ctx)
102 |
103 | logger.V(5).Info("hydrating files", "paths", strings.Join(o.paths, ", "))
104 | for _, path := range o.paths {
105 | if err := o.hydrateKustomize(ctx, path); err != nil {
106 | return err
107 | }
108 | }
109 |
110 | return nil
111 | }
112 |
113 | // hydrateKustomize will read the folder at path and insert the files as resources or patches based on a regex
114 | func (o *Options) hydrateKustomize(ctx context.Context, path string) error {
115 | logger := logr.FromContextOrDiscard(ctx)
116 |
117 | logger.V(5).Info("hydrating", "path", path)
118 | files, err := o.fSys.ReadDir(path)
119 | if err != nil {
120 | return err
121 | }
122 |
123 | logger.V(8).Info("files found", "paths", strings.Join(files, ", "))
124 | var resources []string
125 | var patches []string
126 | regex := regexp.MustCompile(`(^|\.)patch\.ya?ml$`)
127 | yamlExtensions := []string{".yaml", ".yml"}
128 | for _, file := range files {
129 | normalizedName := strings.ToLower(file)
130 | extension := filepath.Ext(normalizedName)
131 | if !slices.Contains(yamlExtensions, extension) {
132 | continue
133 | }
134 |
135 | switch regex.MatchString(normalizedName) {
136 | case true:
137 | logger.V(10).Info("patch found", "path", file)
138 | patches = append(patches, file)
139 | case false:
140 | logger.V(10).Info("resource found", "path", file)
141 | resources = append(resources, file)
142 | }
143 | }
144 |
145 | slices.SortStableFunc(resources, cmp.Compare)
146 | slices.SortStableFunc(patches, cmp.Compare)
147 | return updateKustomize(ctx, o.fSys, path, resources, patches)
148 | }
149 |
150 | // updateKustomize will read the kustomization file at path and will add resources and patches if not already
151 | // present in the file
152 | func updateKustomize(ctx context.Context, fSys filesys.FileSystem, path string, resources, patches []string) error {
153 | logger := logr.FromContextOrDiscard(ctx)
154 |
155 | kf, err := newKustomizationFile(fSys, path)
156 | if err != nil {
157 | return err
158 | }
159 |
160 | logger.V(3).Info("reading kustomization file", "path", path)
161 | k, err := kf.read()
162 | if err != nil {
163 | return err
164 | }
165 |
166 | alreadyPresentFiles := k.Resources
167 | for _, patch := range k.Patches {
168 | alreadyPresentFiles = append(alreadyPresentFiles, patch.Path)
169 | }
170 |
171 | for _, resource := range resources {
172 | if kf.GetPath() == filepath.Join(path, resource) || slices.Contains(alreadyPresentFiles, resource) {
173 | continue
174 | }
175 | logger.V(8).Info("adding resource", "path", resource)
176 | k.Resources = append(k.Resources, resource)
177 | }
178 |
179 | for _, patch := range patches {
180 | p := types.Patch{
181 | Path: patch,
182 | }
183 |
184 | found := false
185 | for _, path := range alreadyPresentFiles {
186 | if path == patch {
187 | found = true
188 | break
189 | }
190 | }
191 |
192 | if !found {
193 | logger.V(8).Info("adding patch", "path", patch)
194 | k.Patches = append(k.Patches, p)
195 | }
196 | }
197 |
198 | // add managed by annotation to allow empty kustomization files
199 | k.MetaData = &types.ObjectMeta{Labels: map[string]string{"app.kubernetes.io/managed-by": "mlp"}}
200 | logger.V(5).Info("saving kustomization file", "path", path)
201 | return kf.write(k)
202 | }
203 |
--------------------------------------------------------------------------------
/pkg/cmd/deploy/convert_inventory.go:
--------------------------------------------------------------------------------
1 | // Copyright Mia srl
2 | // SPDX-License-Identifier: Apache-2.0
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | package deploy
17 |
18 | import (
19 | "context"
20 | "encoding/json"
21 | "fmt"
22 |
23 | "github.com/mia-platform/jpl/pkg/inventory"
24 | "github.com/mia-platform/jpl/pkg/resource"
25 | "github.com/mia-platform/jpl/pkg/util"
26 | apierrors "k8s.io/apimachinery/pkg/api/errors"
27 | "k8s.io/apimachinery/pkg/api/meta"
28 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
30 | "k8s.io/apimachinery/pkg/runtime/schema"
31 | "k8s.io/apimachinery/pkg/util/sets"
32 | "k8s.io/client-go/kubernetes"
33 | )
34 |
35 | const (
36 | oldInventoryName = "resources-deployed"
37 | oldInventoryKey = "resources"
38 | )
39 |
40 | // Inventory wrap
41 | type Inventory struct {
42 | delegate inventory.Store
43 | namespace string
44 |
45 | compatibilityMode bool
46 |
47 | clientset kubernetes.Interface
48 | mapper meta.RESTMapper
49 | }
50 |
51 | func NewInventory(factory util.ClientFactory, name, namespace, filedManager string) (inventory.Store, error) {
52 | cmInventory, err := inventory.NewConfigMapStore(factory, name, namespace, filedManager)
53 | if err != nil {
54 | return nil, err
55 | }
56 |
57 | clientset, err := factory.KubernetesClientSet()
58 | if err != nil {
59 | return nil, err
60 | }
61 |
62 | mapper, err := factory.ToRESTMapper()
63 | if err != nil {
64 | return nil, err
65 | }
66 |
67 | return &Inventory{
68 | delegate: cmInventory,
69 | namespace: namespace,
70 |
71 | compatibilityMode: true,
72 |
73 | clientset: clientset,
74 | mapper: mapper,
75 | }, nil
76 | }
77 |
78 | func (s *Inventory) Load(ctx context.Context) (sets.Set[resource.ObjectMetadata], error) {
79 | objs, err := s.delegate.Load(ctx)
80 | if err != nil || len(objs) > 0 {
81 | s.compatibilityMode = false
82 | }
83 |
84 | if s.compatibilityMode {
85 | return s.oldInventoryObjects(ctx)
86 | }
87 |
88 | return objs, err
89 | }
90 |
91 | func (s *Inventory) Save(ctx context.Context, dryRun bool) error {
92 | if err := s.delegate.Save(ctx, dryRun); err != nil || !s.compatibilityMode {
93 | return err
94 | }
95 |
96 | return s.deleteOldInventory(ctx, dryRun)
97 | }
98 |
99 | func (s *Inventory) Delete(ctx context.Context, dryRun bool) error {
100 | return s.delegate.Delete(ctx, dryRun)
101 | }
102 |
103 | func (s *Inventory) SetObjects(objects sets.Set[*unstructured.Unstructured]) {
104 | s.delegate.SetObjects(objects)
105 | }
106 |
107 | func (s *Inventory) oldInventoryObjects(ctx context.Context) (sets.Set[resource.ObjectMetadata], error) {
108 | metadataSet := make(sets.Set[resource.ObjectMetadata], 0)
109 | sec, err := s.clientset.CoreV1().Secrets(s.namespace).Get(ctx, oldInventoryName, metav1.GetOptions{})
110 | if err != nil {
111 | if apierrors.IsNotFound(err) {
112 | s.compatibilityMode = false
113 | return metadataSet, nil
114 | }
115 | return nil, fmt.Errorf("failed to find inventory: %w", err)
116 | }
117 |
118 | secretData := sec.Data[oldInventoryKey]
119 | resourceList := make(map[string]*resourceList)
120 |
121 | if err := json.Unmarshal(secretData, &resourceList); err != nil {
122 | var convertError error
123 | resourceList, convertError = convertSecretFormat(secretData)
124 | if convertError != nil {
125 | return nil, fmt.Errorf("error unmarshalling resource map in secret %s: %w in namespace %s. Try to convert format from v0, but fails", oldInventoryName, err, s.namespace)
126 | }
127 | }
128 |
129 | set := make(sets.Set[resource.ObjectMetadata])
130 | for _, list := range resourceList {
131 | gvk := list.Gvk
132 |
133 | mapping, err := s.mapper.RESTMapping(gvk.GroupKind())
134 | if err != nil {
135 | if meta.IsNoMatchError(err) {
136 | continue
137 | }
138 | return nil, err
139 | }
140 |
141 | namespace := ""
142 | if mapping.Scope == meta.RESTScopeNamespace {
143 | namespace = s.namespace
144 | }
145 | for _, name := range list.Resources {
146 | set.Insert(resource.ObjectMetadata{
147 | Name: name,
148 | Namespace: namespace,
149 | Group: gvk.Group,
150 | Kind: gvk.Kind,
151 | })
152 | }
153 | }
154 |
155 | return set, nil
156 | }
157 |
158 | func (s *Inventory) deleteOldInventory(ctx context.Context, dryRun bool) error {
159 | if dryRun {
160 | return nil
161 | }
162 |
163 | s.compatibilityMode = false
164 | propagation := metav1.DeletePropagationBackground
165 | opts := metav1.DeleteOptions{
166 | PropagationPolicy: &propagation,
167 | }
168 |
169 | if err := s.clientset.CoreV1().Secrets(s.namespace).Delete(ctx, oldInventoryName, opts); err != nil && !apierrors.IsNotFound(err) {
170 | return fmt.Errorf("failed to delete inventory: %w", err)
171 | }
172 |
173 | return nil
174 | }
175 |
176 | // resourceList is the base block used to build the secret containing
177 | // the resources deployed in the cluster.
178 | type resourceList struct {
179 | Gvk schema.GroupVersionKind `json:"kind"` //nolint:tagliatelle
180 | Resources []string `json:"resources"`
181 | }
182 |
183 | // oldResourceList is the v0 of old secret base inventory
184 | type oldResourceList struct {
185 | Kind string `json:"kind"`
186 | Mapping schema.GroupVersionResource
187 | Resources []string `json:"resources"`
188 | }
189 |
190 | // Resources secrets created with helper/builer version of mlp is incompatible with newer versions
191 | // this function convert old format in the new one
192 | func convertSecretFormat(resources []byte) (map[string]*resourceList, error) {
193 | oldres := make(map[string]*oldResourceList)
194 | err := json.Unmarshal(resources, &oldres)
195 | if err != nil {
196 | return nil, err
197 | }
198 |
199 | res := make(map[string]*resourceList)
200 |
201 | for k, v := range oldres {
202 | res[k] = &resourceList{
203 | Gvk: schema.GroupVersionKind{
204 | Group: v.Mapping.Group,
205 | Version: v.Mapping.Version,
206 | Kind: k,
207 | },
208 | Resources: v.Resources}
209 | }
210 | return res, nil
211 | }
212 |
--------------------------------------------------------------------------------