diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index b80d64e..581fe0e 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -21,3 +21,38 @@ jobs: packages: write id-token: write security-events: write + + dagger: + name: Dagger + runs-on: ubuntu-latest-large + + permissions: + contents: read + packages: write + id-token: write + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Set up Nix + uses: cachix/install-nix-action@7ac1ec25491415c381d9b62f0657c7a028df52a7 # v24 + with: + extra_nix_config: | + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + + - name: Set up magic Nix cache + uses: DeterminateSystems/magic-nix-cache-action@8a218f9e264e9c3803c9a1ee1c30d8e4ab55be63 # v2 + + - name: Prepare Nix shell + run: nix develop --impure .#ci + + - name: Release + run: nix develop --impure .#ci -c dagger call --registry-user ${{ github.actor }} --registry-password ${{ github.token }} release --version ${{ github.ref_name }} + env: + DAGGER_CLOUD_TOKEN: ${{ secrets.DAGGER_CLOUD_TOKEN }} + + - name: Stop Engine + run: docker stop -t 300 $(docker ps --filter name="dagger-engine-*" -q) + if: always() diff --git a/ci/build.go b/ci/build.go index 0bea12c..119b7d5 100644 --- a/ci/build.go +++ b/ci/build.go @@ -1,6 +1,8 @@ package main import ( + "path/filepath" + "strings" "time" ) @@ -82,3 +84,32 @@ func (m *Build) binary(platform Platform, version Optional[string]) *File { }, }) } + +func (m *Build) HelmChart(version Optional[string]) *File { + chart := m.helmChartDir() + + var opts HelmPackageOpts + + if v, ok := version.Get(); ok { + opts.Version = strings.TrimPrefix(v, "v") + opts.AppVersion = v + } + + return dag.Helm(HelmOpts{Version: helmVersion}).Package(chart, opts) +} + +func (m *Build) helmChartDir() *Directory { + chart := dag.Host().Directory(filepath.Join(root(), "deploy/charts/benthos-openmeter"), HostDirectoryOpts{ + Exclude: []string{"charts"}, // exclude dependencies + }) + + readme := dag.HelmDocs(HelmDocsOpts{Version: helmDocsVersion}).Generate(chart, HelmDocsGenerateOpts{ + Templates: []*File{ + dag.Host().File(filepath.Join(root(), "deploy/charts/template.md")), + dag.Host().File(filepath.Join(root(), "deploy/charts/benthos-openmeter/README.tmpl.md")), + }, + SortValuesOrder: "file", + }) + + return chart.WithFile("README.md", readme) +} diff --git a/ci/dagger.json b/ci/dagger.json index 4dd3053..265507b 100644 --- a/ci/dagger.json +++ b/ci/dagger.json @@ -10,6 +10,8 @@ "dependencies": [ "github.com/sagikazarmark/daggerverse/go@54de323eba85e8d1154751b84a3fb7ab8e2fdd8e", "github.com/sagikazarmark/daggerverse/golangci-lint@fd0f3da52f511b9188a6f5bd44b193e3d614f0e2", + "github.com/sagikazarmark/daggerverse/helm-docs@1e4b4c1ac88f2980870d084f51f5adb33bf227a3", + "github.com/sagikazarmark/daggerverse/helm@4e48ba4471930a7b1f131f029afd4785c8a6f133", "github.com/shykes/daggerverse/supergit@4113b803fcf4ba83b39cd464856af94656197cbf" ] } diff --git a/ci/main.go b/ci/main.go index 531b869..c658b0e 100644 --- a/ci/main.go +++ b/ci/main.go @@ -13,6 +13,9 @@ const ( golangciLintVersion = "v1.54.2" alpineBaseImage = "alpine:3.19.0@sha256:51b67269f354137895d43f3b3d810bfacd3945438e94dc5ac55fdac340352f48" + + helmDocsVersion = "v1.11.3" + helmVersion = "3.13.2" ) const imageRepo = "ghcr.io/openmeterio/benthos-openmeter" @@ -112,6 +115,13 @@ func (m *Ci) Ci(ctx context.Context) error { return nil }) + // TODO: run trivy scan on helm chart + group.Go(func() error { + _, err := m.Build().HelmChart(Opt("0.0.0")).Sync(ctx) + + return err + }) + return group.Wait() } @@ -124,12 +134,12 @@ func (m *Ci) Test() *Container { } func (m *Ci) Lint() *Container { - return dag.GolangciLint(). - Run(GolangciLintRunOpts{ - Version: golangciLintVersion, - GoVersion: goVersion, - Source: m.Source, - Verbose: true, + return dag.GolangciLint(GolangciLintOpts{ + Version: golangciLintVersion, + GoVersion: goVersion, + }). + Run(m.Source, GolangciLintRunOpts{ + Verbose: true, }) } @@ -142,8 +152,41 @@ func (m *Ci) Snapshot(ctx context.Context) error { // Build and publish all release artifacts. func (m *Ci) Release(ctx context.Context, version string) error { - // TODO: refuse to publish release artifacts in a dirty git dir or when there is no tag pointing to the current ref - return m.pushImages(ctx, version, []string{version}) + var group errgroup.Group + + group.Go(func() error { + // Disable pushing images for now + return nil + + // TODO: refuse to publish release artifacts in a dirty git dir or when there is no tag pointing to the current ref + return m.pushImages(ctx, version, []string{version}) + }) + + group.Go(func() error { + username, password := m.RegistryUser, m.RegistryPassword + + if username == "" { + return errors.New("registry user is required to push helm charts to ghcr.io") + } + + if password == nil { + return errors.New("registry password is required to push helm charts to ghcr.io") + } + + chart := m.Build().HelmChart(Opt(version)) + + _, err := dag.Helm(HelmOpts{Version: helmVersion}). + Login("ghcr.io", username, password). + Push(chart, "oci://ghcr.io/openmeterio/helm-charts"). + Sync(ctx) + if err != nil { + return err + } + + return nil + }) + + return group.Wait() } func (m *Ci) pushImages(ctx context.Context, version string, tags []string) error { diff --git a/deploy/charts/Makefile b/deploy/charts/Makefile new file mode 100644 index 0000000..7f2a50f --- /dev/null +++ b/deploy/charts/Makefile @@ -0,0 +1,3 @@ +.PHONY: docs +docs: + helm-docs --log-level trace -s file -c . -t $$PWD/template.md -t README.tmpl.md diff --git a/deploy/charts/benthos-openmeter/.helmignore b/deploy/charts/benthos-openmeter/.helmignore new file mode 100644 index 0000000..b737c53 --- /dev/null +++ b/deploy/charts/benthos-openmeter/.helmignore @@ -0,0 +1,25 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ + +README.tmpl.md diff --git a/deploy/charts/benthos-openmeter/Chart.yaml b/deploy/charts/benthos-openmeter/Chart.yaml new file mode 100644 index 0000000..3e0c132 --- /dev/null +++ b/deploy/charts/benthos-openmeter/Chart.yaml @@ -0,0 +1,15 @@ +apiVersion: v2 +type: application +name: benthos-openmeter +version: 0.0.0 +appVersion: "latest" +description: "A set of plugins and a custom distribution for Benthos to ingest events into OpenMeter." +icon: https://openmeter.io/favicon.svg +keywords: + - metering + - usage + - billing + - aggregation +home: https://openmeter.io +sources: + - https://github.com/openmeterio/benthos-openmeter diff --git a/deploy/charts/benthos-openmeter/README.md b/deploy/charts/benthos-openmeter/README.md new file mode 100644 index 0000000..bb61c73 --- /dev/null +++ b/deploy/charts/benthos-openmeter/README.md @@ -0,0 +1,44 @@ +# benthos-openmeter + +![type: application](https://img.shields.io/badge/type-application-informational?style=flat-square) [![artifact hub](https://img.shields.io/badge/artifact%20hub-benthos--openmeter-informational?style=flat-square)](https://artifacthub.io/packages/helm/openmeter/benthos-openmeter) + +A set of plugins and a custom distribution for Benthos to ingest events into OpenMeter. + +**Homepage:** + +## TL;DR; + +```bash +helm install --generate-name --wait oci://ghcr.io/openmeterio/helm-charts/benthos-openmeter +``` + +to install a specific version: + +```bash +helm install --generate-name --wait oci://ghcr.io/openmeterio/helm-charts/benthos-openmeter --version $VERSION +``` + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| image.repository | string | `"ghcr.io/openmeterio/benthos-openmeter"` | Name of the image repository to pull the container image from. | +| image.pullPolicy | string | `"IfNotPresent"` | [Image pull policy](https://kubernetes.io/docs/concepts/containers/images/#updating-images) for updating already existing images on a node. | +| image.tag | string | `""` | Image tag override for the default value (chart appVersion). | +| imagePullSecrets | list | `[]` | Reference to one or more secrets to be used when [pulling images](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#create-a-pod-that-uses-your-secret) (from private registries). | +| nameOverride | string | `""` | A name in place of the chart name for `app:` labels. | +| fullnameOverride | string | `""` | A name to substitute for the full names of resources. | +| serviceAccount.create | bool | `true` | Specifies whether a service account should be created. | +| serviceAccount.automount | bool | `true` | Automatically mount a ServiceAccount's API credentials? | +| serviceAccount.annotations | object | `{}` | Annotations to add to the service account. | +| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template | +| podAnnotations | object | `{}` | Annotations to be added to pods. | +| podLabels | object | `{}` | Labels to be added to pods. | +| podSecurityContext | object | `{}` | Pod [security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod). See the [API reference](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context) for details. | +| securityContext | object | `{}` | Container [security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container). See the [API reference](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context-1) for details. | +| resources | object | No requests or limits. | Container resource [requests and limits](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). See the [API reference](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#resources) for details. | +| volumes | list | `[]` | Additional volumes on the output Deployment definition. | +| volumeMounts | list | `[]` | Additional volumeMounts on the output Deployment definition. | +| nodeSelector | object | `{}` | [Node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) configuration. | +| tolerations | list | `[]` | [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) for node taints. See the [API reference](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling) for details. | +| affinity | object | `{}` | [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) configuration. See the [API reference](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling) for details. | diff --git a/deploy/charts/benthos-openmeter/README.tmpl.md b/deploy/charts/benthos-openmeter/README.tmpl.md new file mode 100644 index 0000000..fdb5814 --- /dev/null +++ b/deploy/charts/benthos-openmeter/README.tmpl.md @@ -0,0 +1 @@ +{{ template "chart.base" . }} diff --git a/deploy/charts/benthos-openmeter/templates/_helpers.tpl b/deploy/charts/benthos-openmeter/templates/_helpers.tpl new file mode 100644 index 0000000..0d9da5d --- /dev/null +++ b/deploy/charts/benthos-openmeter/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "benthos-openmeter.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "benthos-openmeter.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "benthos-openmeter.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "benthos-openmeter.labels" -}} +helm.sh/chart: {{ include "benthos-openmeter.chart" . }} +{{ include "benthos-openmeter.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "benthos-openmeter.selectorLabels" -}} +app.kubernetes.io/name: {{ include "benthos-openmeter.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "benthos-openmeter.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "benthos-openmeter.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/deploy/charts/benthos-openmeter/templates/deployment.yaml b/deploy/charts/benthos-openmeter/templates/deployment.yaml new file mode 100644 index 0000000..82d879e --- /dev/null +++ b/deploy/charts/benthos-openmeter/templates/deployment.yaml @@ -0,0 +1,58 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "benthos-openmeter.fullname" . }} + labels: + {{- include "benthos-openmeter.labels" . | nindent 4 }} +spec: + replicas: 1 + selector: + matchLabels: + {{- include "benthos-openmeter.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "benthos-openmeter.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "benthos-openmeter.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/deploy/charts/benthos-openmeter/templates/serviceaccount.yaml b/deploy/charts/benthos-openmeter/templates/serviceaccount.yaml new file mode 100644 index 0000000..169d07d --- /dev/null +++ b/deploy/charts/benthos-openmeter/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "benthos-openmeter.serviceAccountName" . }} + labels: + {{- include "benthos-openmeter.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/deploy/charts/benthos-openmeter/values.yaml b/deploy/charts/benthos-openmeter/values.yaml new file mode 100644 index 0000000..3627981 --- /dev/null +++ b/deploy/charts/benthos-openmeter/values.yaml @@ -0,0 +1,94 @@ +# Default values for benthos-openmeter. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +image: + # -- Name of the image repository to pull the container image from. + repository: ghcr.io/openmeterio/benthos-openmeter + + # -- [Image pull policy](https://kubernetes.io/docs/concepts/containers/images/#updating-images) for updating already existing images on a node. + pullPolicy: IfNotPresent + + # -- Image tag override for the default value (chart appVersion). + tag: "" + +# -- Reference to one or more secrets to be used when [pulling images](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#create-a-pod-that-uses-your-secret) (from private registries). +imagePullSecrets: [] + +# -- A name in place of the chart name for `app:` labels. +nameOverride: "" + +# -- A name to substitute for the full names of resources. +fullnameOverride: "" + +serviceAccount: + # -- Specifies whether a service account should be created. + create: true + # -- Automatically mount a ServiceAccount's API credentials? + automount: true + # -- Annotations to add to the service account. + annotations: {} + # -- The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +# -- Annotations to be added to pods. +podAnnotations: {} + +# -- Labels to be added to pods. +podLabels: {} + +# -- Pod [security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod). +# See the [API reference](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context) for details. +podSecurityContext: {} + # fsGroup: 2000 + +# -- Container [security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container). +# See the [API reference](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context-1) for details. +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +# -- Container resource [requests and limits](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). +# See the [API reference](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#resources) for details. +# @default -- No requests or limits. +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + + +# -- Additional volumes on the output Deployment definition. +volumes: [] +# - name: foo +# secret: +# secretName: mysecret +# optional: false + +# -- Additional volumeMounts on the output Deployment definition. +volumeMounts: [] +# - name: foo +# mountPath: "/etc/foo" +# readOnly: true + +# -- [Node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) configuration. +nodeSelector: {} + +# -- [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) for node taints. +# See the [API reference](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling) for details. +tolerations: [] + +# -- [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) configuration. +# See the [API reference](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling) for details. +affinity: {} diff --git a/deploy/charts/template.md b/deploy/charts/template.md new file mode 100644 index 0000000..e627abf --- /dev/null +++ b/deploy/charts/template.md @@ -0,0 +1,57 @@ +{{- define "chart.versionBadge" -}} +![version: {{ .Version }}](https://img.shields.io/badge/version-{{ .Version | replace "-" "--" }}-informational?style=flat-square) +{{- end -}} + +{{- define "chart.typeBadge" -}} +{{- if .Type -}}![type: {{ .Type }}](https://img.shields.io/badge/type-{{ .Type }}-informational?style=flat-square){{- end -}} +{{- end -}} + +{{- define "chart.appVersionBadge" -}} +{{- if .AppVersion -}}![app version: {{ .AppVersion }}](https://img.shields.io/badge/app%20version-{{ .AppVersion | replace "-" "--" }}-informational?style=flat-square){{- end -}} +{{- end -}} + +{{- define "chart.kubeVersionBadge" -}} +{{- if .KubeVersion -}}![kube version: {{ .KubeVersion }}](https://img.shields.io/badge/kube%20version-{{ .KubeVersion | replace "-" "--" }}-informational?style=flat-square){{- end -}} +{{- end -}} + +{{- define "chart.artifactHubBadge" -}} +[![artifact hub](https://img.shields.io/badge/artifact%20hub-{{ .Name | replace "-" "--" }}-informational?style=flat-square)](https://artifacthub.io/packages/helm/openmeter/{{ .Name }}) +{{- end -}} + +{{- define "tldr" -}} +## TL;DR; + +```bash +helm install --generate-name --wait oci://ghcr.io/openmeterio/helm-charts/{{ .Name }} +``` + +to install a specific version: + +```bash +helm install --generate-name --wait oci://ghcr.io/openmeterio/helm-charts/{{ .Name }} --version $VERSION +``` +{{- end -}} + +{{- define "chart.badges" -}} +{{ template "chart.typeBadge" . }} {{ template "chart.kubeVersionBadge" . }} {{ template "chart.artifactHubBadge" . }} +{{- end -}} + +{{- define "chart.baseHead" -}} +{{ template "chart.header" . }} + +{{ template "chart.badges" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "tldr" . }} +{{- end -}} + +{{- define "chart.base" -}} +{{ template "chart.baseHead" . }} + +{{ template "chart.valuesSection" . }} +{{- end -}} diff --git a/flake.nix b/flake.nix index 82e936b..d7487a8 100644 --- a/flake.nix +++ b/flake.nix @@ -53,6 +53,8 @@ dagger benthos + + helm-docs ]; env = {