├── operator ├── .gitignore ├── helm-charts │ └── alloy │ │ ├── templates │ │ ├── NOTES.txt │ │ ├── extra-manifests.yaml │ │ ├── configmap.yaml │ │ ├── serviceaccount.yaml │ │ ├── _config.tpl │ │ ├── networkpolicy.yaml │ │ ├── controllers │ │ │ ├── daemonset.yaml │ │ │ ├── deployment.yaml │ │ │ ├── statefulset.yaml │ │ │ └── _pod.yaml │ │ ├── containers │ │ │ ├── _watch.yaml │ │ │ └── _agent.yaml │ │ ├── servicemonitor.yaml │ │ ├── pdb.yaml │ │ ├── cluster_service.yaml │ │ ├── service.yaml │ │ ├── vpa.yaml │ │ ├── rbac.yaml │ │ ├── ingress.yaml │ │ ├── hpa.yaml │ │ └── _helpers.tpl │ │ ├── ci │ │ ├── create-networkpolicy-values.yaml │ │ ├── disable-http-server-port-values.yaml │ │ ├── readinessprobe-values.yaml │ │ ├── controller-extraLabels-label-values.yaml │ │ ├── default-values-values.yaml │ │ ├── additional-serviceaccount-label-values.yaml │ │ ├── termination-grace-period-values.yaml │ │ ├── clustering-values.yaml │ │ ├── existing-config-values.yaml │ │ ├── host-alias-values.yaml │ │ ├── create-daemonset-values.yaml │ │ ├── create-deployment-values.yaml │ │ ├── roles-and-rolebindings-values.yaml │ │ ├── controller-deployment-pdb-min-available-values.yaml │ │ ├── nonroot-values.yaml │ │ ├── controller-deployment-pdb-max-unavailable-values.yaml │ │ ├── controller-statefulset-pdb-min-available-values.yaml │ │ ├── local-image-pullsecrets-values.yaml │ │ ├── pod_annotations-values.yaml │ │ ├── controller-statefulset-pdb-max-unavailable-values.yaml │ │ ├── enable-servicemonitor-values.yaml │ │ ├── envFrom-values.yaml │ │ ├── create-statefulset-values.yaml │ │ ├── lifecycle-hooks-values.yaml │ │ ├── faro-ingress-values.yaml │ │ ├── create-daemonset-hostnetwork-values.yaml │ │ ├── extra-ports-values.yaml │ │ ├── local-image-registry-values.yaml │ │ ├── custom-config-values.yaml │ │ ├── extra-manifests-values.yaml │ │ ├── enable-servicemonitor-tls-values.yaml │ │ ├── controller-volumes-extra-values.yaml │ │ ├── livinessprobe-values.yaml │ │ ├── extra-env-values.yaml │ │ ├── nodeselectors-and-tolerations-values.yaml │ │ ├── global-image-registry-values.yaml │ │ ├── global-image-pullsecrets-values.yaml │ │ ├── topologyspreadconstraints-values.yaml │ │ ├── with-digests-values.yaml │ │ ├── rbac-rules-values.yaml │ │ ├── create-statefulset-vertical-autoscaling-values.yaml │ │ ├── create-deployment-autoscaling-values.yaml │ │ ├── create-statefulset-autoscaling-values.yaml │ │ ├── sidecars-values.yaml │ │ └── initcontainers-values.yaml │ │ ├── Chart.yaml │ │ ├── config │ │ └── example.alloy │ │ └── .helmignore ├── config │ ├── prometheus │ │ ├── kustomization.yaml │ │ └── monitor.yaml │ ├── network-policy │ │ ├── kustomization.yaml │ │ └── allow-metrics-traffic.yaml │ ├── samples │ │ ├── kustomization.yaml │ │ └── collectors_v1alpha1_alloy.yaml │ ├── scorecard │ │ ├── bases │ │ │ └── config.yaml │ │ ├── patches │ │ │ ├── basic.config.yaml │ │ │ └── olm.config.yaml │ │ └── kustomization.yaml │ ├── rbac │ │ ├── metrics_reader_role.yaml │ │ ├── service_account.yaml │ │ ├── metrics_auth_role_binding.yaml │ │ ├── metrics_auth_role.yaml │ │ ├── role_binding.yaml │ │ ├── leader_election_role_binding.yaml │ │ ├── alloy_viewer_role.yaml │ │ ├── alloy_editor_role.yaml │ │ ├── leader_election_role.yaml │ │ ├── kustomization.yaml │ │ └── role.yaml │ ├── manifests │ │ └── kustomization.yaml │ ├── manager │ │ ├── kustomization.yaml │ │ └── manager.yaml │ ├── crd │ │ ├── kustomization.yaml │ │ └── bases │ │ │ └── collectors.grafana.com_alloys.yaml │ └── default │ │ ├── metrics_service.yaml │ │ ├── manager_metrics_patch.yaml │ │ └── kustomization.yaml ├── watches.yaml └── Dockerfile ├── charts ├── alloy-crd │ ├── values.yaml │ ├── .helmignore │ ├── ct.yaml │ ├── Chart.yaml │ └── crds │ │ └── collectors.grafana.com_alloy.yaml ├── alloy-helm-chart │ ├── values.yaml │ ├── README.md │ └── Chart.yaml ├── alloy-operator │ ├── docs │ │ └── examples │ │ │ ├── default │ │ │ └── values.yaml │ │ │ ├── ownNamespaceOnly │ │ │ └── values.yaml │ │ │ ├── namespaced │ │ │ └── values.yaml │ │ │ ├── securityContext │ │ │ ├── disabled │ │ │ │ └── values.yaml │ │ │ └── noUserOrGroup │ │ │ │ └── values.yaml │ │ │ ├── upgrade-job │ │ │ └── values.yaml │ │ │ └── image-by-digest │ │ │ └── values.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── serviceaccount.yaml │ │ ├── service.yaml │ │ ├── rbac │ │ │ ├── validation.yaml │ │ │ ├── leader-election.yaml │ │ │ ├── alloy-manager.yaml │ │ │ └── alloy-objects.yaml │ │ ├── servicemonitor.yaml │ │ ├── _helpers.tpl │ │ └── deployment.yaml │ ├── charts │ │ ├── podlogs-crd │ │ │ └── Chart.yaml │ │ └── alloy-crd-1.0.0.tgz │ ├── .helmignore │ ├── ct.yaml │ ├── Chart.lock │ ├── schema-mods │ │ └── types-and-enums.json │ ├── tests │ │ ├── pod-settings_test.yaml │ │ ├── rbac-validation_test.yaml │ │ └── rbac-alloy-manager_test.yaml │ ├── Chart.yaml │ ├── README.md.gotmpl │ ├── Makefile │ ├── CHANGELOG.md │ └── values.yaml └── sample-parent-chart │ ├── charts │ └── alloy-operator-0.3.15.tgz │ ├── Chart.lock │ ├── templates │ ├── configmap.yaml │ ├── alloys.yaml │ ├── _helpers.tpl │ └── _crd-validation.tpl │ ├── Chart.yaml │ ├── values.yaml │ └── .helmignore ├── tests ├── integration │ ├── .gitignore │ ├── .envrc │ ├── namespaced │ │ ├── values.yaml │ │ ├── namespaces.yaml │ │ ├── test-plan.yaml │ │ └── alloys.yaml │ ├── own-namespace │ │ ├── values.yaml │ │ ├── namespaces.yaml │ │ ├── test-plan.yaml │ │ └── alloys.yaml │ ├── service-monitor │ │ ├── internal │ │ │ ├── values.yaml │ │ │ ├── alloy.yaml │ │ │ ├── prom-operator-crds.yaml │ │ │ └── test-plan.yaml │ │ └── alloy │ │ │ ├── alloy.yaml │ │ │ ├── prom-operator-crds.yaml │ │ │ └── test-plan.yaml │ ├── upgrade-via-parent-chart │ │ ├── values-first.yaml │ │ ├── values-second.yaml │ │ └── test-plan.yaml │ ├── ingress │ │ ├── alloy.yaml │ │ └── test-plan.yaml │ ├── leader-election │ │ └── test-plan.yaml │ ├── vpa │ │ ├── dependencies │ │ │ └── vpa.yaml │ │ ├── alloy.yaml │ │ └── test-plan.yaml │ ├── basic │ │ ├── alloy.yaml │ │ └── test-plan.yaml │ ├── hpa │ │ ├── kube-state-metrics.yaml │ │ ├── alloy.yaml │ │ └── test-plan.yaml │ ├── pdb │ │ ├── kube-state-metrics.yaml │ │ ├── alloy.yaml │ │ └── test-plan.yaml │ ├── operator-logs │ │ ├── alloy.yaml │ │ └── test-plan.yaml │ ├── statefulset-with-wal │ │ ├── test-plan.yaml │ │ └── alloy.yaml │ ├── beyla-and-unix │ │ ├── test-plan.yaml │ │ └── alloy.yaml │ └── daemonset-with-volumes │ │ ├── test-plan.yaml │ │ └── alloy.yaml └── platform │ ├── .gitignore │ └── remote-config │ ├── Makefile │ ├── .envrc │ ├── test-plan.yaml │ └── alloy.yaml ├── .helmdocsignore ├── cr.yaml ├── .gitignore ├── .github └── workflows │ ├── release-alloy-crd.yaml │ ├── lint.yaml │ ├── integration-test.yml │ └── release-alloy-operator.yaml ├── .yamllint.yml ├── README.md └── .markdownlint.yml /operator/.gitignore: -------------------------------------------------------------------------------- 1 | .temp 2 | -------------------------------------------------------------------------------- /charts/alloy-crd/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /charts/alloy-crd/.helmignore: -------------------------------------------------------------------------------- 1 | ct.yaml 2 | -------------------------------------------------------------------------------- /charts/alloy-helm-chart/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /tests/integration/.gitignore: -------------------------------------------------------------------------------- 1 | kubeconfig.yaml -------------------------------------------------------------------------------- /charts/alloy-operator/docs/examples/default/values.yaml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/integration/.envrc: -------------------------------------------------------------------------------- 1 | export KUBECONFIG=kubeconfig.yaml 2 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | Welcome to Grafana Alloy! 2 | -------------------------------------------------------------------------------- /tests/integration/namespaced/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | namespaces: 3 | - alpha 4 | -------------------------------------------------------------------------------- /tests/integration/own-namespace/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ownNamespaceOnly: true 3 | -------------------------------------------------------------------------------- /operator/config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /charts/alloy-operator/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | Thanks for trying out the {{ .Chart.Name }}! -------------------------------------------------------------------------------- /operator/config/network-policy/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - allow-metrics-traffic.yaml 3 | -------------------------------------------------------------------------------- /tests/platform/.gitignore: -------------------------------------------------------------------------------- 1 | */kubeconfig.yaml 2 | */.random 3 | */grafana-cloud-credentials.yaml 4 | -------------------------------------------------------------------------------- /charts/alloy-operator/docs/examples/ownNamespaceOnly/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ownNamespaceOnly: true 3 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/create-networkpolicy-values.yaml: -------------------------------------------------------------------------------- 1 | networkPolicy: 2 | enabled: true 3 | -------------------------------------------------------------------------------- /tests/integration/service-monitor/internal/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | serviceMonitor: 3 | enabled: true 4 | -------------------------------------------------------------------------------- /.helmdocsignore: -------------------------------------------------------------------------------- 1 | charts/alloy-operator/charts/alloy-crd 2 | charts/alloy-operator/charts/pod-logs-crd 3 | -------------------------------------------------------------------------------- /charts/alloy-operator/docs/examples/namespaced/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | namespaces: 3 | - alpha 4 | - bravo 5 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/disable-http-server-port-values.yaml: -------------------------------------------------------------------------------- 1 | alloy: 2 | enableHttpServerPort: false 3 | -------------------------------------------------------------------------------- /charts/alloy-operator/charts/podlogs-crd/Chart.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v2 3 | name: podlogs-crd 4 | version: 0.0.0 5 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/readinessprobe-values.yaml: -------------------------------------------------------------------------------- 1 | alloy: 2 | initialDelaySeconds: 15 3 | timeoutSeconds: 5 4 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/controller-extraLabels-label-values.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | extraLabels: 3 | custom: "true" 4 | -------------------------------------------------------------------------------- /tests/integration/own-namespace/namespaces.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: alpha 6 | -------------------------------------------------------------------------------- /charts/alloy-operator/.helmignore: -------------------------------------------------------------------------------- 1 | docs 2 | schema-mods 3 | tests 4 | CHANGELOG.md 5 | ct.yaml 6 | Makefile 7 | README.md.gotmpl 8 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/default-values-values.yaml: -------------------------------------------------------------------------------- 1 | # Test rendering of the chart with everything set to the default values. 2 | -------------------------------------------------------------------------------- /charts/alloy-operator/docs/examples/securityContext/disabled/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | podSecurityContext: null 3 | securityContext: null 4 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/additional-serviceaccount-label-values.yaml: -------------------------------------------------------------------------------- 1 | serviceAccount: 2 | additionalLabels: 3 | test: "true" 4 | -------------------------------------------------------------------------------- /cr.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | owner: grafana 3 | git-repo: helm-charts 4 | skip-existing: true 5 | release-name-template: "{{ .Name }}-{{ .Version }}" 6 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/extra-manifests.yaml: -------------------------------------------------------------------------------- 1 | {{ range .Values.extraObjects }} 2 | --- 3 | {{ tpl (toYaml .) $ }} 4 | {{ end }} 5 | -------------------------------------------------------------------------------- /charts/alloy-operator/docs/examples/upgrade-job/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | crds: 3 | deployAlloyCRD: true 4 | upgradeJob: 5 | enabled: true 6 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/termination-grace-period-values.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | type: deployment 3 | terminationGracePeriodSeconds: 20 4 | -------------------------------------------------------------------------------- /charts/alloy-operator/charts/alloy-crd-1.0.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grafana/alloy-operator/HEAD/charts/alloy-operator/charts/alloy-crd-1.0.0.tgz -------------------------------------------------------------------------------- /charts/alloy-operator/docs/examples/image-by-digest/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | image: 3 | digest: sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef 4 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/clustering-values.yaml: -------------------------------------------------------------------------------- 1 | alloy: 2 | clustering: 3 | enabled: true 4 | 5 | controller: 6 | type: 'statefulset' 7 | replicas: 3 8 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/existing-config-values.yaml: -------------------------------------------------------------------------------- 1 | alloy: 2 | configMap: 3 | create: false 4 | name: existing-config 5 | key: my-config.alloy 6 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/host-alias-values.yaml: -------------------------------------------------------------------------------- 1 | alloy: 2 | hostAliases: 3 | - ip: "20.21.22.23" 4 | hostnames: 5 | - "grafana.company.net" 6 | -------------------------------------------------------------------------------- /charts/alloy-operator/docs/examples/securityContext/noUserOrGroup/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | podSecurityContext: 3 | fsGroup: null 4 | runAsGroup: null 5 | runAsUser: null 6 | -------------------------------------------------------------------------------- /charts/sample-parent-chart/charts/alloy-operator-0.3.15.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grafana/alloy-operator/HEAD/charts/sample-parent-chart/charts/alloy-operator-0.3.15.tgz -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/create-daemonset-values.yaml: -------------------------------------------------------------------------------- 1 | # Test rendering of the chart with the controller explicitly set to DaemonSet. 2 | controller: 3 | type: daemonset 4 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/create-deployment-values.yaml: -------------------------------------------------------------------------------- 1 | # Test rendering of the chart with the controller explicitly set to Deployment. 2 | controller: 3 | type: deployment 4 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/roles-and-rolebindings-values.yaml: -------------------------------------------------------------------------------- 1 | # Specify the namespaces for Roles and RoleBindings 2 | rbac: 3 | create: true 4 | namespaces: 5 | - default 6 | -------------------------------------------------------------------------------- /operator/config/samples/kustomization.yaml: -------------------------------------------------------------------------------- 1 | ## Append samples of your project ## 2 | resources: 3 | - collectors_v1alpha1_alloy.yaml 4 | # +kubebuilder:scaffold:manifestskustomizesamples 5 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/controller-deployment-pdb-min-available-values.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | type: deployment 3 | podDisruptionBudget: 4 | enabled: true 5 | minAvailable: 1 6 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/nonroot-values.yaml: -------------------------------------------------------------------------------- 1 | global: 2 | podSecurityContext: 3 | fsGroup: 473 4 | alloy: 5 | securityContext: 6 | runAsUser: 473 7 | runAsGroup: 473 8 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/controller-deployment-pdb-max-unavailable-values.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | type: deployment 3 | podDisruptionBudget: 4 | enabled: true 5 | maxUnavailable: 1 6 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/controller-statefulset-pdb-min-available-values.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | type: statefulset 3 | podDisruptionBudget: 4 | enabled: true 5 | minAvailable: 1 6 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/local-image-pullsecrets-values.yaml: -------------------------------------------------------------------------------- 1 | # Test rendering of the chart with the image pull secret explicitly set. 2 | image: 3 | pullSecrets: 4 | - name: local-cred 5 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/pod_annotations-values.yaml: -------------------------------------------------------------------------------- 1 | # Test correct rendering of the pod annotations 2 | controller: 3 | podAnnotations: 4 | testAnnotationKey: testAnnotationValue 5 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/controller-statefulset-pdb-max-unavailable-values.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | type: statefulset 3 | podDisruptionBudget: 4 | enabled: true 5 | maxUnavailable: 1 6 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/enable-servicemonitor-values.yaml: -------------------------------------------------------------------------------- 1 | # Test rendering of the chart with the service monitor enabled 2 | service: 3 | enabled: true 4 | serviceMonitor: 5 | enabled: true 6 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/envFrom-values.yaml: -------------------------------------------------------------------------------- 1 | # Specify extra ports for verifying rendering the template works 2 | alloy: 3 | envFrom: 4 | - configMapRef: 5 | name: special-config 6 | -------------------------------------------------------------------------------- /operator/config/scorecard/bases/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: [] 8 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/create-statefulset-values.yaml: -------------------------------------------------------------------------------- 1 | # Test rendering of the chart with the controller explicitly set to StatefulSet. 2 | controller: 3 | type: statefulset 4 | minReadySeconds: 60 5 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/lifecycle-hooks-values.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | type: deployment 3 | 4 | alloy: 5 | lifecycle: 6 | preStop: 7 | exec: 8 | command: ["/bin/sh", "-c", "sleep 1"] 9 | -------------------------------------------------------------------------------- /tests/integration/namespaced/namespaces.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: alpha 6 | --- 7 | apiVersion: v1 8 | kind: Namespace 9 | metadata: 10 | name: bravo 11 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/faro-ingress-values.yaml: -------------------------------------------------------------------------------- 1 | alloy: 2 | extraPorts: 3 | - name: "faro" 4 | port: 12347 5 | targetPort: 12347 6 | protocol: "TCP" 7 | 8 | ingress: 9 | enabled: true 10 | -------------------------------------------------------------------------------- /tests/integration/upgrade-via-parent-chart/values-first.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | alloyInstances: {} 3 | 4 | alloy-operator: 5 | replicaCount: 0 6 | crds: 7 | deployAlloyCRD: false 8 | upgradeJob: 9 | enabled: false 10 | -------------------------------------------------------------------------------- /operator/config/rbac/metrics_reader_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: 7 | - "/metrics" 8 | verbs: 9 | - get 10 | -------------------------------------------------------------------------------- /operator/watches.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Use the 'create api' subcommand to add watches to this file. 3 | - group: collectors.grafana.com 4 | version: v1alpha1 5 | kind: Alloy 6 | chart: helm-charts/alloy 7 | # +kubebuilder:scaffold:watch 8 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/create-daemonset-hostnetwork-values.yaml: -------------------------------------------------------------------------------- 1 | # Test rendering of the chart with the controller explicitly set to DaemonSet. 2 | controller: 3 | type: daemonset 4 | hostNetwork: true 5 | dnsPolicy: ClusterFirstWithHostNet 6 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/extra-ports-values.yaml: -------------------------------------------------------------------------------- 1 | # Specify extra ports for verifying rendering the template works 2 | alloy: 3 | extraPorts: 4 | - name: jaeger-thrift 5 | port: 14268 6 | targetPort: 14268 7 | protocol: TCP 8 | -------------------------------------------------------------------------------- /operator/config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: alloy-operator 6 | app.kubernetes.io/managed-by: kustomize 7 | name: controller-manager 8 | namespace: system 9 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/local-image-registry-values.yaml: -------------------------------------------------------------------------------- 1 | # Test rendering of the chart with the individual image registries explicitly set to another value. 2 | image: 3 | registry: quay.io 4 | 5 | configReloader: 6 | image: 7 | registry: quay.io 8 | -------------------------------------------------------------------------------- /charts/alloy-crd/ct.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | remote: origin 3 | target-branch: main 4 | chart-dirs: 5 | - charts 6 | excluded-charts: 7 | - alloy-helm-chart 8 | - alloy-operator 9 | - sample-parent-chart 10 | check-version-increment: false 11 | validate-maintainers: true 12 | -------------------------------------------------------------------------------- /charts/sample-parent-chart/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: alloy-operator 3 | repository: file://../../charts/alloy-operator 4 | version: 0.3.15 5 | digest: sha256:0df22a8de0b20a8a6ac62291ac5ee917c72ff36c36adbedcd0efebc78b8b6eb6 6 | generated: "2025-12-16T13:39:13.64349-06:00" 7 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | appVersion: v1.12.1 3 | dependencies: [] 4 | description: Grafana Alloy 5 | icon: https://raw.githubusercontent.com/grafana/alloy/main/docs/sources/assets/alloy_icon_orange.svg 6 | name: alloy 7 | type: application 8 | version: 1.5.1 9 | -------------------------------------------------------------------------------- /charts/alloy-helm-chart/README.md: -------------------------------------------------------------------------------- 1 | # Alloy Helm Chart 2 | 3 | This chart is used to hold a pristine version of the upstream Alloy Helm chart. 4 | The files here are used to build the contents for the Alloy Operator and associated files. 5 | It is done this way so it can be updated using Dependabot. 6 | -------------------------------------------------------------------------------- /operator/config/manifests/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # These resources constitute the fully configured set of manifests 2 | # used to generate the 'manifests/' directory in a bundle. 3 | resources: 4 | - bases/alloy-operator.clusterserviceversion.yaml 5 | - ../default 6 | - ../samples 7 | - ../scorecard 8 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/custom-config-values.yaml: -------------------------------------------------------------------------------- 1 | alloy: 2 | configMap: 3 | content: |- 4 | logging { 5 | level = "warn" 6 | format = "logfmt" 7 | } 8 | discovery.kubernetes "custom_pods" { 9 | role = "pod" 10 | } 11 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/extra-manifests-values.yaml: -------------------------------------------------------------------------------- 1 | extraObjects: 2 | - apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: grafana-cloud 6 | stringData: 7 | PROMETHEUS_HOST: 'https://prometheus-us-central1.grafana.net/api/prom/push' 8 | PROMETHEUS_USERNAME: '123456' 9 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/enable-servicemonitor-tls-values.yaml: -------------------------------------------------------------------------------- 1 | # Test rendering of the chart with the service monitor enabled 2 | alloy: 3 | listenScheme: HTTPS 4 | service: 5 | enabled: true 6 | serviceMonitor: 7 | enabled: true 8 | tlsConfig: 9 | insecureSkipVerify: true 10 | -------------------------------------------------------------------------------- /charts/sample-parent-chart/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: {{ include "sample-parent-chart.fullname" . }} 6 | labels: 7 | {{- include "sample-parent-chart.labels" . | nindent 4 }} 8 | data: 9 | VERSION: {{ .Chart.Version | quote }} 10 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/controller-volumes-extra-values.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | volumes: 3 | extra: 4 | - name: cache-volume 5 | emptyDir: 6 | sizeLimit: 500Mi 7 | 8 | alloy: 9 | mounts: 10 | extra: 11 | - mountPath: /cache 12 | name: cache-volume 13 | -------------------------------------------------------------------------------- /operator/config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | images: 6 | - name: controller 7 | newName: ghcr.io/grafana/alloy-operator 8 | newTag: 1.4.0@sha256:7dcfac18423eac9aaeacd08748ddde054186d7ec062b7afb380c45bd49065beb 9 | -------------------------------------------------------------------------------- /charts/alloy-helm-chart/Chart.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v2 3 | name: alloy-helm-chart 4 | description: A fake chart for holding the current Alloy helm chart 5 | type: library 6 | version: 1.5.1 7 | dependencies: 8 | - name: alloy 9 | repository: https://grafana.github.io/helm-charts 10 | version: 1.5.1 11 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/livinessprobe-values.yaml: -------------------------------------------------------------------------------- 1 | alloy: 2 | livenessProbe: 3 | httpGet: 4 | path: /metrics 5 | port: 12345 6 | scheme: HTTP 7 | initialDelaySeconds: 30 8 | timeoutSeconds: 2 9 | periodSeconds: 30 10 | successThreshold: 1 11 | failureThreshold: 3 12 | -------------------------------------------------------------------------------- /operator/config/scorecard/patches/basic.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - basic-check-spec 7 | image: quay.io/operator-framework/scorecard-test:v1.39.1 8 | labels: 9 | suite: basic 10 | test: basic-check-spec-test 11 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/extra-env-values.yaml: -------------------------------------------------------------------------------- 1 | # Specify extra ports for verifying rendering the template works 2 | alloy: 3 | extraEnv: 4 | - name: GREETING 5 | value: "Warm greetings to" 6 | - name: HONORIFIC 7 | value: "The Most Honorable" 8 | - name: NAME 9 | value: "Kubernetes" 10 | -------------------------------------------------------------------------------- /operator/config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This kustomization.yaml is not intended to be run by itself, 2 | # since it depends on service name and namespace that are out of this kustomize package. 3 | # It should be run by config/default 4 | resources: 5 | - bases/collectors.grafana.com_alloys.yaml 6 | # +kubebuilder:scaffold:crdkustomizeresource 7 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/nodeselectors-and-tolerations-values.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | nodeSelector: 3 | key1: "value1" 4 | tolerations: 5 | - key: "key1" 6 | operator: "Equal" 7 | value: "value1" 8 | effect: "NoSchedule" 9 | - key: "key2" 10 | operator: "Exists" 11 | effect: "NoSchedule" 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .temp 2 | 3 | # Binaries for programs and plugins 4 | *.exe 5 | *.exe~ 6 | *.dll 7 | *.so 8 | *.dylib 9 | bin 10 | 11 | # editor and IDE paraphernalia 12 | .idea 13 | *.swp 14 | *.swo 15 | *~ 16 | 17 | # A fake chart for holding the current Alloy helm chart 18 | charts/alloy-helm-chart/charts 19 | charts/alloy-helm-chart/Chart.lock -------------------------------------------------------------------------------- /charts/alloy-operator/ct.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | remote: origin 3 | target-branch: main 4 | chart-dirs: 5 | - charts 6 | excluded-charts: 7 | - alloy-crd 8 | - alloy-helm-chart 9 | - sample-parent-chart 10 | chart-repos: 11 | - grafana=https://grafana.github.io/helm-charts 12 | check-version-increment: false 13 | validate-maintainers: true 14 | -------------------------------------------------------------------------------- /charts/alloy-operator/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: alloy-crd 3 | repository: https://grafana.github.io/helm-charts 4 | version: 1.0.0 5 | - name: podlogs-crd 6 | repository: "" 7 | version: 0.0.0 8 | digest: sha256:4912a53de799c03d72164a59b5c3074b6dd0ce980db21bab30e5f84a9b84d17c 9 | generated: "2025-06-05T09:29:36.197717-05:00" 10 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/global-image-registry-values.yaml: -------------------------------------------------------------------------------- 1 | # Test rendering of the chart with the global image registry explicitly set to another value. 2 | global: 3 | image: 4 | registry: quay.io 5 | 6 | image: 7 | registry: docker.com # Invalid value by default 8 | 9 | configReloader: 10 | image: 11 | registry: docker.com 12 | -------------------------------------------------------------------------------- /charts/sample-parent-chart/Chart.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v2 3 | name: sample-parent-chart 4 | description: A sample parent Helm chart for the Alloy Operator 5 | type: application 6 | version: 0.1.0 7 | appVersion: 0.1.0 8 | dependencies: 9 | - name: alloy-operator 10 | repository: "file://../../charts/alloy-operator" 11 | version: "0.3.15" 12 | -------------------------------------------------------------------------------- /tests/integration/ingress/alloy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: collectors.grafana.com/v1alpha1 3 | kind: Alloy 4 | metadata: 5 | name: alloy-test 6 | spec: 7 | alloy: 8 | configMap: 9 | content: |- 10 | logging { 11 | level = "info" 12 | format = "logfmt" 13 | } 14 | 15 | ingress: 16 | enabled: true 17 | -------------------------------------------------------------------------------- /charts/sample-parent-chart/templates/alloys.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.alloyInstances }} 2 | {{- include "crdValidation" . }} 3 | {{- end }} 4 | {{- range $name, $spec := .Values.alloyInstances }} 5 | --- 6 | apiVersion: collectors.grafana.com/v1alpha1 7 | kind: Alloy 8 | metadata: 9 | name: {{ $name }} 10 | spec: {{ $spec | toYaml | nindent 2 }} 11 | {{- end }} 12 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/global-image-pullsecrets-values.yaml: -------------------------------------------------------------------------------- 1 | # Test rendering of the chart with the global image pull secret explicitly set. 2 | global: 3 | image: 4 | pullSecrets: 5 | - name: global-cred 6 | 7 | podSecurityContext: 8 | runAsUser: 1000 9 | runAsGroup: 1000 10 | 11 | image: 12 | pullSecrets: 13 | - name: local-cred 14 | -------------------------------------------------------------------------------- /operator/config/rbac/metrics_auth_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: metrics-auth-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: metrics-auth-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/topologyspreadconstraints-values.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | type: deployment 3 | topologySpreadConstraints: 4 | - maxSkew: 1 5 | topologyKey: topology.kubernetes.io/zone 6 | whenUnsatisfiable: ScheduleAnyway 7 | labelSelector: 8 | matchLabels: 9 | app.kubernetes.io/name: alloy 10 | app.kubernetes.io/instance: alloy 11 | -------------------------------------------------------------------------------- /operator/config/rbac/metrics_auth_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-auth-role 5 | rules: 6 | - apiGroups: 7 | - authentication.k8s.io 8 | resources: 9 | - tokenreviews 10 | verbs: 11 | - create 12 | - apiGroups: 13 | - authorization.k8s.io 14 | resources: 15 | - subjectaccessreviews 16 | verbs: 17 | - create 18 | -------------------------------------------------------------------------------- /charts/sample-parent-chart/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | nameOverride: "" 3 | fullnameOverride: "" 4 | 5 | # -- Example: 6 | # alloyInstances: 7 | # test1: 8 | # alloy: 9 | # configMap: 10 | # content: |- 11 | # prometheus.exporter.self "default" {} 12 | alloyInstances: {} 13 | 14 | alloy-operator: 15 | crds: 16 | deployAlloyCRD: false 17 | upgradeJob: 18 | enabled: false 19 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/with-digests-values.yaml: -------------------------------------------------------------------------------- 1 | image: 2 | registry: "docker.io" 3 | repository: "grafana/agent" 4 | digest: "sha256:82575a7be3e4770e53f620298e58bcc4cdb0fd0338e01c4b206cae9e3ca46ebf" 5 | 6 | configReloader: 7 | image: 8 | registry: "docker.io" 9 | repository: "jimmidyson/configmap-reload" 10 | digest: "sha256:5af9d3041d12a3e63f115125f89b66d2ba981fe82e64302ac370c5496055059c" 11 | -------------------------------------------------------------------------------- /operator/config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: alloy-operator 6 | app.kubernetes.io/managed-by: kustomize 7 | name: manager-rolebinding 8 | roleRef: 9 | apiGroup: rbac.authorization.k8s.io 10 | kind: ClusterRole 11 | name: manager-role 12 | subjects: 13 | - kind: ServiceAccount 14 | name: controller-manager 15 | namespace: system 16 | -------------------------------------------------------------------------------- /charts/sample-parent-chart/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /operator/config/default/metrics_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | app.kubernetes.io/name: alloy-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: controller-manager-metrics-service 9 | namespace: system 10 | spec: 11 | ports: 12 | - name: https 13 | port: 8443 14 | protocol: TCP 15 | targetPort: 8443 16 | selector: 17 | control-plane: controller-manager 18 | -------------------------------------------------------------------------------- /operator/config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: alloy-operator 6 | app.kubernetes.io/managed-by: kustomize 7 | name: leader-election-rolebinding 8 | roleRef: 9 | apiGroup: rbac.authorization.k8s.io 10 | kind: Role 11 | name: leader-election-role 12 | subjects: 13 | - kind: ServiceAccount 14 | name: controller-manager 15 | namespace: system 16 | -------------------------------------------------------------------------------- /charts/alloy-operator/schema-mods/types-and-enums.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "namespaces": {"items": { "type": "string" }}, 4 | "podSecurityContext": { 5 | "type": ["null", "object"], 6 | "properties": { 7 | "fsGroup": {"type": ["null", "integer"]}, 8 | "runAsGroup": {"type": ["null", "integer"]}, 9 | "runAsUser": {"type": ["null", "integer"]} 10 | } 11 | }, 12 | "securityContext": {"type": ["null", "object"]} 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /operator/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM quay.io/operator-framework/helm-operator:v1.42.0@sha256:102abedc1eb4ece4187eab36107cb3176e672ff6bf17d64396cd903ce13affa0 2 | 3 | LABEL org.opencontainers.image.source=https://github.com/grafana/alloy-operator 4 | LABEL org.opencontainers.image.description="Alloy Operator container image" 5 | LABEL org.opencontainers.image.licenses=Apache-2.0 6 | 7 | ENV HOME=/opt/helm 8 | COPY watches.yaml ${HOME}/watches.yaml 9 | COPY helm-charts ${HOME}/helm-charts 10 | WORKDIR ${HOME} 11 | -------------------------------------------------------------------------------- /charts/alloy-crd/Chart.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v2 3 | name: alloy-crd 4 | description: A Helm chart the Alloy CustomResourceDefinition, allowing for discrete deployments of the Alloy CRD independently of the Alloy Operator. 5 | icon: https://raw.githubusercontent.com/grafana/grafana/main/public/img/grafana_icon.svg 6 | sources: 7 | - https://github.com/grafana/alloy-operator 8 | type: application 9 | version: 1.0.0 10 | appVersion: 1.0.0 11 | maintainers: 12 | - email: pete.wall@grafana.com 13 | name: petewall 14 | -------------------------------------------------------------------------------- /tests/integration/leader-election/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: leader-election 5 | 6 | subject: 7 | releaseName: alloy-operator 8 | path: ../../../charts/alloy-operator 9 | namespace: operator 10 | values: 11 | replicaCount: 3 12 | 13 | cluster: 14 | type: kind 15 | 16 | tests: 17 | - type: kubernetes-objects-test 18 | values: 19 | checks: 20 | - kind: Lease 21 | name: alloy-operator 22 | namespace: operator 23 | -------------------------------------------------------------------------------- /operator/config/scorecard/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - bases/config.yaml 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | patches: 6 | - path: patches/basic.config.yaml 7 | target: 8 | group: scorecard.operatorframework.io 9 | kind: Configuration 10 | name: config 11 | version: v1alpha3 12 | - path: patches/olm.config.yaml 13 | target: 14 | group: scorecard.operatorframework.io 15 | kind: Configuration 16 | name: config 17 | version: v1alpha3 18 | # +kubebuilder:scaffold:patches 19 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/config/example.alloy: -------------------------------------------------------------------------------- 1 | logging { 2 | level = "info" 3 | format = "logfmt" 4 | } 5 | 6 | discovery.kubernetes "pods" { 7 | role = "pod" 8 | } 9 | 10 | discovery.kubernetes "nodes" { 11 | role = "node" 12 | } 13 | 14 | discovery.kubernetes "services" { 15 | role = "service" 16 | } 17 | 18 | discovery.kubernetes "endpoints" { 19 | role = "endpoints" 20 | } 21 | 22 | discovery.kubernetes "endpointslices" { 23 | role = "endpointslice" 24 | } 25 | 26 | discovery.kubernetes "ingresses" { 27 | role = "ingress" 28 | } 29 | -------------------------------------------------------------------------------- /charts/alloy-operator/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | --- 3 | apiVersion: v1 4 | kind: ServiceAccount 5 | metadata: 6 | name: {{ include "alloy-operator.serviceAccountName" . }} 7 | namespace: {{ .Release.Namespace }} 8 | labels: 9 | {{- include "alloy-operator.labels" . | nindent 4 }} 10 | {{- with .Values.serviceAccount.annotations }} 11 | annotations: 12 | {{- toYaml . | nindent 4 }} 13 | {{- end }} 14 | automountServiceAccountToken: {{ .Values.serviceAccount.automount }} 15 | {{- end }} 16 | -------------------------------------------------------------------------------- /tests/integration/service-monitor/internal/alloy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: collectors.grafana.com/v1alpha1 3 | kind: Alloy 4 | metadata: 5 | name: alloy-test 6 | spec: 7 | alloy: 8 | configMap: 9 | content: |- 10 | prometheus.operator.servicemonitors "default" { 11 | forward_to = [prometheus.remote_write.local_prom.receiver] 12 | } 13 | 14 | prometheus.remote_write "local_prom" { 15 | endpoint { 16 | url = "http://prometheus-server.prometheus.svc:9090/api/v1/write" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /operator/config/rbac/alloy_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view alloys. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: alloy-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: alloy-viewer-role 9 | rules: 10 | - apiGroups: 11 | - collectors.grafana.com 12 | resources: 13 | - alloys 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - collectors.grafana.com 20 | resources: 21 | - alloys/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /operator/config/default/manager_metrics_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch adds the args to allow exposing the metrics endpoint using HTTPS 2 | - op: add 3 | path: /spec/template/spec/containers/0/args/0 4 | value: --metrics-bind-address=:8443 5 | # This patch adds the args to allow securing the metrics endpoint 6 | - op: add 7 | path: /spec/template/spec/containers/0/args/0 8 | value: --metrics-secure 9 | # This patch adds the args to allow RBAC-based authn/authz the metrics endpoint 10 | - op: add 11 | path: /spec/template/spec/containers/0/args/0 12 | value: --metrics-require-rbac 13 | -------------------------------------------------------------------------------- /.github/workflows/release-alloy-crd.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release Alloy CRD Helm chart 3 | # yamllint disable-line rule:truthy 4 | on: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | release: 9 | uses: grafana/helm-charts/.github/workflows/update-helm-repo.yaml@e5ee6963cce3ab19519dc6dbd5f250aa14dc6f12 10 | permissions: 11 | contents: write 12 | id-token: write 13 | packages: write 14 | with: 15 | charts_dir: charts 16 | cr_configfile: cr.yaml 17 | ct_configfile: charts/alloy-crd/ct.yaml 18 | secrets: 19 | vault_repo_secret_name: github-app 20 | -------------------------------------------------------------------------------- /charts/alloy-operator/tests/pod-settings_test.yaml: -------------------------------------------------------------------------------- 1 | # yamllint disable rule:document-start rule:line-length rule:trailing-spaces 2 | suite: Test - Alloy Operator - Pod Settings 3 | templates: 4 | - deployment.yaml 5 | tests: 6 | - it: can set the priority class name 7 | set: 8 | priorityClassName: high-priority 9 | asserts: 10 | - containsDocument: 11 | apiVersion: apps/v1 12 | kind: Deployment 13 | name: RELEASE-NAME-alloy-operator 14 | - equal: 15 | path: spec.template.spec.priorityClassName 16 | value: high-priority 17 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | 25 | # Don't package templates. 26 | README.md.gotmpl 27 | 28 | # Don't packages the tests used for CI. 29 | /tests/ 30 | -------------------------------------------------------------------------------- /tests/integration/service-monitor/alloy/alloy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: collectors.grafana.com/v1alpha1 3 | kind: Alloy 4 | metadata: 5 | name: alloy-test 6 | spec: 7 | alloy: 8 | configMap: 9 | content: |- 10 | prometheus.operator.servicemonitors "default" { 11 | forward_to = [prometheus.remote_write.local_prom.receiver] 12 | } 13 | 14 | prometheus.remote_write "local_prom" { 15 | endpoint { 16 | url = "http://prometheus-server.prometheus.svc:9090/api/v1/write" 17 | } 18 | } 19 | 20 | serviceMonitor: 21 | enabled: true 22 | -------------------------------------------------------------------------------- /tests/integration/vpa/dependencies/vpa.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: source.toolkit.fluxcd.io/v1 3 | kind: HelmRepository 4 | metadata: 5 | name: fairwinds 6 | namespace: kube-system 7 | spec: 8 | interval: 1m 9 | url: https://charts.fairwinds.com/stable 10 | --- 11 | apiVersion: helm.toolkit.fluxcd.io/v2 12 | kind: HelmRelease 13 | metadata: 14 | name: vpa 15 | namespace: kube-system 16 | spec: 17 | interval: 1m 18 | chart: 19 | spec: 20 | chart: vpa 21 | sourceRef: 22 | kind: HelmRepository 23 | name: fairwinds 24 | namespace: kube-system 25 | interval: 1m 26 | -------------------------------------------------------------------------------- /operator/config/rbac/alloy_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit alloys. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: alloy-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: alloy-editor-role 9 | rules: 10 | - apiGroups: 11 | - collectors.grafana.com 12 | resources: 13 | - alloys 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - collectors.grafana.com 24 | resources: 25 | - alloys/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /tests/integration/basic/alloy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: collectors.grafana.com/v1alpha1 3 | kind: Alloy 4 | metadata: 5 | name: alloy-test 6 | spec: 7 | alloy: 8 | configMap: 9 | content: |- 10 | prometheus.exporter.self "default" {} 11 | 12 | prometheus.scrape "default" { 13 | targets = prometheus.exporter.self.default.targets 14 | forward_to = [prometheus.remote_write.local_prom.receiver] 15 | } 16 | 17 | prometheus.remote_write "local_prom" { 18 | endpoint { 19 | url = "http://prometheus-server.prometheus.svc:9090/api/v1/write" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /charts/alloy-operator/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "alloy-operator.fullname" . }} 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | {{- include "alloy-operator.labels" . | nindent 4 }} 8 | spec: 9 | type: {{ .Values.service.type }} 10 | ports: 11 | - name: http 12 | port: {{ .Values.service.health.port }} 13 | targetPort: http 14 | protocol: TCP 15 | - name: metrics 16 | port: {{ .Values.service.metrics.port }} 17 | targetPort: metrics 18 | protocol: TCP 19 | selector: 20 | {{- include "alloy-operator.selectorLabels" . | nindent 4 }} 21 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/rbac-rules-values.yaml: -------------------------------------------------------------------------------- 1 | # Specify the namespaces for Roles and RoleBindings 2 | rbac: 3 | create: true 4 | rules: 5 | # -- Rules required for the `discovery.kubernetes` component. 6 | - apiGroups: ["", "discovery.k8s.io", "networking.k8s.io"] 7 | resources: ["endpoints", "endpointslices", "ingresses", "nodes", "nodes/proxy", "nodes/metrics", "pods", "services"] 8 | verbs: ["get", "list", "watch"] 9 | # Rules for the `prometheus.operator.*` components. 10 | - apiGroups: ["monitoring.coreos.com"] 11 | resources: ["podmonitors", "servicemonitors", "probes", "scrapeconfigs"] 12 | verbs: ["get", "list", "watch"] 13 | -------------------------------------------------------------------------------- /tests/integration/ingress/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: ingress 5 | 6 | subject: 7 | releaseName: alloy-operator 8 | path: ../../../charts/alloy-operator 9 | namespace: operator 10 | 11 | postInstall: 12 | files: [alloy.yaml] 13 | 14 | cluster: 15 | type: kind 16 | 17 | tests: 18 | - type: kubernetes-objects-test 19 | values: 20 | checks: 21 | # Check that the Alloy instance has been reconciled 22 | - kind: DaemonSet 23 | name: alloy-test 24 | namespace: default 25 | - kind: Ingress 26 | name: alloy-test 27 | namespace: default 28 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | {{- $values := (mustMergeOverwrite .Values.alloy (or .Values.agent dict)) -}} 2 | {{- if $values.configMap.create }} 3 | apiVersion: v1 4 | kind: ConfigMap 5 | metadata: 6 | name: {{ include "alloy.config-map.name" . }} 7 | namespace: {{ include "alloy.namespace" . }} 8 | labels: 9 | {{- include "alloy.labels" . | nindent 4 }} 10 | app.kubernetes.io/component: config 11 | data: 12 | {{- if $values.configMap.content }} 13 | config.alloy: |- {{- (tpl $values.configMap.content .) | nindent 4 }} 14 | {{- else }} 15 | config.alloy: |- {{- .Files.Get "config/example.alloy" | trim | nindent 4 }} 16 | {{- end }} 17 | {{- end }} 18 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} 5 | metadata: 6 | name: {{ include "alloy.serviceAccountName" . }} 7 | namespace: {{ include "alloy.namespace" . }} 8 | labels: 9 | {{- include "alloy.labels" . | nindent 4 }} 10 | app.kubernetes.io/component: rbac 11 | {{- with .Values.serviceAccount.additionalLabels }} 12 | {{- toYaml . | nindent 4 }} 13 | {{- end }} 14 | {{- with .Values.serviceAccount.annotations }} 15 | annotations: 16 | {{- toYaml . | nindent 4 }} 17 | {{- end }} 18 | {{- end }} 19 | -------------------------------------------------------------------------------- /.yamllint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | yaml-files: 3 | - "*.yaml" 4 | - "*.yml" 5 | - ".yamllint" 6 | 7 | extends: default 8 | 9 | ignore: 10 | - charts/**/templates # Chart template files, which are often templated 11 | - charts/alloy-helm-chart/charts/**/* # Local copy of the Alloy Helm chart 12 | - charts/alloy-crd/crds/* # Alloy CRD 13 | - charts/alloy-operator/alloy-values.yaml # Upstream Alloy Helm chart values.yaml 14 | - charts/alloy-operator/charts/podlogs-crd/crds/* # PodLogs CRD 15 | - tests/integration/**/kubeconfig.yaml # Integration test kubeconfig files 16 | rules: 17 | line-length: 18 | max: 200 19 | level: warning 20 | -------------------------------------------------------------------------------- /tests/platform/remote-config/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: grafana-cloud-credentials.yaml 3 | grafana-cloud-credentials.yaml: 4 | echo "---" > $@ 5 | echo "# yamllint disable rule:line-length" >> $@ 6 | kubectl create secret generic grafana-cloud-credentials \ 7 | --from-literal=GRAFANA_CLOUD_FLEET_MGMT_USER="$$GRAFANA_CLOUD_FLEET_MGMT_USER" \ 8 | --from-literal=GRAFANA_CLOUD_FLEET_MGMT_TOKEN="$$GRAFANA_CLOUD_FLEET_MGMT_TOKEN" \ 9 | --from-literal=PROMETHEUS_USER="$$GRAFANA_CLOUD_METRICS_USERNAME" \ 10 | --from-literal=PROMETHEUS_PASS="$$GRAFANA_CLOUD_RW_POLICY_TOKEN" \ 11 | -o yaml --dry-run=client >> $@ 12 | 13 | .PHONY: clean 14 | clean: 15 | rm -f grafana-cloud-credentials.yaml .random kubeconfig.yaml 16 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/create-statefulset-vertical-autoscaling-values.yaml: -------------------------------------------------------------------------------- 1 | # Test rendering of the chart with the controller explicitly set to StatefulSet and autoscaling with vertical. 2 | controller: 3 | type: statefulset 4 | autoscaling: 5 | vertical: 6 | enabled: true 7 | resourcePolicy: 8 | containerPolicies: 9 | - containerName: alloy 10 | controlledResources: 11 | - cpu 12 | - memory 13 | controlledValues: "RequestsAndLimits" 14 | maxAllowed: 15 | cpu: "500m" 16 | memory: "600Mi" 17 | minAllowed: 18 | cpu: "250m" 19 | memory: "100Mi" 20 | enableStatefulSetAutoDeletePVC: true 21 | -------------------------------------------------------------------------------- /tests/integration/upgrade-via-parent-chart/values-second.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | alloyInstances: 3 | alloy: 4 | alloy: 5 | configMap: 6 | content: |- 7 | prometheus.exporter.self "default" {} 8 | 9 | prometheus.scrape "default" { 10 | targets = prometheus.exporter.self.default.targets 11 | forward_to = [prometheus.remote_write.local_prom.receiver] 12 | } 13 | 14 | prometheus.remote_write "local_prom" { 15 | endpoint { 16 | url = "http://prometheus-server.prometheus.svc:9090/api/v1/write" 17 | } 18 | } 19 | 20 | alloy-operator: 21 | crds: 22 | deployAlloyCRD: true 23 | upgradeJob: 24 | enabled: true 25 | -------------------------------------------------------------------------------- /tests/platform/remote-config/.envrc: -------------------------------------------------------------------------------- 1 | export GRAFANA_CLOUD_FLEET_MGMT_USER=$(op --account grafana.1password.com read "op://Kubernetes Monitoring/helmchart Fleet Management/username") 2 | export GRAFANA_CLOUD_FLEET_MGMT_TOKEN=$(op --account grafana.1password.com read "op://Kubernetes Monitoring/helmchart Fleet Management/password") 3 | export GRAFANA_CLOUD_METRICS_USERNAME=$(op --account grafana.1password.com read "op://Kubernetes Monitoring/helmchart Prometheus/username") 4 | export GRAFANA_CLOUD_RW_POLICY_TOKEN=$(op --account grafana.1password.com read "op://Kubernetes Monitoring/helmchart Prometheus/password") 5 | export KUBECONFIG=kubeconfig.yaml 6 | export PATH=$(pwd)/../../../../helm-chart-toolbox/tools/helm-test:$PATH 7 | make grafana-cloud-credentials.yaml 8 | -------------------------------------------------------------------------------- /charts/alloy-operator/Chart.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v2 3 | name: alloy-operator 4 | description: A Helm chart the Alloy Operator, a project to innovate on creating instances of Grafana Alloy. 5 | icon: https://raw.githubusercontent.com/grafana/grafana/main/public/img/grafana_icon.svg 6 | sources: 7 | - https://github.com/grafana/alloy-operator 8 | type: application 9 | version: 0.3.15 10 | appVersion: "1.5.1" 11 | maintainers: 12 | - email: pete.wall@grafana.com 13 | name: petewall 14 | dependencies: 15 | - name: alloy-crd 16 | version: 1.0.0 17 | repository: https://grafana.github.io/helm-charts 18 | condition: crds.deployAlloyCRD 19 | - name: podlogs-crd 20 | version: 0.0.0 21 | repository: "" 22 | condition: crds.deployPodLogsCRD 23 | -------------------------------------------------------------------------------- /operator/config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: alloy-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: leader-election-role 9 | rules: 10 | - apiGroups: 11 | - "" 12 | resources: 13 | - configmaps 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - create 19 | - update 20 | - patch 21 | - delete 22 | - apiGroups: 23 | - coordination.k8s.io 24 | resources: 25 | - leases 26 | verbs: 27 | - get 28 | - list 29 | - watch 30 | - create 31 | - update 32 | - patch 33 | - delete 34 | - apiGroups: 35 | - "" 36 | resources: 37 | - events 38 | verbs: 39 | - create 40 | - patch 41 | -------------------------------------------------------------------------------- /tests/integration/hpa/kube-state-metrics.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: kube-state-metrics 6 | --- 7 | apiVersion: source.toolkit.fluxcd.io/v1 8 | kind: HelmRepository 9 | metadata: 10 | name: prometheus-community 11 | namespace: kube-state-metrics 12 | spec: 13 | interval: 1m 14 | url: https://prometheus-community.github.io/helm-charts 15 | --- 16 | apiVersion: helm.toolkit.fluxcd.io/v2 17 | kind: HelmRelease 18 | metadata: 19 | name: kube-state-metrics 20 | namespace: kube-state-metrics 21 | spec: 22 | interval: 1m 23 | chart: 24 | spec: 25 | chart: kube-state-metrics 26 | sourceRef: 27 | kind: HelmRepository 28 | name: prometheus-community 29 | namespace: kube-state-metrics 30 | interval: 1m 31 | -------------------------------------------------------------------------------- /tests/integration/pdb/kube-state-metrics.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: kube-state-metrics 6 | --- 7 | apiVersion: source.toolkit.fluxcd.io/v1 8 | kind: HelmRepository 9 | metadata: 10 | name: prometheus-community 11 | namespace: kube-state-metrics 12 | spec: 13 | interval: 1m 14 | url: https://prometheus-community.github.io/helm-charts 15 | --- 16 | apiVersion: helm.toolkit.fluxcd.io/v2 17 | kind: HelmRelease 18 | metadata: 19 | name: kube-state-metrics 20 | namespace: kube-state-metrics 21 | spec: 22 | interval: 1m 23 | chart: 24 | spec: 25 | chart: kube-state-metrics 26 | sourceRef: 27 | kind: HelmRepository 28 | name: prometheus-community 29 | namespace: kube-state-metrics 30 | interval: 1m 31 | -------------------------------------------------------------------------------- /tests/integration/service-monitor/alloy/prom-operator-crds.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: prometheus 6 | --- 7 | apiVersion: source.toolkit.fluxcd.io/v1 8 | kind: HelmRepository 9 | metadata: 10 | name: prometheus-community 11 | namespace: prometheus 12 | spec: 13 | interval: 1m 14 | url: https://prometheus-community.github.io/helm-charts 15 | --- 16 | apiVersion: helm.toolkit.fluxcd.io/v2 17 | kind: HelmRelease 18 | metadata: 19 | name: prometheus-operator-crds 20 | namespace: prometheus 21 | spec: 22 | interval: 1m 23 | chart: 24 | spec: 25 | chart: prometheus-operator-crds 26 | sourceRef: 27 | kind: HelmRepository 28 | name: prometheus-community 29 | namespace: prometheus 30 | interval: 1m 31 | -------------------------------------------------------------------------------- /tests/integration/service-monitor/internal/prom-operator-crds.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: prometheus 6 | --- 7 | apiVersion: source.toolkit.fluxcd.io/v1 8 | kind: HelmRepository 9 | metadata: 10 | name: prometheus-community 11 | namespace: prometheus 12 | spec: 13 | interval: 1m 14 | url: https://prometheus-community.github.io/helm-charts 15 | --- 16 | apiVersion: helm.toolkit.fluxcd.io/v2 17 | kind: HelmRelease 18 | metadata: 19 | name: prometheus-operator-crds 20 | namespace: prometheus 21 | spec: 22 | interval: 1m 23 | chart: 24 | spec: 25 | chart: prometheus-operator-crds 26 | sourceRef: 27 | kind: HelmRepository 28 | name: prometheus-community 29 | namespace: prometheus 30 | interval: 1m 31 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/create-deployment-autoscaling-values.yaml: -------------------------------------------------------------------------------- 1 | # Test rendering of the chart with the controller explicitly set to Deployment and autoscaling enabled. 2 | controller: 3 | type: deployment 4 | autoscaling: 5 | horizontal: 6 | enabled: true 7 | scaleDown: 8 | policies: 9 | - type: Pods 10 | value: 4 11 | periodSeconds: 60 12 | selectPolicy: Min 13 | stabilizationWindowSeconds: 100 14 | scaleUp: 15 | policies: 16 | - type: Pods 17 | value: 4 18 | periodSeconds: 60 19 | - type: Percent 20 | value: 100 21 | periodSeconds: 15 22 | stabilizationWindowSeconds: 80 23 | alloy: 24 | resources: 25 | requests: 26 | memory: 100Mi 27 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/create-statefulset-autoscaling-values.yaml: -------------------------------------------------------------------------------- 1 | # Test rendering of the chart with the controller explicitly set to StatefulSet and autoscaling the old way enabled. 2 | controller: 3 | type: statefulset 4 | autoscaling: 5 | enabled: true 6 | scaleDown: 7 | policies: 8 | - type: Pods 9 | value: 4 10 | periodSeconds: 60 11 | selectPolicy: Min 12 | stabilizationWindowSeconds: 100 13 | scaleUp: 14 | policies: 15 | - type: Pods 16 | value: 4 17 | periodSeconds: 60 18 | - type: Percent 19 | value: 100 20 | periodSeconds: 15 21 | stabilizationWindowSeconds: 80 22 | enableStatefulSetAutoDeletePVC: true 23 | alloy: 24 | resources: 25 | requests: 26 | memory: 100Mi 27 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/_config.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Retrieve configMap name from the name of the chart or the ConfigMap the user 3 | specified. 4 | */}} 5 | {{- define "alloy.config-map.name" -}} 6 | {{- $values := (mustMergeOverwrite .Values.alloy (or .Values.agent dict)) -}} 7 | {{- if $values.configMap.name -}} 8 | {{- $values.configMap.name }} 9 | {{- else -}} 10 | {{- include "alloy.fullname" . }} 11 | {{- end }} 12 | {{- end }} 13 | 14 | {{/* 15 | The name of the config file is the default or the key the user specified in the 16 | ConfigMap. 17 | */}} 18 | {{- define "alloy.config-map.key" -}} 19 | {{- $values := (mustMergeOverwrite .Values.alloy (or .Values.agent dict)) -}} 20 | {{- if $values.configMap.key -}} 21 | {{- $values.configMap.key }} 22 | {{- else -}} 23 | config.alloy 24 | {{- end }} 25 | {{- end }} 26 | -------------------------------------------------------------------------------- /charts/alloy-operator/README.md.gotmpl: -------------------------------------------------------------------------------- 1 | 5 | 6 | {{ template "chart.header" . }} 7 | {{ template "chart.deprecationWarning" . }} 8 | 9 | {{ template "chart.badgesSection" . }} 10 | 11 | {{ template "chart.description" . }} 12 | 13 | {{ template "chart.homepageLine" . }} 14 | 15 | {{ template "chart.maintainersSection" . }} 16 | 17 | 18 | 19 | {{ template "chart.sourcesSection" . }} 20 | 21 | 22 | {{ template "chart.requirementsSection" . }} 23 | 24 | 25 | {{ template "chart.valuesSection" . }} 26 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/sidecars-values.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | extraContainers: 3 | - name: geo-ip 4 | image: ghcr.io/maxmind/geoipupdate:v6.0 5 | volumeMounts: 6 | - name: geoip 7 | mountPath: /etc/geoip 8 | volumes: 9 | - name: geoip 10 | emptyDir: {} 11 | env: 12 | - name: GEOIPUPDATE_ACCOUNT_ID 13 | value: "geoipupdate_account_id" 14 | - name: GEOIPUPDATE_LICENSE_KEY 15 | value: "geoipupdate_license_key" 16 | - name: GEOIPUPDATE_EDITION_IDS 17 | value: "GeoLite2-ASN GeoLite2-City GeoLite2-Country" 18 | - name: GEOIPUPDATE_DB_DIR 19 | value: "/etc/geoip" 20 | volumes: 21 | extra: 22 | - name: geoip 23 | mountPath: /etc/geoip 24 | 25 | alloy: 26 | mounts: 27 | extra: 28 | - name: geoip 29 | mountPath: /etc/geoip 30 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/ci/initcontainers-values.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | initContainers: 3 | - name: geo-ip 4 | image: ghcr.io/maxmind/geoipupdate:v6.0 5 | volumeMounts: 6 | - name: geoip 7 | mountPath: /etc/geoip 8 | volumes: 9 | - name: geoip 10 | emptyDir: {} 11 | env: 12 | - name: GEOIPUPDATE_ACCOUNT_ID 13 | value: "geoipupdate_account_id" 14 | - name: GEOIPUPDATE_LICENSE_KEY 15 | value: "geoipupdate_license_key" 16 | - name: GEOIPUPDATE_EDITION_IDS 17 | value: "GeoLite2-ASN GeoLite2-City GeoLite2-Country" 18 | - name: GEOIPUPDATE_DB_DIR 19 | value: "/etc/geoip" 20 | volumes: 21 | extra: 22 | - name: geoip 23 | mountPath: /etc/geoip 24 | 25 | alloy: 26 | mounts: 27 | extra: 28 | - name: geoip 29 | mountPath: /etc/geoip 30 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/networkpolicy.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.networkPolicy.enabled (eq .Values.networkPolicy.flavor "kubernetes") -}} 2 | apiVersion: networking.k8s.io/v1 3 | kind: NetworkPolicy 4 | metadata: 5 | name: {{ include "alloy.fullname" . }} 6 | namespace: {{ include "alloy.namespace" . }} 7 | labels: 8 | {{- include "alloy.labels" . | nindent 4 }} 9 | app.kubernetes.io/component: networking 10 | spec: 11 | podSelector: 12 | matchLabels: 13 | {{- include "alloy.selectorLabels" . | nindent 6 }} 14 | policyTypes: 15 | {{- toYaml .Values.networkPolicy.policyTypes | nindent 4 }} 16 | {{- if .Values.networkPolicy.ingress }} 17 | ingress: 18 | {{- toYaml .Values.networkPolicy.ingress | nindent 4 }} 19 | {{- end }} 20 | {{- if .Values.networkPolicy.egress }} 21 | egress: 22 | {{- toYaml .Values.networkPolicy.egress | nindent 4 }} 23 | {{- end }} 24 | {{- end }} 25 | -------------------------------------------------------------------------------- /operator/config/network-policy/allow-metrics-traffic.yaml: -------------------------------------------------------------------------------- 1 | # This NetworkPolicy allows ingress traffic 2 | # with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those 3 | # namespaces are able to gathering data from the metrics endpoint. 4 | apiVersion: networking.k8s.io/v1 5 | kind: NetworkPolicy 6 | metadata: 7 | labels: 8 | app.kubernetes.io/name: alloy-operator 9 | app.kubernetes.io/managed-by: kustomize 10 | name: allow-metrics-traffic 11 | namespace: system 12 | spec: 13 | podSelector: 14 | matchLabels: 15 | control-plane: controller-manager 16 | policyTypes: 17 | - Ingress 18 | ingress: 19 | # This allows ingress traffic from any namespace with the label metrics: enabled 20 | - from: 21 | - namespaceSelector: 22 | matchLabels: 23 | metrics: enabled # Only from namespaces with this label 24 | ports: 25 | - port: 8443 26 | protocol: TCP 27 | -------------------------------------------------------------------------------- /charts/sample-parent-chart/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{- define "sample-parent-chart.name" -}} 2 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 3 | {{- end }} 4 | 5 | {{- define "sample-parent-chart.fullname" -}} 6 | {{- if .Values.fullnameOverride }} 7 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 8 | {{- else }} 9 | {{- $name := default .Chart.Name .Values.nameOverride }} 10 | {{- if contains $name .Release.Name }} 11 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 12 | {{- else }} 13 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 14 | {{- end }} 15 | {{- end }} 16 | {{- end }} 17 | 18 | {{- define "sample-parent-chart.labels" -}} 19 | helm.sh/chart: {{ include "alloy-operator.chart" . }} 20 | {{ include "alloy-operator.selectorLabels" . }} 21 | {{- if .Chart.AppVersion }} 22 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 23 | {{- end }} 24 | app.kubernetes.io/managed-by: {{ .Release.Service }} 25 | {{- end }} 26 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/controllers/daemonset.yaml: -------------------------------------------------------------------------------- 1 | {{- if eq .Values.controller.type "daemonset" }} 2 | apiVersion: apps/v1 3 | kind: DaemonSet 4 | metadata: 5 | name: {{ include "alloy.fullname" . }} 6 | namespace: {{ include "alloy.namespace" . }} 7 | labels: 8 | {{- include "alloy.labels" . | nindent 4 }} 9 | {{- with .Values.controller.extraLabels }} 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- with .Values.controller.extraAnnotations }} 13 | annotations: 14 | {{- toYaml . | nindent 4 }} 15 | {{- end }} 16 | spec: 17 | {{- if ge (int .Capabilities.KubeVersion.Minor) 22 }} 18 | minReadySeconds: {{ .Values.controller.minReadySeconds }} 19 | {{- end }} 20 | selector: 21 | matchLabels: 22 | {{- include "alloy.selectorLabels" . | nindent 6 }} 23 | template: 24 | {{- include "alloy.pod-template" . | nindent 4 }} 25 | {{- with .Values.controller.updateStrategy }} 26 | updateStrategy: 27 | {{- toYaml . | nindent 4 }} 28 | {{- end }} 29 | {{- end }} 30 | -------------------------------------------------------------------------------- /tests/integration/operator-logs/alloy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: collectors.grafana.com/v1alpha1 3 | kind: Alloy 4 | metadata: 5 | name: alloy-logs 6 | spec: 7 | alloy: 8 | configMap: 9 | content: |- 10 | discovery.kubernetes "operator_pods" { 11 | role = "pod" 12 | namespaces { 13 | names = ["operator"] 14 | } 15 | selectors { 16 | role = "pod" 17 | label = "app.kubernetes.io/name=alloy-operator" 18 | } 19 | } 20 | 21 | loki.source.kubernetes "operator_pods" { 22 | targets = discovery.kubernetes.operator_pods.targets 23 | forward_to = [loki.write.loki.receiver] 24 | } 25 | 26 | loki.write "loki" { 27 | endpoint { 28 | url = "http://loki.loki.svc:3100/loki/api/v1/push" 29 | tenant_id = "1" 30 | basic_auth { 31 | username = "loki" 32 | password = "lokipassword" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/containers/_watch.yaml: -------------------------------------------------------------------------------- 1 | {{- define "alloy.watch-container" -}} 2 | {{- $values := (mustMergeOverwrite .Values.alloy (or .Values.agent dict)) -}} 3 | {{- if .Values.configReloader.enabled -}} 4 | - name: config-reloader 5 | image: {{ .Values.global.image.registry | default .Values.configReloader.image.registry }}/{{ .Values.configReloader.image.repository }}{{ include "config-reloader.imageId" . }} 6 | {{- if .Values.configReloader.customArgs }} 7 | args: 8 | {{- toYaml .Values.configReloader.customArgs | nindent 4 }} 9 | {{- else }} 10 | args: 11 | - --watched-dir=/etc/alloy 12 | - --reload-url=http://localhost:{{ $values.listenPort }}/-/reload 13 | {{- end }} 14 | volumeMounts: 15 | - name: config 16 | mountPath: /etc/alloy 17 | {{- with .Values.configReloader.resources }} 18 | resources: 19 | {{- toYaml . | nindent 4 }} 20 | {{- end }} 21 | {{- with .Values.configReloader.securityContext }} 22 | securityContext: 23 | {{- toYaml . | nindent 4 }} 24 | {{- end }} 25 | {{- end }} 26 | {{- end -}} 27 | -------------------------------------------------------------------------------- /charts/alloy-operator/templates/rbac/validation.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | {{- if and .Values.rbac.create (not .Values.rbac.createClusterRoles) (not .Values.namespaces) (not .Values.ownNamespaceOnly) }} 3 | {{- $msg := list "" "The Alloy Operator requires namespaces to be set when not using ClusterRoles." }} 4 | {{- $msg = append $msg "Please set the list of namespaces to manage Alloy instances:" }} 5 | {{- $msg = append $msg "alloy-operator:" }} 6 | {{- $msg = append $msg " namespaces: [\"namespace1\", \"namespace2\"]" }} 7 | {{- $msg = append $msg "" }} 8 | {{- $msg = append $msg (printf "Or, only allow management of Alloy instances within the same namespace (i.e. \"%s\")" $.Release.Namespace) }} 9 | {{- $msg = append $msg "alloy-operator:" }} 10 | {{- $msg = append $msg " ownNamespaceOnly: true" }} 11 | {{- $msg = append $msg "" }} 12 | {{- $msg = append $msg "Or, allow the creation of ClusterRoles and ClusterRoleBindings:" }} 13 | {{- $msg = append $msg "alloy-operator:" }} 14 | {{- $msg = append $msg " rbac:" }} 15 | {{- $msg = append $msg " createClusterRoles: true" }} 16 | {{- fail (join "\n" $msg) }} 17 | {{- end }} 18 | -------------------------------------------------------------------------------- /tests/platform/remote-config/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: remote-config 5 | 6 | subject: 7 | releaseName: alloy-operator 8 | path: ../../../charts/alloy-operator 9 | namespace: operator 10 | 11 | postInstall: 12 | files: [alloy.yaml] 13 | 14 | cluster: 15 | type: kind 16 | appendRandomNumber: true 17 | 18 | dependencies: 19 | - preset: test-parameters 20 | - preset: test-parameters 21 | namespace: toolbox 22 | - file: grafana-cloud-credentials.yaml 23 | - file: grafana-cloud-credentials.yaml 24 | namespace: toolbox 25 | 26 | tests: 27 | - type: query-test 28 | values: 29 | tests: 30 | - env: 31 | NAMESPACE: default 32 | PROMETHEUS_URL: https://prometheus-prod-13-prod-us-east-0.grafana.net/api/prom/api/v1/query 33 | envFrom: 34 | - secretRef: {name: grafana-cloud-credentials} 35 | - configMapRef: {name: test-parameters} 36 | queries: 37 | - query: alloy_build_info{collector_id=~"alloy-operator-$clusterName-$NAMESPACE-alloy-client-.*"} 38 | type: promql 39 | -------------------------------------------------------------------------------- /tests/integration/upgrade-via-parent-chart/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: upgrade-via-parent-chart 5 | 6 | subject: 7 | releaseName: sample-parent-chart 8 | path: ../../../charts/sample-parent-chart 9 | valuesFile: values-first.yaml 10 | 11 | postInstall: 12 | files: [../../../charts/alloy-crd/crds/collectors.grafana.com_alloy.yaml] 13 | 14 | upgrade: 15 | valuesFile: values-second.yaml 16 | 17 | cluster: 18 | type: kind 19 | 20 | dependencies: 21 | - preset: prometheus 22 | - preset: grafana 23 | overrides: 24 | datasources: 25 | datasources.yaml: 26 | apiVersion: 1 27 | datasources: 28 | - name: Prometheus 29 | type: prometheus 30 | url: http://prometheus-server.prometheus.svc:9090 31 | isDefault: true 32 | 33 | tests: 34 | - type: query-test 35 | values: 36 | tests: 37 | - env: 38 | PROMETHEUS_URL: http://prometheus-server.prometheus.svc:9090/api/v1/query 39 | queries: 40 | - query: alloy_build_info{job="integrations/self"} 41 | type: promql 42 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/controllers/deployment.yaml: -------------------------------------------------------------------------------- 1 | {{- if eq .Values.controller.type "deployment" }} 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: {{ include "alloy.fullname" . }} 6 | namespace: {{ include "alloy.namespace" . }} 7 | labels: 8 | {{- include "alloy.labels" . | nindent 4 }} 9 | {{- with .Values.controller.extraLabels }} 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- with .Values.controller.extraAnnotations }} 13 | annotations: 14 | {{- toYaml . | nindent 4 }} 15 | {{- end }} 16 | spec: 17 | {{- if (and (not .Values.controller.autoscaling.enabled) (not .Values.controller.autoscaling.horizontal.enabled)) }} 18 | replicas: {{ .Values.controller.replicas }} 19 | {{- end }} 20 | {{- if ge (int .Capabilities.KubeVersion.Minor) 22 }} 21 | minReadySeconds: {{ .Values.controller.minReadySeconds }} 22 | {{- end }} 23 | selector: 24 | matchLabels: 25 | {{- include "alloy.selectorLabels" . | nindent 6 }} 26 | template: 27 | {{- include "alloy.pod-template" . | nindent 4 }} 28 | {{- with .Values.controller.updateStrategy }} 29 | strategy: 30 | {{- toYaml . | nindent 4 }} 31 | {{- end }} 32 | {{- end }} 33 | -------------------------------------------------------------------------------- /operator/config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | # All RBAC will be applied under this service account in 3 | # the deployment namespace. You may comment out this resource 4 | # if your manager will use a service account that exists at 5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding 6 | # subjects if changing service account names. 7 | - service_account.yaml 8 | - role.yaml 9 | - role_binding.yaml 10 | - leader_election_role.yaml 11 | - leader_election_role_binding.yaml 12 | # The following RBAC configurations are used to protect 13 | # the metrics endpoint with authn/authz. These configurations 14 | # ensure that only authorized users and service accounts 15 | # can access the metrics endpoint. Comment the following 16 | # permissions if you want to disable this protection. 17 | # More info: https://book.kubebuilder.io/reference/metrics.html 18 | - metrics_auth_role.yaml 19 | - metrics_auth_role_binding.yaml 20 | - metrics_reader_role.yaml 21 | # For each CRD, "Editor" and "Viewer" roles are scaffolded by 22 | # default, aiding admins in cluster management. Those roles are 23 | # not used by the Project itself. You can comment the following lines 24 | # if you do not want those helpers be installed with your Project. 25 | - alloy_editor_role.yaml 26 | - alloy_viewer_role.yaml 27 | 28 | -------------------------------------------------------------------------------- /tests/integration/service-monitor/alloy/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: alloy-service-monitor 5 | 6 | subject: 7 | releaseName: alloy-operator 8 | path: ../../../../charts/alloy-operator 9 | 10 | postInstall: 11 | files: [alloy.yaml] 12 | 13 | cluster: 14 | type: kind 15 | 16 | dependencies: 17 | - file: prom-operator-crds.yaml 18 | - preset: prometheus 19 | - preset: grafana 20 | overrides: 21 | datasources: 22 | datasources.yaml: 23 | apiVersion: 1 24 | datasources: 25 | - name: Prometheus 26 | type: prometheus 27 | url: http://prometheus-server.prometheus.svc:9090 28 | isDefault: true 29 | 30 | tests: 31 | - type: kubernetes-objects-test 32 | values: 33 | checks: 34 | # Check that the Alloy instance has been reconciled 35 | - kind: DaemonSet 36 | name: alloy-test 37 | namespace: default 38 | - kind: ServiceMonitor 39 | name: alloy-test 40 | namespace: default 41 | - type: query-test 42 | values: 43 | tests: 44 | - env: 45 | PROMETHEUS_URL: http://prometheus-server.prometheus.svc:9090/api/v1/query 46 | queries: 47 | - query: alloy_build_info{service="alloy-test"} 48 | type: promql 49 | -------------------------------------------------------------------------------- /tests/integration/service-monitor/internal/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: internal-service-monitor 5 | 6 | subject: 7 | releaseName: alloy-operator 8 | path: ../../../../charts/alloy-operator 9 | valuesFile: values.yaml 10 | 11 | postInstall: 12 | files: [alloy.yaml] 13 | 14 | cluster: 15 | type: kind 16 | 17 | dependencies: 18 | - file: prom-operator-crds.yaml 19 | - preset: prometheus 20 | - preset: grafana 21 | overrides: 22 | datasources: 23 | datasources.yaml: 24 | apiVersion: 1 25 | datasources: 26 | - name: Prometheus 27 | type: prometheus 28 | url: http://prometheus-server.prometheus.svc:9090 29 | isDefault: true 30 | 31 | tests: 32 | - type: kubernetes-objects-test 33 | values: 34 | checks: 35 | # Check that the Alloy instance has been reconciled 36 | - kind: DaemonSet 37 | name: alloy-test 38 | namespace: default 39 | - kind: ServiceMonitor 40 | name: alloy-operator 41 | namespace: default 42 | - type: query-test 43 | values: 44 | tests: 45 | - env: 46 | PROMETHEUS_URL: http://prometheus-server.prometheus.svc:9090/api/v1/query 47 | queries: 48 | - query: helm_operator_build_info{job="alloy-operator"} 49 | type: promql 50 | -------------------------------------------------------------------------------- /operator/config/scorecard/patches/olm.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - olm-bundle-validation 7 | image: quay.io/operator-framework/scorecard-test:v1.39.1 8 | labels: 9 | suite: olm 10 | test: olm-bundle-validation-test 11 | - op: add 12 | path: /stages/0/tests/- 13 | value: 14 | entrypoint: 15 | - scorecard-test 16 | - olm-crds-have-validation 17 | image: quay.io/operator-framework/scorecard-test:v1.39.1 18 | labels: 19 | suite: olm 20 | test: olm-crds-have-validation-test 21 | - op: add 22 | path: /stages/0/tests/- 23 | value: 24 | entrypoint: 25 | - scorecard-test 26 | - olm-crds-have-resources 27 | image: quay.io/operator-framework/scorecard-test:v1.39.1 28 | labels: 29 | suite: olm 30 | test: olm-crds-have-resources-test 31 | - op: add 32 | path: /stages/0/tests/- 33 | value: 34 | entrypoint: 35 | - scorecard-test 36 | - olm-spec-descriptors 37 | image: quay.io/operator-framework/scorecard-test:v1.39.1 38 | labels: 39 | suite: olm 40 | test: olm-spec-descriptors-test 41 | - op: add 42 | path: /stages/0/tests/- 43 | value: 44 | entrypoint: 45 | - scorecard-test 46 | - olm-status-descriptors 47 | image: quay.io/operator-framework/scorecard-test:v1.39.1 48 | labels: 49 | suite: olm 50 | test: olm-status-descriptors-test 51 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/servicemonitor.yaml: -------------------------------------------------------------------------------- 1 | {{- $values := (mustMergeOverwrite .Values.alloy (or .Values.agent dict)) -}} 2 | {{- if and .Values.service.enabled .Values.serviceMonitor.enabled -}} 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | name: {{ include "alloy.fullname" . }} 7 | namespace: {{ include "alloy.namespace" . }} 8 | labels: 9 | {{- include "alloy.labels" . | nindent 4 }} 10 | app.kubernetes.io/component: metrics 11 | {{- with .Values.serviceMonitor.additionalLabels }} 12 | {{- toYaml . | nindent 4 }} 13 | {{- end }} 14 | spec: 15 | endpoints: 16 | - port: http-metrics 17 | scheme: {{ $values.listenScheme | lower }} 18 | honorLabels: true 19 | {{- if .Values.serviceMonitor.interval }} 20 | interval: {{ .Values.serviceMonitor.interval }} 21 | {{- end }} 22 | {{- if .Values.serviceMonitor.metricRelabelings }} 23 | metricRelabelings: 24 | {{ tpl (toYaml .Values.serviceMonitor.metricRelabelings | nindent 6) . }} 25 | {{- end }} 26 | {{- if .Values.serviceMonitor.relabelings }} 27 | relabelings: 28 | {{ tpl (toYaml .Values.serviceMonitor.relabelings | nindent 6) . }} 29 | {{- end }} 30 | {{- with .Values.serviceMonitor.tlsConfig }} 31 | tlsConfig: 32 | {{- toYaml . | nindent 6 }} 33 | {{- end }} 34 | selector: 35 | matchLabels: 36 | {{- include "alloy.selectorLabels" . | nindent 6 }} 37 | {{- end }} 38 | -------------------------------------------------------------------------------- /operator/config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: manager-role 5 | rules: 6 | ## 7 | ## Base operator rules 8 | ## 9 | # We need to get namespaces so the operator can read namespaces to ensure they exist 10 | - apiGroups: 11 | - "" 12 | resources: 13 | - namespaces 14 | verbs: 15 | - get 16 | # We need to manage Helm release secrets 17 | - apiGroups: 18 | - "" 19 | resources: 20 | - secrets 21 | verbs: 22 | - "*" 23 | # We need to create events on CRs about things happening during reconciliation 24 | - apiGroups: 25 | - "" 26 | resources: 27 | - events 28 | verbs: 29 | - create 30 | 31 | ## 32 | ## Rules for collectors.grafana.com/v1alpha1, Kind: Alloy 33 | ## 34 | - apiGroups: 35 | - collectors.grafana.com 36 | resources: 37 | - alloys 38 | - alloys/status 39 | - alloys/finalizers 40 | verbs: 41 | - create 42 | - delete 43 | - get 44 | - list 45 | - patch 46 | - update 47 | - watch 48 | - verbs: 49 | - "*" 50 | apiGroups: 51 | - "rbac.authorization.k8s.io" 52 | resources: 53 | - "clusterrolebindings" 54 | - "clusterroles" 55 | - verbs: 56 | - "*" 57 | apiGroups: 58 | - "" 59 | resources: 60 | - "configmaps" 61 | - "serviceaccounts" 62 | - "services" 63 | - verbs: 64 | - "*" 65 | apiGroups: 66 | - "apps" 67 | resources: 68 | - "daemonsets" 69 | - "deployments" 70 | - "statefulsets" 71 | 72 | # +kubebuilder:scaffold:rules 73 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Lint 3 | # yamllint disable-line rule:truthy 4 | on: 5 | push: 6 | branches: ["main"] 7 | 8 | pull_request: 9 | 10 | workflow_dispatch: 11 | 12 | permissions: {} 13 | 14 | jobs: 15 | 16 | actionlint: 17 | name: GitHub Actions 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 21 | with: 22 | persist-credentials: 'false' 23 | - uses: raven-actions/actionlint@963d4779ef039e217e5d0e6fd73ce9ab7764e493 # v2.1.0 24 | 25 | markdown: 26 | name: Markdown 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 30 | with: 31 | persist-credentials: 'false' 32 | - name: Lint Markdown files 33 | run: make lint-markdown 34 | 35 | yamllint: 36 | name: YAML 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 40 | with: 41 | persist-credentials: 'false' 42 | - name: Lint YAML files 43 | run: make lint-yaml 44 | 45 | zizmor: 46 | name: Zizmor 47 | runs-on: ubuntu-latest 48 | steps: 49 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 50 | with: 51 | persist-credentials: 'false' 52 | - name: Lint Zizmor files 53 | run: make lint-zizmor 54 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/pdb.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.controller.podDisruptionBudget.enabled }} 2 | {{- if eq .Values.controller.type "daemonset" }} 3 | {{- fail "PDBs (Pod Disruption Budgets) are not intended for DaemonSets. Please use a different controller type." }} 4 | {{- end }} 5 | 6 | {{- if and .Values.controller.podDisruptionBudget.minAvailable .Values.controller.podDisruptionBudget.maxUnavailable }} 7 | {{- fail "Only one of minAvailable or maxUnavailable should be defined for PodDisruptionBudget" }} 8 | {{- end }} 9 | 10 | {{- if not (or .Values.controller.podDisruptionBudget.minAvailable .Values.controller.podDisruptionBudget.maxUnavailable) }} 11 | {{- fail "Either minAvailable or maxUnavailable must be defined for PodDisruptionBudget" }} 12 | {{- end }} 13 | 14 | apiVersion: {{ include "alloy.controller.pdb.apiVersion" . }} 15 | kind: PodDisruptionBudget 16 | metadata: 17 | name: {{ include "alloy.fullname" . }} 18 | namespace: {{ include "alloy.namespace" . }} 19 | labels: 20 | {{- include "alloy.labels" . | nindent 4 }} 21 | spec: 22 | selector: 23 | matchLabels: 24 | {{- include "alloy.selectorLabels" . | nindent 6 }} 25 | {{- if .Values.controller.podDisruptionBudget.minAvailable }} 26 | minAvailable: {{ .Values.controller.podDisruptionBudget.minAvailable }} 27 | {{- end }} 28 | {{- if .Values.controller.podDisruptionBudget.maxUnavailable }} 29 | maxUnavailable: {{ .Values.controller.podDisruptionBudget.maxUnavailable }} 30 | {{- end }} 31 | {{- end }} 32 | -------------------------------------------------------------------------------- /tests/integration/pdb/alloy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: collectors.grafana.com/v1alpha1 3 | kind: Alloy 4 | metadata: 5 | name: alloy-test 6 | spec: 7 | alloy: 8 | configMap: 9 | content: |- 10 | // Self Metrics 11 | prometheus.exporter.self "default" {} 12 | 13 | prometheus.scrape "default" { 14 | targets = prometheus.exporter.self.default.targets 15 | forward_to = [prometheus.remote_write.local_prom.receiver] 16 | } 17 | 18 | // Kubernetes Cluster Metrics 19 | discovery.kubernetes "kube_state_metrics" { 20 | role = "pod" 21 | selectors { 22 | role = "pod" 23 | label = "app.kubernetes.io/name=kube-state-metrics" 24 | } 25 | namespaces { 26 | names = ["kube-state-metrics"] 27 | } 28 | } 29 | 30 | prometheus.scrape "kube_state_metrics" { 31 | targets = discovery.kubernetes.kube_state_metrics.targets 32 | forward_to = [prometheus.remote_write.local_prom.receiver] 33 | } 34 | 35 | // Delivery 36 | prometheus.remote_write "local_prom" { 37 | endpoint { 38 | url = "http://prometheus-server.prometheus.svc:9090/api/v1/write" 39 | } 40 | } 41 | resources: 42 | requests: 43 | cpu: 100m 44 | controller: 45 | type: statefulset 46 | replicas: 3 47 | podDisruptionBudget: 48 | enabled: true 49 | minAvailable: 1 50 | -------------------------------------------------------------------------------- /operator/config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | # Prometheus Monitor Service (Metrics) 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: ServiceMonitor 4 | metadata: 5 | labels: 6 | control-plane: controller-manager 7 | app.kubernetes.io/name: alloy-operator 8 | app.kubernetes.io/managed-by: kustomize 9 | name: controller-manager-metrics-monitor 10 | namespace: system 11 | spec: 12 | endpoints: 13 | - path: /metrics 14 | port: https # Ensure this is the name of the port that exposes HTTPS metrics 15 | scheme: https 16 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 17 | tlsConfig: 18 | # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables 19 | # certificate verification. This poses a significant security risk by making the system vulnerable to 20 | # man-in-the-middle attacks, where an attacker could intercept and manipulate the communication between 21 | # Prometheus and the monitored services. This could lead to unauthorized access to sensitive metrics data, 22 | # compromising the integrity and confidentiality of the information. 23 | # Please use the following options for secure configurations: 24 | # caFile: /etc/metrics-certs/ca.crt 25 | # certFile: /etc/metrics-certs/tls.crt 26 | # keyFile: /etc/metrics-certs/tls.key 27 | insecureSkipVerify: true 28 | selector: 29 | matchLabels: 30 | control-plane: controller-manager 31 | -------------------------------------------------------------------------------- /tests/integration/basic/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: basic 5 | 6 | subject: 7 | releaseName: alloy-operator 8 | path: ../../../charts/alloy-operator 9 | namespace: operator 10 | 11 | values: 12 | leaderElection: {enabled: false} 13 | 14 | postInstall: 15 | files: [alloy.yaml] 16 | 17 | cluster: 18 | type: kind 19 | 20 | dependencies: 21 | - preset: prometheus 22 | - preset: grafana 23 | overrides: 24 | datasources: 25 | datasources.yaml: 26 | apiVersion: 1 27 | datasources: 28 | - name: Prometheus 29 | type: prometheus 30 | url: http://prometheus-server.prometheus.svc:9090 31 | isDefault: true 32 | 33 | tests: 34 | - type: kubernetes-objects-test 35 | values: 36 | checks: 37 | # Check that leader election is not enabled 38 | - kind: Lease 39 | name: alloy-operator 40 | namespace: operator 41 | expect: 42 | count: 0 43 | # Check that the Alloy instance has been reconciled 44 | - kind: DaemonSet 45 | name: alloy-test 46 | namespace: default 47 | - type: query-test 48 | values: 49 | tests: 50 | - env: 51 | PROMETHEUS_URL: http://prometheus-server.prometheus.svc:9090/api/v1/query 52 | queries: 53 | - query: alloy_build_info{job="integrations/self"} 54 | type: promql 55 | -------------------------------------------------------------------------------- /tests/integration/operator-logs/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: operator-logs 5 | 6 | subject: 7 | releaseName: alloy-operator 8 | path: ../../../charts/alloy-operator 9 | namespace: operator 10 | values: 11 | extraArgs: 12 | - --zap-encoder=json 13 | - --zap-log-level=debug 14 | 15 | postInstall: 16 | files: [alloy.yaml] 17 | 18 | cluster: 19 | type: kind 20 | 21 | dependencies: 22 | - preset: loki 23 | - preset: grafana 24 | overrides: 25 | datasources: 26 | datasources.yaml: 27 | apiVersion: 1 28 | datasources: 29 | - name: Loki 30 | type: loki 31 | url: http://loki-gateway.loki.svc:8080 32 | basicAuth: true 33 | basicAuthUser: loki 34 | jsonData: 35 | httpHeaderName1: X-Scope-OrgID 36 | secureJsonData: 37 | basicAuthPassword: lokipassword 38 | httpHeaderValue1: "1" 39 | isDefault: true 40 | 41 | tests: 42 | - type: query-test 43 | values: 44 | tests: 45 | - env: 46 | LOKI_URL: http://loki.loki.svc:3100/loki/api/v1/query 47 | LOKI_TENANTID: 1 48 | LOKI_USER: loki 49 | LOKI_PASS: lokipassword 50 | queries: 51 | - query: count_over_time({job="loki.source.kubernetes.operator_pods"}[1h] | json | level = "debug") 52 | type: logql 53 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/cluster_service.yaml: -------------------------------------------------------------------------------- 1 | {{- $values := (mustMergeOverwrite .Values.alloy (or .Values.agent dict)) -}} 2 | {{- if $values.clustering.enabled -}} 3 | apiVersion: v1 4 | kind: Service 5 | metadata: 6 | name: {{ include "alloy.fullname" . }}-cluster 7 | namespace: {{ include "alloy.namespace" . }} 8 | labels: 9 | {{- include "alloy.labels" . | nindent 4 }} 10 | app.kubernetes.io/component: networking 11 | spec: 12 | type: ClusterIP 13 | clusterIP: 'None' 14 | publishNotReadyAddresses: true 15 | selector: 16 | {{- include "alloy.selectorLabels" . | nindent 4 }} 17 | ports: 18 | # Do not include the -metrics suffix in the port name, otherwise metrics 19 | # can be double-collected with the non-headless Service if it's also 20 | # enabled. 21 | # 22 | # This service should only be used for clustering, and not metric 23 | # collection. 24 | - name: {{ $values.clustering.portName }} 25 | port: {{ $values.listenPort }} 26 | targetPort: {{ $values.listenPort }} 27 | protocol: "TCP" 28 | {{- range $portMap := $values.extraPorts }} 29 | - name: {{ $portMap.name }} 30 | port: {{ $portMap.port }} 31 | targetPort: {{ $portMap.targetPort }} 32 | protocol: {{ coalesce $portMap.protocol "TCP" }} 33 | {{- if not (empty $portMap.appProtocol) }} 34 | # Useful for OpenShift clusters that want to expose Alloy ports externally 35 | appProtocol: {{ $portMap.appProtocol }} 36 | {{- end }} 37 | {{- end }} 38 | {{- end }} 39 | -------------------------------------------------------------------------------- /operator/config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: alloy-operator-system 3 | 4 | # Value of this field is prepended to the 5 | # names of all resources, e.g. a deployment named 6 | # "wordpress" becomes "alices-wordpress". 7 | # Note that it should also match with the prefix (text before '-') of the namespace 8 | # field above. 9 | namePrefix: alloy-operator- 10 | 11 | # Labels to add to all resources and selectors. 12 | #labels: 13 | #- includeSelectors: true 14 | # pairs: 15 | # someName: someValue 16 | 17 | resources: 18 | - ../crd 19 | - ../rbac 20 | - ../manager 21 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 22 | #- ../prometheus 23 | # [METRICS] Expose the controller manager metrics service. 24 | - metrics_service.yaml 25 | # [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy. 26 | # Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics. 27 | # Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will 28 | # be able to communicate with the Webhook Server. 29 | #- ../network-policy 30 | 31 | # Uncomment the patches line if you enable Metrics, and/or are using webhooks and cert-manager 32 | patches: 33 | # [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. 34 | # More info: https://book.kubebuilder.io/reference/metrics 35 | - path: manager_metrics_patch.yaml 36 | target: 37 | kind: Deployment 38 | -------------------------------------------------------------------------------- /charts/sample-parent-chart/templates/_crd-validation.tpl: -------------------------------------------------------------------------------- 1 | {{- define "crdValidation" -}} 2 | {{- if .Release.IsInstall }} 3 | {{- if not (.Capabilities.APIVersions.Has "collectors.grafana.com/v1alpha1/Alloy") }} 4 | {{- if not (index .Values "alloy-operator").crds.deployAlloyCRD }} 5 | {{- $msg := list "" "The Alloy Operator requires the Alloy CRD to be deployed." }} 6 | {{- $msg = append $msg "Please set:" }} 7 | {{- $msg = append $msg "alloy-operator:" }} 8 | {{- $msg = append $msg " crds:" }} 9 | {{- $msg = append $msg " deployAlloyCRD: true" }} 10 | {{- $msg = append $msg "" "Or install the Alloy CRD manually:" }} 11 | {{- $msg = append $msg (printf "kubectl apply -f https://github.com/grafana/alloy-operator/releases/download/%s/collectors.grafana.com_alloy.yaml" (index .Subcharts "alloy-operator").Chart.Version) }} 12 | {{- fail (join "\n" $msg) }} 13 | {{- end }} 14 | {{- end }} 15 | {{- end }} 16 | 17 | {{- if .Release.IsUpgrade }} 18 | {{- if not (.Capabilities.APIVersions.Has "collectors.grafana.com/v1alpha1/Alloy") }} 19 | {{- $msg := list "" "Upgrading to use the Alloy Operator requires the Alloy CRD to be deployed." }} 20 | {{- $msg = append $msg "Please install the Alloy CRD manually:" }} 21 | {{- $msg = append $msg (printf "kubectl apply -f https://github.com/grafana/alloy-operator/releases/download/%s/collectors.grafana.com_alloy.yaml" (index .Subcharts "alloy-operator").Chart.Version) }} 22 | {{- fail (join "\n" $msg) }} 23 | {{- end }} 24 | {{- end }} 25 | {{- end }} 26 | -------------------------------------------------------------------------------- /charts/alloy-operator/templates/rbac/leader-election.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.leaderElection.enabled .Values.rbac.create }} 2 | --- 3 | apiVersion: rbac.authorization.k8s.io/v1 4 | kind: Role 5 | metadata: 6 | name: {{ include "alloy-operator.fullname" . }}-leader-election-role 7 | namespace: {{ .Release.Namespace }} 8 | labels:{{ include "alloy-operator.labels" . | nindent 4 }} 9 | rules: 10 | - apiGroups: 11 | - "" 12 | resources: 13 | - configmaps 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - create 19 | - update 20 | - patch 21 | - delete 22 | - apiGroups: 23 | - coordination.k8s.io 24 | resources: 25 | - leases 26 | verbs: 27 | - get 28 | - list 29 | - watch 30 | - create 31 | - update 32 | - patch 33 | - delete 34 | - apiGroups: 35 | - "" 36 | resources: 37 | - events 38 | verbs: 39 | - create 40 | - patch 41 | --- 42 | apiVersion: rbac.authorization.k8s.io/v1 43 | kind: RoleBinding 44 | metadata: 45 | name: {{ include "alloy-operator.fullname" . }}-leader-election-rolebinding 46 | namespace: {{ .Release.Namespace }} 47 | labels:{{ include "alloy-operator.labels" . | nindent 4 }} 48 | roleRef: 49 | apiGroup: rbac.authorization.k8s.io 50 | kind: Role 51 | name: {{ include "alloy-operator.fullname" . }}-leader-election-role 52 | subjects: 53 | - kind: ServiceAccount 54 | name: {{ include "alloy-operator.serviceAccountName" . }} 55 | namespace: {{ .Release.Namespace }} 56 | {{- end }} 57 | -------------------------------------------------------------------------------- /charts/alloy-operator/templates/servicemonitor.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceMonitor.enabled -}} 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: ServiceMonitor 4 | metadata: 5 | name: {{ include "alloy-operator.fullname" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "alloy-operator.labels" . | nindent 4 }} 9 | app.kubernetes.io/component: operator 10 | {{- with .Values.serviceMonitor.additionalLabels }} 11 | {{- toYaml . | nindent 4 }} 12 | {{- end }} 13 | spec: 14 | jobLabel: {{ .Release.Name }} 15 | namespaceSelector: 16 | matchNames: 17 | - {{ .Release.Namespace }} 18 | selector: 19 | matchLabels: 20 | {{- include "alloy-operator.selectorLabels" . | nindent 6 }} 21 | endpoints: 22 | - port: metrics 23 | path: {{ .Values.serviceMonitor.telemetryPath }} 24 | {{- with .Values.serviceMonitor.interval }} 25 | interval: {{ . }} 26 | {{- end }} 27 | {{- with .Values.serviceMonitor.scrapeTimeout }} 28 | scrapeTimeout: {{ . }} 29 | {{- end }} 30 | {{- if .Values.serviceMonitor.metricRelabelings }} 31 | metricRelabelings: 32 | {{- toYaml .Values.serviceMonitor.metricRelabelings | nindent 8 }} 33 | {{- end }} 34 | {{- if .Values.serviceMonitor.relabelings }} 35 | relabelings: 36 | {{- toYaml .Values.serviceMonitor.relabelings | nindent 8 }} 37 | {{- end }} 38 | {{- if .Values.serviceMonitor.targetLabels }} 39 | targetLabels: 40 | {{- range .Values.serviceMonitor.targetLabels }} 41 | - {{ . }} 42 | {{- end }} 43 | {{- end }} 44 | {{- end }} 45 | -------------------------------------------------------------------------------- /tests/integration/hpa/alloy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: collectors.grafana.com/v1alpha1 3 | kind: Alloy 4 | metadata: 5 | name: alloy-test 6 | spec: 7 | alloy: 8 | configMap: 9 | content: |- 10 | // Self Metrics 11 | prometheus.exporter.self "default" {} 12 | 13 | prometheus.scrape "default" { 14 | targets = prometheus.exporter.self.default.targets 15 | forward_to = [prometheus.remote_write.local_prom.receiver] 16 | } 17 | 18 | // Kubernetes Cluster Metrics 19 | discovery.kubernetes "kube_state_metrics" { 20 | role = "pod" 21 | selectors { 22 | role = "pod" 23 | label = "app.kubernetes.io/name=kube-state-metrics" 24 | } 25 | namespaces { 26 | names = ["kube-state-metrics"] 27 | } 28 | } 29 | 30 | prometheus.scrape "kube_state_metrics" { 31 | targets = discovery.kubernetes.kube_state_metrics.targets 32 | forward_to = [prometheus.remote_write.local_prom.receiver] 33 | } 34 | 35 | // Delivery 36 | prometheus.remote_write "local_prom" { 37 | endpoint { 38 | url = "http://prometheus-server.prometheus.svc:9090/api/v1/write" 39 | } 40 | } 41 | resources: 42 | requests: 43 | cpu: 100m 44 | controller: 45 | type: deployment 46 | autoscaling: 47 | horizontal: 48 | enabled: true 49 | minReplicas: 2 50 | maxReplicas: 10 51 | targetCPUUtilizationPercentage: 50 52 | targetMemoryUtilizationPercentage: 0 53 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/service.yaml: -------------------------------------------------------------------------------- 1 | {{- $values := (mustMergeOverwrite .Values.alloy (or .Values.agent dict)) -}} 2 | {{- if .Values.service.enabled -}} 3 | apiVersion: v1 4 | kind: Service 5 | metadata: 6 | name: {{ include "alloy.fullname" . }} 7 | namespace: {{ include "alloy.namespace" . }} 8 | labels: 9 | {{- include "alloy.labels" . | nindent 4 }} 10 | app.kubernetes.io/component: networking 11 | {{- with .Values.service.annotations }} 12 | annotations: 13 | {{- toYaml . | nindent 4 }} 14 | {{- end }} 15 | spec: 16 | type: {{ .Values.service.type }} 17 | {{- if .Values.service.clusterIP }} 18 | clusterIP: {{ .Values.service.clusterIP }} 19 | {{- end }} 20 | selector: 21 | {{- include "alloy.selectorLabels" . | nindent 4 }} 22 | {{- if semverCompare ">=1.26-0" .Capabilities.KubeVersion.Version }} 23 | internalTrafficPolicy: {{.Values.service.internalTrafficPolicy}} 24 | {{- end }} 25 | ports: 26 | - name: http-metrics 27 | {{- if eq .Values.service.type "NodePort" }} 28 | nodePort: {{ .Values.service.nodePort }} 29 | {{- end }} 30 | port: {{ $values.listenPort }} 31 | targetPort: {{ $values.listenPort }} 32 | protocol: "TCP" 33 | {{- range $portMap := $values.extraPorts }} 34 | - name: {{ $portMap.name }} 35 | port: {{ $portMap.port }} 36 | targetPort: {{ $portMap.targetPort }} 37 | protocol: {{ coalesce $portMap.protocol "TCP" }} 38 | {{- if not (empty $portMap.appProtocol) }} 39 | # Useful for OpenShift clusters that want to expose Alloy ports externally 40 | appProtocol: {{ $portMap.appProtocol }} 41 | {{- end }} 42 | {{- end }} 43 | {{- end }} 44 | -------------------------------------------------------------------------------- /charts/alloy-operator/Makefile: -------------------------------------------------------------------------------- 1 | HAS_HELM_UNITTEST := $(shell helm plugin list | grep unittest 2> /dev/null) 2 | 3 | .PHONY: help 4 | help: ## Display this help. 5 | @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) 6 | 7 | HELM_TEMPLATE_FILES = $(shell find templates -type f) 8 | 9 | EXAMPLE_RELEASE_NAME := test 10 | EXAMPLE_VALUES_FILES := $(shell find docs/examples -name values.yaml) 11 | EXAMPLE_OUTPUT_FILES := $(EXAMPLE_VALUES_FILES:values.yaml=output.yaml) 12 | 13 | docs/examples/%/output.yaml: docs/examples/%/values.yaml values.yaml Chart.yaml $(HELM_TEMPLATE_FILES) 14 | helm template --namespace operator $(EXAMPLE_RELEASE_NAME) . -f $< > $@ 15 | 16 | README.md: README.md.gotmpl values.yaml Chart.yaml 17 | docker run --rm --platform linux/amd64 --volume $(shell pwd):/chart ghcr.io/grafana/helm-chart-toolbox-doc-generator --chart /chart > $@ 18 | 19 | .SECONDEXPANSION: 20 | values.schema.json: values.yaml $$(wildcard schema-mods/*) 21 | docker run --rm --platform linux/amd64 --volume $(shell pwd):/chart ghcr.io/grafana/helm-chart-toolbox-schema-generator --chart /chart > $@ 22 | 23 | .PHONY: build 24 | build: README.md values.schema.json $(EXAMPLE_OUTPUT_FILES) ## Builds documentation and examples 25 | 26 | .PHONY: clean 27 | clean: ## Deletes generated files 28 | rm -f README.md $(EXAMPLE_OUTPUT_FILES) 29 | 30 | .PHONY: test 31 | test: ## Runs tests 32 | ifdef HAS_HELM_UNITTEST 33 | helm unittest --failfast . 34 | else 35 | docker run --rm --volume $(shell pwd):/apps helmunittest/helm-unittest:3.17.0-0.7.1 --failfast . 36 | endif 37 | -------------------------------------------------------------------------------- /tests/integration/namespaced/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: namespaced 5 | 6 | subject: 7 | releaseName: alloy-operator 8 | path: ../../../charts/alloy-operator 9 | namespace: operator 10 | valuesFile: values.yaml 11 | 12 | postInstall: 13 | files: [alloys.yaml] 14 | 15 | cluster: 16 | type: kind 17 | 18 | dependencies: 19 | - file: namespaces.yaml 20 | - preset: prometheus 21 | - preset: grafana 22 | overrides: 23 | datasources: 24 | datasources.yaml: 25 | apiVersion: 1 26 | datasources: 27 | - name: Prometheus 28 | type: prometheus 29 | url: http://prometheus-server.prometheus.svc:9090 30 | isDefault: true 31 | 32 | tests: 33 | - type: kubernetes-objects-test 34 | values: 35 | checks: 36 | # Check that the Alloy instance has been reconciled 37 | - kind: DaemonSet 38 | name: alloy-alpha 39 | namespace: alpha 40 | # Ensure that the Alloy instance in the non-monitored namespace has not been reconciled 41 | - kind: DaemonSet 42 | name: alloy-bravo 43 | namespace: bravo 44 | expect: 45 | count: 0 46 | - type: query-test 47 | values: 48 | tests: 49 | - env: 50 | PROMETHEUS_URL: http://prometheus-server.prometheus.svc:9090/api/v1/query 51 | queries: 52 | - query: alloy_build_info{job="integrations/self", alloy_instance="alpha"} 53 | type: promql 54 | - query: count(alloy_build_info{job="integrations/self", alloy_instance="bravo"}) or vector(0) 55 | type: promql 56 | expect: 57 | value: 0 58 | -------------------------------------------------------------------------------- /tests/integration/own-namespace/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: own-namespace 5 | 6 | subject: 7 | releaseName: alloy-operator 8 | path: ../../../charts/alloy-operator 9 | namespace: operator 10 | valuesFile: values.yaml 11 | 12 | postInstall: 13 | files: [alloys.yaml] 14 | 15 | cluster: 16 | type: kind 17 | 18 | dependencies: 19 | - file: namespaces.yaml 20 | - preset: prometheus 21 | - preset: grafana 22 | overrides: 23 | datasources: 24 | datasources.yaml: 25 | apiVersion: 1 26 | datasources: 27 | - name: Prometheus 28 | type: prometheus 29 | url: http://prometheus-server.prometheus.svc:9090 30 | isDefault: true 31 | 32 | tests: 33 | - type: kubernetes-objects-test 34 | values: 35 | checks: 36 | # Check that the Alloy instance has been reconciled 37 | - kind: DaemonSet 38 | name: alloy-test 39 | namespace: operator 40 | # Ensure that the Alloy instance in the non-monitored namespace has not been reconciled 41 | - kind: DaemonSet 42 | name: alloy-bravo 43 | namespace: bravo 44 | expect: 45 | count: 0 46 | - type: query-test 47 | values: 48 | tests: 49 | - env: 50 | PROMETHEUS_URL: http://prometheus-server.prometheus.svc:9090/api/v1/query 51 | queries: 52 | - query: alloy_build_info{job="integrations/self", alloy_instance="test"} 53 | type: promql 54 | - query: count(alloy_build_info{job="integrations/self", alloy_instance="alpha"}) or vector(0) 55 | type: promql 56 | expect: 57 | value: 0 58 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/vpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Capabilities.APIVersions.Has "autoscaling.k8s.io/v1" -}} 2 | {{- if .Values.controller.autoscaling.vertical.enabled -}} 3 | apiVersion: autoscaling.k8s.io/v1 4 | kind: VerticalPodAutoscaler 5 | metadata: 6 | name: {{ include "alloy.fullname" . }} 7 | labels: 8 | {{- include "alloy.labels" . | nindent 4 }} 9 | app.kubernetes.io/component: availability 10 | spec: 11 | {{- with .Values.controller.autoscaling.vertical }} 12 | {{- with .recommenders }} 13 | recommenders: 14 | {{- toYaml . | nindent 4 }} 15 | {{- end }} 16 | {{- with .resourcePolicy }} 17 | resourcePolicy: 18 | {{- with .containerPolicies }} 19 | containerPolicies: 20 | {{- range . }} 21 | - containerName: {{ .containerName }} 22 | {{- with .controlledResources }} 23 | controlledResources: 24 | {{- toYaml . | nindent 8 }} 25 | {{- end }} 26 | {{- if .controlledValues }} 27 | controlledValues: {{ .controlledValues }} 28 | {{- end }} 29 | {{- with .maxAllowed }} 30 | maxAllowed: 31 | {{- toYaml . | nindent 8 }} 32 | {{- end }} 33 | {{- with .minAllowed }} 34 | minAllowed: 35 | {{- toYaml . | nindent 8 }} 36 | {{- end }} 37 | {{- end }} 38 | {{- end }} 39 | {{- end }} 40 | {{- with .updatePolicy }} 41 | updatePolicy: 42 | {{- toYaml . | nindent 4 }} 43 | {{- end }} 44 | {{- end }} 45 | targetRef: 46 | apiVersion: apps/v1 47 | {{- if eq .Values.controller.type "deployment" }} 48 | kind: Deployment 49 | {{- else if eq .Values.controller.type "statefulset" }} 50 | kind: StatefulSet 51 | {{- else }} 52 | kind: DaemonSet 53 | {{- end }} 54 | name: {{ include "alloy.fullname" . }} 55 | {{- end }} 56 | {{- end }} 57 | -------------------------------------------------------------------------------- /tests/integration/namespaced/alloys.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: collectors.grafana.com/v1alpha1 3 | kind: Alloy 4 | metadata: 5 | name: alloy-alpha 6 | namespace: alpha 7 | spec: 8 | alloy: 9 | configMap: 10 | content: |- 11 | prometheus.exporter.self "default" {} 12 | 13 | prometheus.scrape "default" { 14 | targets = prometheus.exporter.self.default.targets 15 | forward_to = [prometheus.relabel.default.receiver] 16 | } 17 | 18 | prometheus.relabel "default" { 19 | rule { 20 | replacement = "alpha" 21 | target_label = "alloy_instance" 22 | } 23 | forward_to = [prometheus.remote_write.local_prom.receiver] 24 | } 25 | 26 | prometheus.remote_write "local_prom" { 27 | endpoint { 28 | url = "http://prometheus-server.prometheus.svc:9090/api/v1/write" 29 | } 30 | } 31 | --- 32 | apiVersion: collectors.grafana.com/v1alpha1 33 | kind: Alloy 34 | metadata: 35 | name: alloy-bravo 36 | namespace: bravo 37 | spec: 38 | alloy: 39 | configMap: 40 | content: |- 41 | prometheus.exporter.self "default" {} 42 | 43 | prometheus.scrape "default" { 44 | targets = prometheus.exporter.self.default.targets 45 | forward_to = [prometheus.relabel.default.receiver] 46 | } 47 | 48 | prometheus.relabel "default" { 49 | rule { 50 | replacement = "bravo" 51 | target_label = "alloy_instance" 52 | } 53 | forward_to = [prometheus.remote_write.local_prom.receiver] 54 | } 55 | 56 | prometheus.remote_write "local_prom" { 57 | endpoint { 58 | url = "http://prometheus-server.prometheus.svc:9090/api/v1/write" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/integration/pdb/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: pdb 5 | 6 | subject: 7 | releaseName: alloy-operator 8 | path: ../../../charts/alloy-operator 9 | namespace: operator 10 | 11 | postInstall: 12 | files: [alloy.yaml] 13 | 14 | cluster: 15 | type: kind 16 | 17 | dependencies: 18 | - file: kube-state-metrics.yaml 19 | - preset: prometheus 20 | - preset: grafana 21 | overrides: 22 | datasources: 23 | datasources.yaml: 24 | apiVersion: 1 25 | datasources: 26 | - name: Prometheus 27 | type: prometheus 28 | url: http://prometheus-server.prometheus.svc:9090 29 | isDefault: true 30 | 31 | tests: 32 | - type: kubernetes-objects-test 33 | values: 34 | checks: 35 | # Check that the Alloy instance has been reconciled 36 | - kind: StatefulSet 37 | name: alloy-test 38 | namespace: default 39 | - kind: PodDisruptionBudget 40 | name: alloy-test 41 | namespace: default 42 | - type: query-test 43 | values: 44 | tests: 45 | - env: 46 | PROMETHEUS_URL: http://prometheus-server.prometheus.svc:9090/api/v1/query 47 | queries: 48 | - query: alloy_build_info{job="integrations/self"} 49 | type: promql 50 | 51 | - query: kube_statefulset_replicas{statefulset="alloy-test"} 52 | type: promql 53 | expect: 54 | operator: == 55 | value: 3 56 | 57 | - query: kube_poddisruptionbudget_status_pod_disruptions_allowed{poddisruptionbudget="alloy-test"} 58 | type: promql 59 | expect: 60 | operator: == 61 | value: 2 62 | -------------------------------------------------------------------------------- /tests/integration/own-namespace/alloys.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: collectors.grafana.com/v1alpha1 3 | kind: Alloy 4 | metadata: 5 | name: alloy-alpha 6 | namespace: alpha 7 | spec: 8 | alloy: 9 | configMap: 10 | content: |- 11 | prometheus.exporter.self "default" {} 12 | 13 | prometheus.scrape "default" { 14 | targets = prometheus.exporter.self.default.targets 15 | forward_to = [prometheus.relabel.default.receiver] 16 | } 17 | 18 | prometheus.relabel "default" { 19 | rule { 20 | replacement = "alpha" 21 | target_label = "alloy_instance" 22 | } 23 | forward_to = [prometheus.remote_write.local_prom.receiver] 24 | } 25 | 26 | prometheus.remote_write "local_prom" { 27 | endpoint { 28 | url = "http://prometheus-server.prometheus.svc:9090/api/v1/write" 29 | } 30 | } 31 | --- 32 | apiVersion: collectors.grafana.com/v1alpha1 33 | kind: Alloy 34 | metadata: 35 | name: alloy-test 36 | namespace: operator 37 | spec: 38 | alloy: 39 | configMap: 40 | content: |- 41 | prometheus.exporter.self "default" {} 42 | 43 | prometheus.scrape "default" { 44 | targets = prometheus.exporter.self.default.targets 45 | forward_to = [prometheus.relabel.default.receiver] 46 | } 47 | 48 | prometheus.relabel "default" { 49 | rule { 50 | replacement = "test" 51 | target_label = "alloy_instance" 52 | } 53 | forward_to = [prometheus.remote_write.local_prom.receiver] 54 | } 55 | 56 | prometheus.remote_write "local_prom" { 57 | endpoint { 58 | url = "http://prometheus-server.prometheus.svc:9090/api/v1/write" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/integration/hpa/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: hpa 5 | 6 | subject: 7 | releaseName: alloy-operator 8 | path: ../../../charts/alloy-operator 9 | namespace: operator 10 | 11 | postInstall: 12 | files: [alloy.yaml] 13 | 14 | cluster: 15 | type: kind 16 | 17 | dependencies: 18 | - file: kube-state-metrics.yaml 19 | - preset: prometheus 20 | - preset: grafana 21 | overrides: 22 | datasources: 23 | datasources.yaml: 24 | apiVersion: 1 25 | datasources: 26 | - name: Prometheus 27 | type: prometheus 28 | url: http://prometheus-server.prometheus.svc:9090 29 | isDefault: true 30 | 31 | tests: 32 | - type: kubernetes-objects-test 33 | values: 34 | checks: 35 | # Check that the Alloy instance has been reconciled 36 | - kind: Deployment 37 | name: alloy-test 38 | namespace: default 39 | - kind: HorizontalPodAutoscaler 40 | name: alloy-test 41 | namespace: default 42 | - type: query-test 43 | values: 44 | tests: 45 | - env: 46 | PROMETHEUS_URL: http://prometheus-server.prometheus.svc:9090/api/v1/query 47 | queries: 48 | - query: alloy_build_info{job="integrations/self"} 49 | type: promql 50 | 51 | - query: kube_deployment_status_replicas{deployment="alloy-test"} 52 | type: promql 53 | expect: 54 | operator: <= 55 | value: 2 56 | 57 | - query: kube_horizontalpodautoscaler_status_current_replicas{horizontalpodautoscaler="alloy-test"} 58 | type: promql 59 | expect: 60 | operator: <= 61 | value: 2 62 | -------------------------------------------------------------------------------- /tests/integration/vpa/alloy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: collectors.grafana.com/v1alpha1 3 | kind: Alloy 4 | metadata: 5 | name: alloy-test 6 | spec: 7 | alloy: 8 | configMap: 9 | content: |- 10 | // Self Metrics 11 | prometheus.exporter.self "default" {} 12 | 13 | prometheus.scrape "default" { 14 | targets = prometheus.exporter.self.default.targets 15 | forward_to = [prometheus.remote_write.local_prom.receiver] 16 | } 17 | 18 | // Kubernetes Cluster Metrics 19 | discovery.kubernetes "kube_state_metrics" { 20 | role = "pod" 21 | selectors { 22 | role = "pod" 23 | label = "app.kubernetes.io/name=kube-state-metrics" 24 | } 25 | namespaces { 26 | names = ["kube-state-metrics"] 27 | } 28 | } 29 | 30 | prometheus.scrape "kube_state_metrics" { 31 | targets = discovery.kubernetes.kube_state_metrics.targets 32 | forward_to = [prometheus.remote_write.local_prom.receiver] 33 | } 34 | 35 | // Delivery 36 | prometheus.remote_write "local_prom" { 37 | endpoint { 38 | url = "http://prometheus-server.prometheus.svc:9090/api/v1/write" 39 | } 40 | } 41 | resources: 42 | requests: 43 | cpu: 1m 44 | limits: 45 | cpu: 1m 46 | controller: 47 | type: deployment 48 | autoscaling: 49 | vertical: 50 | enabled: true 51 | resourcePolicy: 52 | containerPolicies: 53 | - containerName: alloy 54 | controlledResources: 55 | - cpu 56 | controlledValues: "RequestsAndLimits" 57 | maxAllowed: 58 | cpu: 1 59 | minAllowed: 60 | cpu: 50m 61 | -------------------------------------------------------------------------------- /charts/alloy-operator/tests/rbac-validation_test.yaml: -------------------------------------------------------------------------------- 1 | # yamllint disable rule:document-start rule:line-length rule:trailing-spaces 2 | suite: Test - Alloy Operator - RBAC Validation 3 | templates: 4 | - rbac/validation.yaml 5 | tests: 6 | - it: deploys fine by default 7 | asserts: 8 | - notFailedTemplate: {} 9 | 10 | - it: displays an error if clusterRoles are not allowed, but namespaces are not set 11 | set: 12 | rbac: 13 | createClusterRoles: false 14 | asserts: 15 | - failedTemplate: 16 | errorMessage: |- 17 | The Alloy Operator requires namespaces to be set when not using ClusterRoles. 18 | Please set the list of namespaces to manage Alloy instances: 19 | alloy-operator: 20 | namespaces: ["namespace1", "namespace2"] 21 | 22 | Or, only allow management of Alloy instances within the same namespace (i.e. "NAMESPACE") 23 | alloy-operator: 24 | ownNamespaceOnly: true 25 | 26 | Or, allow the creation of ClusterRoles and ClusterRoleBindings: 27 | alloy-operator: 28 | rbac: 29 | createClusterRoles: true 30 | 31 | - it: deploys fine when namespaces are set 32 | set: 33 | namespaces: ["alpha", "bravo"] 34 | rbac: 35 | createClusterRoles: false 36 | asserts: 37 | - notFailedTemplate: {} 38 | 39 | - it: deploys fine when ownNamespaceOnly is set 40 | set: 41 | ownNamespaceOnly: true 42 | rbac: 43 | createClusterRoles: false 44 | asserts: 45 | - notFailedTemplate: {} 46 | 47 | - it: deploys fine if rbac objects are not being created 48 | set: 49 | ownNamespaceOnly: true 50 | rbac: 51 | create: false 52 | asserts: 53 | - notFailedTemplate: {} 54 | -------------------------------------------------------------------------------- /charts/alloy-crd/crds/collectors.grafana.com_alloy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: alloys.collectors.grafana.com 5 | spec: 6 | group: collectors.grafana.com 7 | names: 8 | kind: Alloy 9 | listKind: AlloyList 10 | plural: alloys 11 | singular: alloy 12 | scope: Namespaced 13 | versions: 14 | - name: v1alpha1 15 | schema: 16 | openAPIV3Schema: 17 | description: Alloy is the Schema for the alloys API 18 | properties: 19 | apiVersion: 20 | description: 'APIVersion defines the versioned schema of this representation 21 | of an object. Servers should convert recognized schemas to the latest 22 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 23 | type: string 24 | kind: 25 | description: 'Kind is a string value representing the REST resource this 26 | object represents. Servers may infer this from the endpoint the client 27 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 28 | type: string 29 | metadata: 30 | type: object 31 | spec: 32 | description: Spec defines the desired state of Alloy 33 | type: object 34 | x-kubernetes-preserve-unknown-fields: true 35 | status: 36 | description: Status defines the observed state of Alloy 37 | type: object 38 | x-kubernetes-preserve-unknown-fields: true 39 | type: object 40 | served: true 41 | storage: true 42 | subresources: 43 | status: {} 44 | -------------------------------------------------------------------------------- /operator/config/crd/bases/collectors.grafana.com_alloys.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: alloys.collectors.grafana.com 6 | spec: 7 | group: collectors.grafana.com 8 | names: 9 | kind: Alloy 10 | listKind: AlloyList 11 | plural: alloys 12 | singular: alloy 13 | scope: Namespaced 14 | versions: 15 | - name: v1alpha1 16 | schema: 17 | openAPIV3Schema: 18 | description: Alloy is the Schema for the alloys API 19 | properties: 20 | apiVersion: 21 | description: 'APIVersion defines the versioned schema of this representation 22 | of an object. Servers should convert recognized schemas to the latest 23 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 24 | type: string 25 | kind: 26 | description: 'Kind is a string value representing the REST resource this 27 | object represents. Servers may infer this from the endpoint the client 28 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 29 | type: string 30 | metadata: 31 | type: object 32 | spec: 33 | description: Spec defines the desired state of Alloy 34 | type: object 35 | x-kubernetes-preserve-unknown-fields: true 36 | status: 37 | description: Status defines the observed state of Alloy 38 | type: object 39 | x-kubernetes-preserve-unknown-fields: true 40 | type: object 41 | served: true 42 | storage: true 43 | subresources: 44 | status: {} 45 | -------------------------------------------------------------------------------- /tests/integration/statefulset-with-wal/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: statefulset-with-wal 5 | 6 | subject: 7 | releaseName: alloy-operator 8 | path: ../../../charts/alloy-operator 9 | namespace: operator 10 | 11 | postInstall: 12 | files: [alloy.yaml] 13 | 14 | cluster: 15 | type: kind 16 | 17 | dependencies: 18 | - preset: prometheus 19 | - preset: grafana 20 | overrides: 21 | datasources: 22 | datasources.yaml: 23 | apiVersion: 1 24 | datasources: 25 | - name: Prometheus 26 | type: prometheus 27 | url: http://prometheus-server.prometheus.svc:9090 28 | isDefault: true 29 | 30 | tests: 31 | - type: kubernetes-objects-test 32 | values: 33 | checks: 34 | # Check that the Alloy instance has been reconciled 35 | - kind: StatefulSet 36 | name: alloy-metrics 37 | namespace: default 38 | - type: query-test 39 | values: 40 | tests: 41 | - env: 42 | CLUSTER: statefulset-with-wal 43 | PROMETHEUS_URL: http://prometheus-server.prometheus.svc:9090/api/v1/query 44 | queries: 45 | # DPM check to ensure clustering is working 46 | - query: avg(count_over_time(scrape_samples_scraped{cluster="$CLUSTER"}[1m])) 47 | type: promql 48 | expect: 49 | value: 1 50 | operator: == 51 | 52 | # Kubernetes service discovery is working 53 | - query: kubernetes_build_info{cluster="$CLUSTER"} 54 | type: promql 55 | 56 | # PVC and WAL are working 57 | - query: prometheus_remote_write_wal_storage_active_series{cluster="$CLUSTER"} 58 | type: promql 59 | expect: 60 | value: 0 61 | operator: "<" 62 | -------------------------------------------------------------------------------- /tests/platform/remote-config/alloy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: collectors.grafana.com/v1alpha1 3 | kind: Alloy 4 | metadata: 5 | name: alloy-client 6 | namespace: default 7 | spec: 8 | controller: 9 | type: deployment 10 | alloy: 11 | extraEnv: 12 | - name: GCLOUD_RW_API_KEY 13 | valueFrom: 14 | secretKeyRef: 15 | key: GRAFANA_CLOUD_FLEET_MGMT_TOKEN 16 | name: grafana-cloud-credentials 17 | - name: CLUSTER_NAME 18 | valueFrom: 19 | configMapKeyRef: 20 | key: clusterName 21 | name: test-parameters 22 | - name: NAMESPACE 23 | valueFrom: 24 | fieldRef: 25 | fieldPath: metadata.namespace 26 | - name: POD_NAME 27 | valueFrom: 28 | fieldRef: 29 | fieldPath: metadata.name 30 | - name: GCLOUD_FM_COLLECTOR_ID 31 | value: alloy-operator-$(CLUSTER_NAME)-$(NAMESPACE)-$(POD_NAME) 32 | configMap: 33 | content: |- 34 | remote.kubernetes.secret "creds" { 35 | name = "grafana-cloud-credentials" 36 | namespace = "default" 37 | } 38 | 39 | remotecfg { 40 | id = sys.env("GCLOUD_FM_COLLECTOR_ID") 41 | url = "https://fleet-management-prod-008.grafana.net" 42 | basic_auth { 43 | username = convert.nonsensitive(remote.kubernetes.secret.creds.data["GRAFANA_CLOUD_FLEET_MGMT_USER"]) 44 | password = remote.kubernetes.secret.creds.data["GRAFANA_CLOUD_FLEET_MGMT_TOKEN"] 45 | } 46 | poll_frequency = "5m" 47 | attributes = { 48 | "platform" = "kubernetes", 49 | "source" = "alloy-operator", 50 | "cluster" = sys.env("CLUSTER_NAME"), 51 | "namespace" = sys.env("NAMESPACE"), 52 | "workloadName" = sys.env("POD_NAME"), 53 | "workloadType" = "deployment", 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/integration/beyla-and-unix/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: beyla-and-unix 5 | 6 | subject: 7 | releaseName: alloy-operator 8 | path: ../../../charts/alloy-operator 9 | namespace: operator 10 | 11 | postInstall: 12 | files: [alloy.yaml] 13 | 14 | cluster: 15 | type: minikube 16 | 17 | dependencies: 18 | - preset: prometheus 19 | - preset: tempo 20 | - preset: grafana 21 | overrides: 22 | datasources: 23 | datasources.yaml: 24 | apiVersion: 1 25 | datasources: 26 | - name: Prometheus 27 | type: prometheus 28 | url: http://prometheus-server.prometheus.svc:9090 29 | isDefault: true 30 | - name: Tempo 31 | type: tempo 32 | url: http://tempo.tempo.svc:3200 33 | 34 | tests: 35 | - type: kubernetes-objects-test 36 | values: 37 | checks: 38 | # Check that the Alloy instance has been reconciled 39 | - kind: DaemonSet 40 | name: alloy-test 41 | namespace: default 42 | - type: query-test 43 | values: 44 | tests: 45 | - env: 46 | PROMETHEUS_URL: http://prometheus-server.prometheus.svc:9090/api/v1/query 47 | TEMPO_URL: http://tempo.tempo.svc:3200/api/search 48 | queries: 49 | # Unix node metrics 50 | - query: node_cpu_seconds_total{job="integrations/unix"} 51 | type: promql 52 | 53 | # Beyla internal metrics 54 | - query: beyla_internal_build_info{job="beyla"} 55 | type: promql 56 | 57 | # Beyla application metrics 58 | - query: '{k8s_deployment_name="alloy-operator", job="operator/alloy-operator", source="beyla"}' 59 | type: promql 60 | 61 | # Beyla application traces 62 | - query: '{resource.k8s.deployment.name="alloy-operator"}' 63 | type: traceql 64 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/rbac.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.rbac.create }} 2 | {{- if .Values.rbac.namespaces }} 3 | {{- range $namespace := .Values.rbac.namespaces }} 4 | --- 5 | apiVersion: rbac.authorization.k8s.io/v1 6 | kind: Role 7 | metadata: 8 | name: {{ include "alloy.fullname" $ }} 9 | namespace: {{ $namespace }} 10 | labels: 11 | {{- include "alloy.labels" $ | nindent 4 }} 12 | app.kubernetes.io/component: rbac 13 | rules: 14 | {{- $.Values.rbac.rules | toYaml | nindent 2 }} 15 | --- 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: RoleBinding 18 | metadata: 19 | name: {{ include "alloy.fullname" $ }} 20 | namespace: {{ $namespace }} 21 | labels: 22 | {{- include "alloy.labels" $ | nindent 4 }} 23 | app.kubernetes.io/component: rbac 24 | roleRef: 25 | apiGroup: rbac.authorization.k8s.io 26 | kind: Role 27 | name: {{ include "alloy.fullname" $ }} 28 | subjects: 29 | - kind: ServiceAccount 30 | name: {{ include "alloy.serviceAccountName" $ }} 31 | namespace: {{ include "alloy.namespace" $ }} 32 | {{- end }} 33 | {{- else }} 34 | --- 35 | apiVersion: rbac.authorization.k8s.io/v1 36 | kind: ClusterRole 37 | metadata: 38 | name: {{ include "alloy.fullname" . }} 39 | labels: 40 | {{- include "alloy.labels" . | nindent 4 }} 41 | app.kubernetes.io/component: rbac 42 | rules: 43 | {{- .Values.rbac.rules | toYaml | nindent 2 }} 44 | {{- .Values.rbac.clusterRules | toYaml | nindent 2 }} 45 | --- 46 | apiVersion: rbac.authorization.k8s.io/v1 47 | kind: ClusterRoleBinding 48 | metadata: 49 | name: {{ include "alloy.fullname" . }} 50 | labels: 51 | {{- include "alloy.labels" . | nindent 4 }} 52 | app.kubernetes.io/component: rbac 53 | roleRef: 54 | apiGroup: rbac.authorization.k8s.io 55 | kind: ClusterRole 56 | name: {{ include "alloy.fullname" . }} 57 | subjects: 58 | - kind: ServiceAccount 59 | name: {{ include "alloy.serviceAccountName" . }} 60 | namespace: {{ include "alloy.namespace" . }} 61 | {{- end }} 62 | {{- end }} 63 | -------------------------------------------------------------------------------- /tests/integration/beyla-and-unix/alloy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: collectors.grafana.com/v1alpha1 3 | kind: Alloy 4 | metadata: 5 | name: alloy-test 6 | spec: 7 | controller: 8 | type: daemonset 9 | hostNetwork: true 10 | hostPID: true 11 | dnsPolicy: ClusterFirstWithHostNet 12 | alloy: 13 | securityContext: 14 | capabilities: 15 | add: [CAP_SYS_ADMIN, CAP_SYS_PTRACE] 16 | stabilityLevel: public-preview 17 | configMap: 18 | content: |- 19 | prometheus.exporter.unix "node_metrics" {} 20 | 21 | prometheus.scrape "default" { 22 | targets = prometheus.exporter.unix.node_metrics.targets 23 | forward_to = [prometheus.remote_write.local_prom.receiver] 24 | } 25 | 26 | beyla.ebpf "default" { 27 | attributes { 28 | kubernetes { 29 | enable = "true" 30 | cluster_name = "beyla-and-unix-cluster" 31 | } 32 | } 33 | 34 | discovery { 35 | services { 36 | kubernetes { 37 | deployment_name = "alloy-operator" 38 | } 39 | } 40 | } 41 | 42 | metrics { 43 | features = ["application"] 44 | } 45 | 46 | output { 47 | traces = [otelcol.exporter.otlp.local_tempo.input] 48 | } 49 | } 50 | 51 | prometheus.scrape "beyla" { 52 | targets = beyla.ebpf.default.targets 53 | honor_labels = true 54 | forward_to = [prometheus.remote_write.local_prom.receiver] 55 | } 56 | 57 | prometheus.remote_write "local_prom" { 58 | endpoint { 59 | url = "http://prometheus-server.prometheus.svc:9090/api/v1/write" 60 | } 61 | } 62 | 63 | otelcol.exporter.otlp "local_tempo" { 64 | client { 65 | endpoint = "tempo.tempo.svc:4317" 66 | tls { 67 | insecure = true 68 | insecure_skip_verify = true 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/controllers/statefulset.yaml: -------------------------------------------------------------------------------- 1 | {{- if eq .Values.controller.type "statefulset" }} 2 | {{- if .Values.enableStatefulSetAutoDeletePVC }} 3 | {{- fail "Value 'enableStatefulSetAutoDeletePVC' should be nested inside 'controller' options." }} 4 | {{- end }} 5 | apiVersion: apps/v1 6 | kind: StatefulSet 7 | metadata: 8 | name: {{ include "alloy.fullname" . }} 9 | namespace: {{ include "alloy.namespace" . }} 10 | labels: 11 | {{- include "alloy.labels" . | nindent 4 }} 12 | {{- with .Values.controller.extraLabels }} 13 | {{- toYaml . | nindent 4 }} 14 | {{- end }} 15 | {{- with .Values.controller.extraAnnotations }} 16 | annotations: 17 | {{- toYaml . | nindent 4 }} 18 | {{- end }} 19 | spec: 20 | {{- if (and (not .Values.controller.autoscaling.enabled) (not .Values.controller.autoscaling.horizontal.enabled)) }} 21 | replicas: {{ .Values.controller.replicas }} 22 | {{- end }} 23 | {{- if .Values.controller.parallelRollout }} 24 | podManagementPolicy: Parallel 25 | {{- end }} 26 | {{- if ge (int .Capabilities.KubeVersion.Minor) 22 }} 27 | minReadySeconds: {{ .Values.controller.minReadySeconds }} 28 | {{- end }} 29 | serviceName: {{ include "alloy.fullname" . }} 30 | selector: 31 | matchLabels: 32 | {{- include "alloy.selectorLabels" . | nindent 6 }} 33 | template: 34 | {{- include "alloy.pod-template" . | nindent 4 }} 35 | {{- with .Values.controller.updateStrategy }} 36 | updateStrategy: 37 | {{- toYaml . | nindent 4 }} 38 | {{- end }} 39 | {{- with .Values.controller.volumeClaimTemplates }} 40 | volumeClaimTemplates: 41 | {{- range . }} 42 | - {{ toYaml . | nindent 6 }} 43 | {{- end }} 44 | {{- end }} 45 | {{- if and (semverCompare ">= 1.23-0" .Capabilities.KubeVersion.Version) (.Values.controller.enableStatefulSetAutoDeletePVC) }} 46 | {{- /* 47 | Data on the read nodes is easy to replace, so we want to always delete PVCs to make 48 | operation easier, and will rely on re-fetching data when needed. 49 | */}} 50 | persistentVolumeClaimRetentionPolicy: 51 | whenDeleted: Delete 52 | whenScaled: Delete 53 | {{- end }} 54 | {{- end }} 55 | -------------------------------------------------------------------------------- /tests/integration/vpa/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: vpa 5 | 6 | subject: 7 | releaseName: alloy-operator 8 | path: ../../../charts/alloy-operator 9 | namespace: operator 10 | 11 | postInstall: 12 | files: [alloy.yaml] 13 | 14 | cluster: 15 | type: kind 16 | 17 | dependencies: 18 | - directory: dependencies 19 | - preset: prometheus 20 | - preset: grafana 21 | overrides: 22 | datasources: 23 | datasources.yaml: 24 | apiVersion: 1 25 | datasources: 26 | - name: Prometheus 27 | type: prometheus 28 | url: http://prometheus-server.prometheus.svc:9090 29 | isDefault: true 30 | 31 | tests: 32 | - type: kubernetes-objects-test 33 | values: 34 | checks: 35 | # Check that the Alloy instance has been reconciled 36 | - kind: Deployment 37 | name: alloy-test 38 | namespace: default 39 | - kind: VerticalPodAutoscaler 40 | name: alloy-test 41 | namespace: default 42 | - type: query-test 43 | values: 44 | tests: 45 | - env: 46 | PROMETHEUS_URL: http://prometheus-server.prometheus.svc:9090/api/v1/query 47 | queries: 48 | - query: alloy_build_info{job="integrations/self"} 49 | type: promql 50 | 51 | # Uncomment when we can figure out how to get the VPA to trigger adjusting the resources 52 | # - query: kube_pod_container_resource_requests{namespace="default", container="alloy"} 53 | # type: promql 54 | # expect: 55 | # operator: "<" 56 | # value: 0.001 57 | # - query: kube_pod_container_resource_limits{namespace="default", container="alloy"} 58 | # type: promql 59 | # expect: 60 | # operator: "<" 61 | # value: 0.001 62 | - query: kube_verticalpodautoscaler_spec_resourcepolicy_container_policies_maxallowed_cpu{namespace="default", container="alloy"} 63 | type: promql 64 | expect: 65 | value: 1 66 | - query: kube_verticalpodautoscaler_spec_resourcepolicy_container_policies_minallowed_cpu{namespace="default", container="alloy"} 67 | type: promql 68 | expect: 69 | value: 0.05 70 | -------------------------------------------------------------------------------- /charts/alloy-operator/templates/rbac/alloy-manager.yaml: -------------------------------------------------------------------------------- 1 | {{- define "alloy-operator.rbac.alloyManagerRules" -}} 2 | - apiGroups: 3 | - collectors.grafana.com 4 | resources: 5 | - alloys 6 | - alloys/status 7 | - alloys/finalizers 8 | verbs: 9 | - create 10 | - delete 11 | - get 12 | - list 13 | - patch 14 | - update 15 | - watch 16 | {{- end }} 17 | 18 | {{- if .Values.rbac.create }} 19 | {{- if .Values.rbac.createClusterRoles }} 20 | --- 21 | apiVersion: rbac.authorization.k8s.io/v1 22 | kind: ClusterRole 23 | metadata: 24 | name: {{ include "alloy-operator.fullname" . }}-alloy-manager 25 | labels:{{ include "alloy-operator.labels" . | nindent 4 }} 26 | rules:{{ include "alloy-operator.rbac.alloyManagerRules" . | nindent 2 }} 27 | --- 28 | apiVersion: rbac.authorization.k8s.io/v1 29 | kind: ClusterRoleBinding 30 | metadata: 31 | name: {{ include "alloy-operator.fullname" . }}-alloy-manager 32 | labels:{{ include "alloy-operator.labels" . | nindent 4 }} 33 | roleRef: 34 | apiGroup: rbac.authorization.k8s.io 35 | kind: ClusterRole 36 | name: {{ include "alloy-operator.fullname" . }}-alloy-manager 37 | subjects: 38 | - kind: ServiceAccount 39 | name: {{ include "alloy-operator.serviceAccountName" . }} 40 | namespace: {{ .Release.Namespace }} 41 | {{- else }} 42 | {{- $namespaces := .Values.namespaces }} 43 | {{- if .Values.ownNamespaceOnly }} 44 | {{- $namespaces = list .Release.Namespace }} 45 | {{- end }} 46 | {{- range $namespace := $namespaces }} 47 | --- 48 | apiVersion: rbac.authorization.k8s.io/v1 49 | kind: Role 50 | metadata: 51 | name: {{ include "alloy-operator.fullname" $ }}-alloy-manager 52 | namespace: {{ $namespace }} 53 | labels:{{ include "alloy-operator.labels" $ | nindent 4 }} 54 | rules:{{ include "alloy-operator.rbac.alloyManagerRules" $ | nindent 2 }} 55 | --- 56 | apiVersion: rbac.authorization.k8s.io/v1 57 | kind: RoleBinding 58 | metadata: 59 | name: {{ include "alloy-operator.fullname" $ }}-alloy-manager 60 | namespace: {{ $namespace }} 61 | labels:{{ include "alloy-operator.labels" $ | nindent 4 }} 62 | roleRef: 63 | apiGroup: rbac.authorization.k8s.io 64 | kind: Role 65 | name: {{ include "alloy-operator.fullname" $ }}-alloy-manager 66 | subjects: 67 | - kind: ServiceAccount 68 | name: {{ include "alloy-operator.serviceAccountName" $ }} 69 | namespace: {{ $.Release.Namespace }} 70 | {{- end }} 71 | {{- end }} 72 | {{- end }} 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alloy Operator 2 | 3 | The Alloy Operator is a Kubernetes Operator that manages the lifecycle of 4 | [Grafana Alloy](https://grafana.com/docs/alloy/latest/) instances. It is built with the 5 | [Operator SDK](https://sdk.operatorframework.io/) using the 6 | [Alloy Helm chart](https://github.com/grafana/alloy/tree/main/operations/helm/charts/alloy) as its base. 7 | 8 | ## Current version 9 | 10 | [//]: # (Version table start) 11 | 12 | | Component | Version | 13 | |------------------|---------| 14 | | Alloy Operator | 0.3.15 | 15 | | Alloy Helm chart | 1.5.1 | 16 | | Alloy binary | v1.12.1 | 17 | 18 | [//]: # (Version table end) 19 | 20 | ## Usage 21 | 22 | To use the Alloy Operator, there are two steps to follow: 23 | 24 | 1. Deploy the Alloy Operator. 25 | 1. Deploy an Alloy instance. 26 | 27 | ### Deploy the Alloy Operator 28 | 29 | ```shell 30 | $ helm install alloy-operator grafana/alloy-operator 31 | ``` 32 | 33 | ### Deploy an Alloy instance 34 | 35 | Create a file for your Alloy instance: 36 | 37 | ```yaml 38 | --- 39 | apiVersion: collectors.grafana.com/v1alpha1 40 | kind: Alloy 41 | metadata: 42 | name: alloy-test 43 | spec: 44 | alloy: 45 | configMap: 46 | content: |- 47 | prometheus.exporter.self "default" {} 48 | 49 | prometheus.scrape "default" { 50 | targets = prometheus.exporter.self.default.targets 51 | forward_to = [prometheus.remote_write.local_prom.receiver] 52 | } 53 | 54 | prometheus.remote_write "local_prom" { 55 | endpoint { 56 | url = "http://prometheus-server.prometheus.svc:9090/api/v1/write" 57 | } 58 | } 59 | ``` 60 | 61 | The `spec` section supports all fields in the 62 | [Alloy Helm chart](https://github.com/grafana/alloy/tree/main/operations/helm/charts/alloy)'s values file. 63 | 64 | For some examples, see the tests that are used within this repository: 65 | 66 | * [Basic Alloy instance](tests/integration/basic/alloy.yaml) 67 | * [DaemonSet with HostPath volume mounts](tests/integration/daemonset-with-volumes/alloy.yaml) 68 | * [StatefulSet with WAL](tests/integration/statefulset-with-wal/alloy.yaml) 69 | * [Remote Configuration](tests/platform/remote-config/alloy.yaml) 70 | 71 | NOTE: The Alloy instances *do not* deploy the PodLogs CRD, nor does it support the `crds` field in the `spec`. 72 | 73 | ## Contributing 74 | 75 | We welcome contributions to the Grafana Alloy Operator! Please see our [Contributing Guide](./CONTRIBUTING.md) for more 76 | information. 77 | -------------------------------------------------------------------------------- /charts/alloy-operator/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* Expand the name of the chart. */}} 2 | {{- define "alloy-operator.name" -}} 3 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 4 | {{- end }} 5 | 6 | {{/* 7 | Create a default fully qualified app name. 8 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 9 | If release name contains chart name it will be used as a full name. 10 | */}} 11 | {{- define "alloy-operator.fullname" -}} 12 | {{- if .Values.fullnameOverride }} 13 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 14 | {{- else }} 15 | {{- $name := default .Chart.Name .Values.nameOverride }} 16 | {{- if contains $name .Release.Name }} 17 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 18 | {{- else }} 19 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 20 | {{- end }} 21 | {{- end }} 22 | {{- end }} 23 | 24 | {{/* Create chart name and version as used by the chart label. */}} 25 | {{- define "alloy-operator.chart" -}} 26 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 27 | {{- end }} 28 | 29 | {{/* Common labels */}} 30 | {{- define "alloy-operator.labels" -}} 31 | helm.sh/chart: {{ include "alloy-operator.chart" . }} 32 | {{ include "alloy-operator.selectorLabels" . }} 33 | {{- if .Chart.AppVersion }} 34 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 35 | {{- end }} 36 | app.kubernetes.io/managed-by: {{ .Release.Service }} 37 | {{- end }} 38 | 39 | {{/* Selector labels */}} 40 | {{- define "alloy-operator.selectorLabels" -}} 41 | app.kubernetes.io/name: {{ include "alloy-operator.name" . }} 42 | app.kubernetes.io/instance: {{ .Release.Name }} 43 | {{- end }} 44 | 45 | {{/* The name of the service account to use */}} 46 | {{- define "alloy-operator.serviceAccountName" -}} 47 | {{- if .Values.serviceAccount.create }} 48 | {{- .Values.serviceAccount.name | default (include "alloy-operator.fullname" .) }} 49 | {{- else }} 50 | {{- .Values.serviceAccount.name | default "default" }} 51 | {{- end }} 52 | {{- end }} 53 | 54 | {{/* Calculate the image identifier to use */}} 55 | {{- define "alloy-operator.imageIdentifier" -}} 56 | {{- if .Values.image.digest }} 57 | {{- $digest := .Values.image.digest }} 58 | {{- if not (hasPrefix "sha256:" $digest) }} 59 | {{- $digest = printf "sha256:%s" $digest }} 60 | {{- end }} 61 | {{- printf "@%s" $digest }} 62 | {{- else if .Values.image.tag }} 63 | {{- printf ":%s" .Values.image.tag }} 64 | {{- else }} 65 | {{- printf ":%s" .Chart.AppVersion }} 66 | {{- end }} 67 | {{- end }} 68 | -------------------------------------------------------------------------------- /tests/integration/daemonset-with-volumes/test-plan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: helm-chart-toolbox.grafana.com/v1 3 | kind: TestPlan 4 | name: daemonset-with-volumes 5 | 6 | subject: 7 | releaseName: alloy-operator 8 | path: ../../../charts/alloy-operator 9 | namespace: operator 10 | 11 | postInstall: 12 | files: [alloy.yaml] 13 | 14 | cluster: 15 | type: kind 16 | config: 17 | apiVersion: kind.x-k8s.io/v1alpha4 18 | kind: Cluster 19 | nodes: 20 | - role: control-plane 21 | - role: worker 22 | - role: worker 23 | 24 | dependencies: 25 | - preset: prometheus 26 | - preset: loki 27 | - preset: grafana 28 | overrides: 29 | datasources: 30 | datasources.yaml: 31 | apiVersion: 1 32 | datasources: 33 | - name: Prometheus 34 | type: prometheus 35 | url: http://prometheus-server.prometheus.svc:9090 36 | isDefault: true 37 | - name: Loki 38 | type: loki 39 | url: http://loki-gateway.loki.svc:8080 40 | basicAuth: true 41 | basicAuthUser: loki 42 | jsonData: 43 | httpHeaderName1: X-Scope-OrgID 44 | secureJsonData: 45 | basicAuthPassword: lokipassword 46 | httpHeaderValue1: "1" 47 | - manifest: 48 | apiVersion: v1 49 | kind: Secret 50 | metadata: 51 | name: loki 52 | namespace: loki 53 | stringData: 54 | tenantId: "1" 55 | username: "loki" 56 | password: "lokipassword" 57 | 58 | tests: 59 | - type: kubernetes-objects-test 60 | values: 61 | checks: 62 | # Check that the Alloy instance has been reconciled 63 | - kind: DaemonSet 64 | name: alloy-logs 65 | namespace: default 66 | - type: query-test 67 | values: 68 | tests: 69 | - env: 70 | CLUSTER: daemonset-test-cluster 71 | PROMETHEUS_URL: http://prometheus-server.prometheus.svc:9090/api/v1/query 72 | LOKI_URL: http://loki.loki.svc:3100/loki/api/v1/query 73 | LOKI_TENANTID: 1 74 | LOKI_USER: loki 75 | LOKI_PASS: lokipassword 76 | queries: 77 | - query: count(alloy_build_info{cluster="$CLUSTER", job="integrations/self"}) 78 | type: promql 79 | expect: 80 | operator: == 81 | value: 3 82 | - query: count_over_time({cluster="$CLUSTER", container="alloy-operator", namespace="operator"}[1h]) 83 | type: logql 84 | -------------------------------------------------------------------------------- /tests/integration/statefulset-with-wal/alloy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: collectors.grafana.com/v1alpha1 3 | kind: Alloy 4 | metadata: 5 | name: alloy-metrics 6 | spec: 7 | controller: 8 | type: statefulset 9 | replicas: 2 10 | enableStatefulSetAutoDeletePVC: true 11 | volumeClaimTemplates: 12 | - metadata: 13 | name: alloy-wal 14 | spec: 15 | accessModes: ["ReadWriteOnce"] 16 | storageClassName: "standard" 17 | resources: 18 | requests: 19 | storage: 100Mi 20 | 21 | alloy: 22 | clustering: 23 | name: alloy-metrics 24 | enabled: true 25 | 26 | extraEnv: 27 | - name: CLUSTER_NAME 28 | value: statefulset-with-wal 29 | 30 | storagePath: /var/lib/alloy 31 | mounts: 32 | extra: 33 | - name: alloy-wal 34 | mountPath: /var/lib/alloy 35 | configMap: 36 | content: |- 37 | discovery.kubernetes "alloy_pods" { 38 | role = "pod" 39 | selectors { 40 | role = "pod" 41 | label = "app.kubernetes.io/name=alloy" 42 | } 43 | } 44 | 45 | prometheus.scrape "alloy_pods" { 46 | targets = discovery.kubernetes.alloy_pods.targets 47 | scrape_interval = "60s" 48 | clustering { 49 | enabled = true 50 | } 51 | forward_to = [prometheus.remote_write.local_prom.receiver] 52 | } 53 | 54 | discovery.kubernetes "nodes" { 55 | role = "node" 56 | } 57 | 58 | prometheus.scrape "kubelet" { 59 | targets = discovery.kubernetes.nodes.targets 60 | job_name = "integrations/kubernetes/kubelet" 61 | scheme = "https" 62 | scrape_interval = "60s" 63 | bearer_token_file = "/var/run/secrets/kubernetes.io/serviceaccount/token" 64 | 65 | tls_config { 66 | ca_file = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" 67 | insecure_skip_verify = true 68 | server_name = "kubernetes" 69 | } 70 | 71 | clustering { 72 | enabled = true 73 | } 74 | 75 | forward_to = [prometheus.remote_write.local_prom.receiver] 76 | } 77 | 78 | prometheus.remote_write "local_prom" { 79 | endpoint { 80 | url = "http://prometheus-server.prometheus.svc:9090/api/v1/write" 81 | 82 | write_relabel_config { 83 | replacement = sys.env("CLUSTER_NAME") 84 | target_label = "cluster" 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /.github/workflows/integration-test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Integration Test 3 | # yamllint disable-line rule:truthy 4 | on: 5 | push: 6 | branches: ["main"] 7 | paths: 8 | - .github/workflows/integration-test.yml 9 | - charts/** 10 | - tests/** 11 | pull_request: 12 | paths: 13 | - .github/workflows/integration-test.yml 14 | - charts/** 15 | - tests/** 16 | 17 | # Allows you to run this workflow manually from the Actions tab 18 | workflow_dispatch: 19 | 20 | permissions: {} 21 | 22 | jobs: 23 | list-tests: 24 | name: List tests 25 | runs-on: ubuntu-latest 26 | outputs: 27 | tests: ${{ steps.list_tests.outputs.tests }} 28 | steps: 29 | - name: Checkout code 30 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 31 | with: 32 | persist-credentials: 'false' 33 | 34 | - name: List tests 35 | id: list_tests 36 | working-directory: tests/integration 37 | run: | 38 | tests=$(find . -name test-plan.yaml -exec dirname {} \; | sed -e "s/^\.\///g") 39 | echo "Tests: ${tests}" 40 | echo "tests=$(echo "${tests}" | jq --raw-input --slurp --compact-output 'split("\n") | map(select(. != ""))')" >> "${GITHUB_OUTPUT}" 41 | 42 | run-tests: 43 | name: Integration Test 44 | needs: list-tests 45 | runs-on: ubuntu-latest 46 | if: needs.list-tests.outputs.tests != '[]' 47 | strategy: 48 | matrix: 49 | test: ${{ fromJson(needs.list-tests.outputs.tests) }} 50 | fail-fast: false 51 | steps: 52 | - name: Checkout code 53 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 54 | with: 55 | persist-credentials: 'false' 56 | path: source 57 | 58 | - name: Checkout code 59 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 60 | with: 61 | persist-credentials: 'false' 62 | repository: grafana/helm-chart-toolbox 63 | path: helm-chart-toolbox 64 | 65 | - name: Install Flux CLI 66 | uses: fluxcd/flux2/action@8454b02a32e48d775b9f563cb51fdcb1787b5b93 # v2.7.5 67 | 68 | - name: Install Kind CLI 69 | uses: helm/kind-action@92086f6be054225fa813e0a4b13787fc9088faab # v1.13.0 70 | with: 71 | install_only: true 72 | 73 | - name: Install Minikube CLI 74 | uses: medyagh/setup-minikube@e3c7f79eb1e997eabccc536a6cf318a2b0fe19d9 # v0.0.20 75 | with: 76 | start: false 77 | 78 | - name: Setup Flux CLI 79 | uses: fluxcd/flux2/action@8454b02a32e48d775b9f563cb51fdcb1787b5b93 # v2.7.5 80 | 81 | - name: Run test 82 | run: helm-chart-toolbox/tools/helm-test/helm-test "${TEST_DIRECTORY}" 83 | env: 84 | TEST_DIRECTORY: "source/tests/integration/${{ matrix.test }}" 85 | DELETE_CLUSTER: true 86 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $ingressApiIsStable := eq (include "alloy.ingress.isStable" .) "true" -}} 3 | {{- $ingressSupportsIngressClassName := eq (include "alloy.ingress.supportsIngressClassName" .) "true" -}} 4 | {{- $ingressSupportsPathType := eq (include "alloy.ingress.supportsPathType" .) "true" -}} 5 | {{- $fullName := include "alloy.fullname" . -}} 6 | {{- $servicePort := .Values.ingress.faroPort -}} 7 | {{- $ingressPath := .Values.ingress.path -}} 8 | {{- $ingressPathType := .Values.ingress.pathType -}} 9 | {{- $extraPaths := .Values.ingress.extraPaths -}} 10 | apiVersion: {{ include "alloy.ingress.apiVersion" . }} 11 | kind: Ingress 12 | metadata: 13 | name: {{ $fullName }} 14 | namespace: {{ include "alloy.namespace" . }} 15 | labels: 16 | {{- include "alloy.labels" . | nindent 4 }} 17 | app.kubernetes.io/component: networking 18 | {{- with .Values.ingress.labels }} 19 | {{- toYaml . | nindent 4 }} 20 | {{- end }} 21 | {{- with .Values.ingress.annotations }} 22 | annotations: 23 | {{- range $key, $value := . }} 24 | {{ $key }}: {{ tpl $value $ | quote }} 25 | {{- end }} 26 | {{- end }} 27 | spec: 28 | {{- if and $ingressSupportsIngressClassName .Values.ingress.ingressClassName }} 29 | ingressClassName: {{ .Values.ingress.ingressClassName }} 30 | {{- end -}} 31 | {{- with .Values.ingress.tls }} 32 | tls: 33 | {{- tpl (toYaml .) $ | nindent 4 }} 34 | {{- end }} 35 | rules: 36 | {{- if .Values.ingress.hosts }} 37 | {{- range .Values.ingress.hosts }} 38 | - host: {{ tpl . $ }} 39 | http: 40 | paths: 41 | {{- with $extraPaths }} 42 | {{- toYaml . | nindent 10 }} 43 | {{- end }} 44 | - path: {{ $ingressPath }} 45 | {{- if $ingressSupportsPathType }} 46 | pathType: {{ $ingressPathType }} 47 | {{- end }} 48 | backend: 49 | {{- if $ingressApiIsStable }} 50 | service: 51 | name: {{ $fullName }} 52 | port: 53 | number: {{ $servicePort }} 54 | {{- else }} 55 | serviceName: {{ $fullName }} 56 | servicePort: {{ $servicePort }} 57 | {{- end }} 58 | {{- end }} 59 | {{- else }} 60 | - http: 61 | paths: 62 | - backend: 63 | {{- if $ingressApiIsStable }} 64 | service: 65 | name: {{ $fullName }} 66 | port: 67 | number: {{ $servicePort }} 68 | {{- else }} 69 | serviceName: {{ $fullName }} 70 | servicePort: {{ $servicePort }} 71 | {{- end }} 72 | {{- with $ingressPath }} 73 | path: {{ . }} 74 | {{- end }} 75 | {{- if $ingressSupportsPathType }} 76 | pathType: {{ $ingressPathType }} 77 | {{- end }} 78 | {{- end -}} 79 | {{- end }} 80 | -------------------------------------------------------------------------------- /charts/alloy-operator/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.3.15 4 | 5 | ## 0.3.14 6 | 7 | * Update Alloy to 1.5.0 (@petewall) 8 | * Update Operator Framework to 1.42.0 (@petewall) 9 | * Add the ability to make pod and container security contexts optional, or override the fs, group, and user IDs (@petewall) 10 | 11 | ## 0.3.12 12 | 13 | * Update Alloy to 1.4.0 (@petewall) 14 | 15 | ## 0.3.11 16 | 17 | * Update Alloy to 1.3.1 (@petewall) 18 | 19 | ## 0.3.10 20 | 21 | * Update Alloy to 1.3.0 (@petewall) 22 | * Fix conditional syntax for API version checks (@siryur) 23 | * Restrict pod and container security contexts (@petewall) 24 | * Add the ability to deploy a ServiceMonitor for the Alloy Operator (@petewall) 25 | 26 | ## 0.3.9 27 | 28 | * Add the ability to create and manage Roles and RoleBindings (@petewall) 29 | 30 | ## 0.3.8 31 | 32 | * Update Alloy to 1.2.1 (@petewall) 33 | 34 | ## 0.3.7 35 | 36 | * Add the ability to pass extra arguments to the Alloy Operator container (@petewall) 37 | 38 | ## 0.3.6 39 | 40 | * Update Alloy to 1.2.0 (@petewall) 41 | 42 | ## 0.3.5 43 | 44 | * Convert to the new test system (@petewall) 45 | * Allow for setting image by digest (@petewall) 46 | 47 | ## 0.3.4 48 | 49 | * Update Alloy to 1.1.2 (@petewall) 50 | 51 | ## 0.3.3 52 | 53 | * Ensure that the Deployment and Service objects are properly namespaced (@petewall) 54 | * Remove extra files from the Helm chart that are not needed (@petewall) 55 | 56 | ## 0.3.2 57 | 58 | * Add the ability to restrict the Alloy Operator to specific namespaces (@petewall) 59 | 60 | ## 0.3.1 61 | 62 | * Update Alloy to 1.1.1 (@petewall) 63 | 64 | ## 0.3.0 65 | 66 | * Removing default resource requests and limits (@petewall, @kespineira) 67 | * Adding values.schema.json to validate the inputs (@petewall) 68 | * Add GitHub Action linting (@petewall) 69 | * Add the ability to enable or disable RBAC object creation (@petewall) 70 | 71 | ## 0.2.10 72 | 73 | * Update Alloy to 1.1.0 (@petewall) 74 | 75 | ## 0.2.9 76 | 77 | * Added more integration tests (@petewall) 78 | * Added RBAC rules for HPA (@petewall) 79 | * Added RBAC rules for Ingress, Prometheus Rules, and Prom operator objects (@discostur) 80 | * Added RBAC rules for PDB (@tw-sematell) 81 | 82 | ## 0.2.8 83 | 84 | * Set node selector by default (@petewall) 85 | * Update the build flag so it updates based on alloy version (@petewall) 86 | * Fix YAML linting (@petewall) 87 | * Set Apache 2.0 license (@tomwilkie) 88 | 89 | ## 0.2.7 90 | 91 | * Update Alloy to 1.0.3 (@petewall) 92 | * Include the CRD in the GitHub release (@petewall) 93 | 94 | ## 0.2.2 95 | 96 | * Added the ability to override image registry and pull secrets with the global option (@petewall) 97 | * Added alloy-values.yaml, the default values, to the Helm chart as a base file (@petewall) 98 | 99 | ## 0.2.1 100 | 101 | * Updated README (@petewall) 102 | 103 | ## 0.2.0 104 | 105 | * First Helm release (@petewall) 106 | 107 | ## 0.1.0 108 | 109 | * Initial prototype (@petewall) 110 | -------------------------------------------------------------------------------- /charts/alloy-operator/tests/rbac-alloy-manager_test.yaml: -------------------------------------------------------------------------------- 1 | # yamllint disable rule:document-start rule:line-length rule:trailing-spaces 2 | suite: Test - Alloy Operator - Alloy Manager RBAC 3 | templates: 4 | - rbac/alloy-manager.yaml 5 | tests: 6 | - it: creates the alloy-manager ClusterRole with correct permissions 7 | asserts: 8 | - documentIndex: 0 9 | equal: 10 | path: rules 11 | value: 12 | - apiGroups: 13 | - collectors.grafana.com 14 | resources: 15 | - alloys 16 | - alloys/status 17 | - alloys/finalizers 18 | verbs: 19 | - create 20 | - delete 21 | - get 22 | - list 23 | - patch 24 | - update 25 | - watch 26 | - documentIndex: 1 27 | equal: 28 | path: roleRef 29 | value: 30 | apiGroup: rbac.authorization.k8s.io 31 | kind: ClusterRole 32 | name: RELEASE-NAME-alloy-operator-alloy-manager 33 | - documentIndex: 1 34 | equal: 35 | path: subjects 36 | value: 37 | - kind: ServiceAccount 38 | name: RELEASE-NAME-alloy-operator 39 | namespace: NAMESPACE 40 | 41 | - it: creates multiple alloy-manager Roles when using the namespaces option 42 | set: 43 | namespaces: ["alpha", "bravo"] 44 | rbac: 45 | createClusterRoles: false 46 | asserts: 47 | - hasDocuments: 48 | count: 4 49 | - documentIndex: 0 50 | containsDocument: 51 | apiVersion: rbac.authorization.k8s.io/v1 52 | kind: Role 53 | name: RELEASE-NAME-alloy-operator-alloy-manager 54 | namespace: alpha 55 | - documentIndex: 1 56 | containsDocument: 57 | apiVersion: rbac.authorization.k8s.io/v1 58 | kind: RoleBinding 59 | name: RELEASE-NAME-alloy-operator-alloy-manager 60 | namespace: alpha 61 | - documentIndex: 2 62 | containsDocument: 63 | apiVersion: rbac.authorization.k8s.io/v1 64 | kind: Role 65 | name: RELEASE-NAME-alloy-operator-alloy-manager 66 | namespace: bravo 67 | - documentIndex: 3 68 | containsDocument: 69 | apiVersion: rbac.authorization.k8s.io/v1 70 | kind: RoleBinding 71 | name: RELEASE-NAME-alloy-operator-alloy-manager 72 | namespace: bravo 73 | 74 | - it: creates an alloy-manager Role for its own namespace when using the ownNamespaceOnly option 75 | set: 76 | ownNamespaceOnly: true 77 | rbac: 78 | createClusterRoles: false 79 | asserts: 80 | - hasDocuments: 81 | count: 2 82 | - documentIndex: 0 83 | containsDocument: 84 | apiVersion: rbac.authorization.k8s.io/v1 85 | kind: Role 86 | name: RELEASE-NAME-alloy-operator-alloy-manager 87 | namespace: NAMESPACE 88 | - documentIndex: 1 89 | containsDocument: 90 | apiVersion: rbac.authorization.k8s.io/v1 91 | kind: RoleBinding 92 | name: RELEASE-NAME-alloy-operator-alloy-manager 93 | namespace: NAMESPACE 94 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/containers/_agent.yaml: -------------------------------------------------------------------------------- 1 | {{- define "alloy.container" -}} 2 | {{- $values := (mustMergeOverwrite .Values.alloy (or .Values.agent dict)) -}} 3 | - name: alloy 4 | image: {{ .Values.global.image.registry | default .Values.image.registry }}/{{ .Values.image.repository }}{{ include "alloy.imageId" . }} 5 | imagePullPolicy: {{ .Values.image.pullPolicy }} 6 | args: 7 | - run 8 | - /etc/alloy/{{ include "alloy.config-map.key" . }} 9 | - --storage.path={{ $values.storagePath }} 10 | - --server.http.listen-addr={{ $values.listenAddr }}:{{ $values.listenPort }} 11 | - --server.http.ui-path-prefix={{ $values.uiPathPrefix }} 12 | {{- if not $values.enableReporting }} 13 | - --disable-reporting 14 | {{- end}} 15 | {{- if $values.clustering.enabled }} 16 | - --cluster.enabled=true 17 | - --cluster.join-addresses={{ include "alloy.fullname" . }}-cluster 18 | {{- if $values.clustering.name }} 19 | - --cluster.name={{ $values.clustering.name }} 20 | {{- end}} 21 | {{- end}} 22 | {{- if $values.stabilityLevel }} 23 | - --stability.level={{ $values.stabilityLevel }} 24 | {{- end }} 25 | {{- range $values.extraArgs }} 26 | - {{ . }} 27 | {{- end}} 28 | env: 29 | - name: ALLOY_DEPLOY_MODE 30 | value: "helm" 31 | - name: HOSTNAME 32 | valueFrom: 33 | fieldRef: 34 | fieldPath: spec.nodeName 35 | {{- with $values.extraEnv }} 36 | {{- toYaml . | nindent 4 }} 37 | {{- end }} 38 | {{- if $values.envFrom }} 39 | envFrom: 40 | {{- toYaml $values.envFrom | nindent 4 }} 41 | {{- end }} 42 | {{- if or $values.enableHttpServerPort (gt (len $values.extraPorts) 0) }} 43 | ports: 44 | {{- if $values.enableHttpServerPort }} 45 | - containerPort: {{ $values.listenPort }} 46 | name: http-metrics 47 | {{- end }} 48 | {{- range $portMap := $values.extraPorts }} 49 | - containerPort: {{ $portMap.targetPort }} 50 | {{- if $portMap.hostPort }} 51 | hostPort: {{ $portMap.hostPort }} 52 | {{- end}} 53 | name: {{ $portMap.name }} 54 | protocol: {{ coalesce $portMap.protocol "TCP" }} 55 | {{- end }} 56 | {{- end }} 57 | {{- if $values.enableHttpServerPort }} 58 | readinessProbe: 59 | httpGet: 60 | path: /-/ready 61 | port: {{ $values.listenPort }} 62 | scheme: {{ $values.listenScheme }} 63 | initialDelaySeconds: {{ $values.initialDelaySeconds }} 64 | timeoutSeconds: {{ $values.timeoutSeconds }} 65 | {{- end }} 66 | {{- with $values.livenessProbe }} 67 | livenessProbe: 68 | {{- toYaml . | nindent 4 }} 69 | {{- end }} 70 | {{- with $values.resources }} 71 | resources: 72 | {{- toYaml . | nindent 4 }} 73 | {{- end }} 74 | {{- with $values.lifecycle }} 75 | lifecycle: 76 | {{- toYaml . | nindent 4 }} 77 | {{- end }} 78 | {{- with $values.securityContext }} 79 | securityContext: 80 | {{- toYaml . | nindent 4 }} 81 | {{- end }} 82 | volumeMounts: 83 | - name: config 84 | mountPath: /etc/alloy 85 | {{- if $values.mounts.varlog }} 86 | - name: varlog 87 | mountPath: /var/log 88 | readOnly: true 89 | {{- end }} 90 | {{- if $values.mounts.dockercontainers }} 91 | - name: dockercontainers 92 | mountPath: /var/lib/docker/containers 93 | readOnly: true 94 | {{- end }} 95 | {{- with $values.mounts.extra }} 96 | {{- toYaml . | nindent 4 }} 97 | {{- end }} 98 | {{- end }} 99 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/controllers/_pod.yaml: -------------------------------------------------------------------------------- 1 | {{- define "alloy.pod-template" -}} 2 | {{- $values := (mustMergeOverwrite .Values.alloy (or .Values.agent dict)) -}} 3 | metadata: 4 | annotations: 5 | kubectl.kubernetes.io/default-container: alloy 6 | {{- if and (not .Values.configReloader.enabled) $values.configMap.create $values.configMap.content }} 7 | checksum/config: {{ (tpl $values.configMap.content .) | sha256sum | trunc 63 }} 8 | {{- end }} 9 | {{- with .Values.controller.podAnnotations }} 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | labels: 13 | {{- include "alloy.selectorLabels" . | nindent 4 }} 14 | {{- with .Values.controller.podLabels }} 15 | {{- toYaml . | nindent 4 }} 16 | {{- end }} 17 | spec: 18 | {{- with .Values.global.podSecurityContext }} 19 | securityContext: 20 | {{- toYaml . | nindent 4 }} 21 | {{- end }} 22 | serviceAccountName: {{ include "alloy.serviceAccountName" . }} 23 | {{- if or .Values.global.image.pullSecrets .Values.image.pullSecrets }} 24 | imagePullSecrets: 25 | {{- if .Values.global.image.pullSecrets }} 26 | {{- toYaml .Values.global.image.pullSecrets | nindent 4 }} 27 | {{- else }} 28 | {{- toYaml .Values.image.pullSecrets | nindent 4 }} 29 | {{- end }} 30 | {{- end }} 31 | {{- if .Values.controller.initContainers }} 32 | initContainers: 33 | {{- with .Values.controller.initContainers }} 34 | {{- toYaml . | nindent 4 }} 35 | {{- end }} 36 | {{- end }} 37 | containers: 38 | {{- include "alloy.container" . | nindent 4 }} 39 | {{- include "alloy.watch-container" . | nindent 4 }} 40 | {{- with .Values.controller.extraContainers }} 41 | {{- toYaml . | nindent 4 }} 42 | {{- end}} 43 | {{- if .Values.controller.priorityClassName }} 44 | priorityClassName: {{ .Values.controller.priorityClassName }} 45 | {{- end }} 46 | {{- if .Values.controller.hostNetwork }} 47 | hostNetwork: {{ .Values.controller.hostNetwork }} 48 | {{- end }} 49 | {{- if .Values.controller.hostPID }} 50 | hostPID: {{ .Values.controller.hostPID }} 51 | {{- end }} 52 | dnsPolicy: {{ .Values.controller.dnsPolicy }} 53 | {{- with .Values.controller.affinity }} 54 | affinity: 55 | {{- toYaml . | nindent 4 }} 56 | {{- end }} 57 | {{- if .Values.controller.terminationGracePeriodSeconds }} 58 | terminationGracePeriodSeconds: {{ .Values.controller.terminationGracePeriodSeconds | int }} 59 | {{- end }} 60 | {{- with .Values.controller.nodeSelector }} 61 | nodeSelector: 62 | {{- toYaml . | nindent 4 }} 63 | {{- end }} 64 | {{- with .Values.controller.tolerations }} 65 | tolerations: 66 | {{- toYaml . | nindent 4 }} 67 | {{- end }} 68 | {{- with .Values.controller.topologySpreadConstraints }} 69 | topologySpreadConstraints: 70 | {{- toYaml . | nindent 4 }} 71 | {{- end }} 72 | volumes: 73 | - name: config 74 | configMap: 75 | name: {{ include "alloy.config-map.name" . }} 76 | {{- if $values.mounts.varlog }} 77 | - name: varlog 78 | hostPath: 79 | path: /var/log 80 | {{- end }} 81 | {{- if $values.mounts.dockercontainers }} 82 | - name: dockercontainers 83 | hostPath: 84 | path: /var/lib/docker/containers 85 | {{- end }} 86 | {{- if .Values.controller.volumes.extra }} 87 | {{- toYaml .Values.controller.volumes.extra | nindent 4 }} 88 | {{- end }} 89 | {{- if $values.hostAliases }} 90 | hostAliases: 91 | {{- toYaml $values.hostAliases | nindent 4 }} 92 | {{- end }} 93 | {{- end }} 94 | -------------------------------------------------------------------------------- /operator/config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | app.kubernetes.io/name: alloy-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: system 9 | --- 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | metadata: 13 | name: controller-manager 14 | namespace: system 15 | labels: 16 | control-plane: controller-manager 17 | app.kubernetes.io/name: alloy-operator 18 | app.kubernetes.io/managed-by: kustomize 19 | spec: 20 | selector: 21 | matchLabels: 22 | control-plane: controller-manager 23 | replicas: 1 24 | template: 25 | metadata: 26 | annotations: 27 | kubectl.kubernetes.io/default-container: manager 28 | labels: 29 | control-plane: controller-manager 30 | spec: 31 | # TODO(user): Uncomment the following code to configure the nodeAffinity expression 32 | # according to the platforms which are supported by your solution. 33 | # It is considered best practice to support multiple architectures. You can 34 | # build your manager image using the makefile target docker-buildx. 35 | # affinity: 36 | # nodeAffinity: 37 | # requiredDuringSchedulingIgnoredDuringExecution: 38 | # nodeSelectorTerms: 39 | # - matchExpressions: 40 | # - key: kubernetes.io/arch 41 | # operator: In 42 | # values: 43 | # - amd64 44 | # - arm64 45 | # - ppc64le 46 | # - s390x 47 | # - key: kubernetes.io/os 48 | # operator: In 49 | # values: 50 | # - linux 51 | securityContext: 52 | runAsNonRoot: true 53 | # TODO(user): For common cases that do not require escalating privileges 54 | # it is recommended to ensure that all your Pods/Containers are restrictive. 55 | # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted 56 | # Please uncomment the following code if your project does NOT have to work on old Kubernetes 57 | # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). 58 | # seccompProfile: 59 | # type: RuntimeDefault 60 | containers: 61 | - args: 62 | - --leader-elect 63 | - --leader-election-id=alloy-operator 64 | - --health-probe-bind-address=:8081 65 | image: controller:latest 66 | name: manager 67 | securityContext: 68 | allowPrivilegeEscalation: false 69 | capabilities: 70 | drop: 71 | - "ALL" 72 | livenessProbe: 73 | httpGet: 74 | path: /healthz 75 | port: 8081 76 | initialDelaySeconds: 15 77 | periodSeconds: 20 78 | readinessProbe: 79 | httpGet: 80 | path: /readyz 81 | port: 8081 82 | initialDelaySeconds: 5 83 | periodSeconds: 10 84 | # TODO(user): Configure the resources accordingly based on the project requirements. 85 | # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ 86 | resources: 87 | limits: 88 | cpu: 500m 89 | memory: 128Mi 90 | requests: 91 | cpu: 10m 92 | memory: 64Mi 93 | serviceAccountName: controller-manager 94 | terminationGracePeriodSeconds: 10 95 | -------------------------------------------------------------------------------- /charts/alloy-operator/templates/rbac/alloy-objects.yaml: -------------------------------------------------------------------------------- 1 | {{- define "alloy-operator.rbac.alloyObjectRules" -}} 2 | # Rules which allow the management of ConfigMaps, ServiceAccounts, and Services. 3 | - apiGroups: [""] 4 | resources: ["configmaps", "secrets", "serviceaccounts", "services"] 5 | verbs: ["*"] 6 | # Rules which allow the management of DaemonSets, Deployments, and StatefulSets. 7 | - apiGroups: ["apps"] 8 | resources: ["daemonsets", "deployments", "statefulsets"] 9 | verbs: ["*"] 10 | # Rules which allow the management of Horizontal Pod Autoscalers. 11 | - apiGroups: ["autoscaling"] 12 | resources: ["horizontalpodautoscalers"] 13 | verbs: ["*"] 14 | {{- if .Capabilities.APIVersions.Has "autoscaling.k8s.io/v1" }} 15 | # Rules which allow the management of VerticalPodAutoscalers. 16 | - apiGroups: ["autoscaling.k8s.io"] 17 | resources: ["verticalpodautoscalers"] 18 | verbs: ["*"] 19 | {{- end }} 20 | {{- if .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" }} 21 | # Rules which allow the management of ServiceMonitor. 22 | - apiGroups: ["monitoring.coreos.com"] 23 | resources: ["servicemonitors"] 24 | verbs: ["*"] 25 | {{- end }} 26 | # Rules which allow the management of Ingresses and NetworkPolicies. 27 | - apiGroups: ["networking.k8s.io"] 28 | resources: ["ingresses", "networkpolicies"] 29 | verbs: ["*"] 30 | # Rules which allow the management of PodDisruptionBudgets. 31 | - apiGroups: ["policy"] 32 | resources: ["poddisruptionbudgets"] 33 | verbs: ["*"] 34 | # Rules which allow the management of ClusterRoles, ClusterRoleBindings, Roles, and RoleBindings. 35 | - apiGroups: ["rbac.authorization.k8s.io"] 36 | resources: ["clusterroles", "clusterrolebindings", "roles", "rolebindings"] 37 | verbs: ["*"] 38 | {{- end }} 39 | 40 | {{- if .Values.rbac.create }} 41 | {{- if .Values.rbac.createClusterRoles }} 42 | --- 43 | apiVersion: rbac.authorization.k8s.io/v1 44 | kind: ClusterRole 45 | metadata: 46 | name: {{ include "alloy-operator.fullname" . }} 47 | rules:{{ include "alloy-operator.rbac.alloyObjectRules" $ | nindent 2 }} 48 | --- 49 | apiVersion: rbac.authorization.k8s.io/v1 50 | kind: ClusterRoleBinding 51 | metadata: 52 | name: {{ include "alloy-operator.fullname" . }} 53 | labels:{{ include "alloy-operator.labels" . | nindent 4 }} 54 | roleRef: 55 | apiGroup: rbac.authorization.k8s.io 56 | kind: ClusterRole 57 | name: {{ include "alloy-operator.fullname" . }} 58 | subjects: 59 | - kind: ServiceAccount 60 | name: {{ include "alloy-operator.serviceAccountName" . }} 61 | namespace: {{ .Release.Namespace }} 62 | {{- else }} 63 | {{- $namespaces := .Values.namespaces }} 64 | {{- if .Values.ownNamespaceOnly }} 65 | {{- $namespaces = list .Release.Namespace }} 66 | {{- end }} 67 | {{- range $namespace := $namespaces }} 68 | --- 69 | apiVersion: rbac.authorization.k8s.io/v1 70 | kind: Role 71 | metadata: 72 | name: {{ include "alloy-operator.fullname" $ }} 73 | namespace: {{ $namespace }} 74 | labels:{{ include "alloy-operator.labels" $ | nindent 4 }} 75 | rules:{{ include "alloy-operator.rbac.alloyObjectRules" $ | nindent 2 }} 76 | --- 77 | apiVersion: rbac.authorization.k8s.io/v1 78 | kind: RoleBinding 79 | metadata: 80 | name: {{ include "alloy-operator.fullname" $ }} 81 | namespace: {{ $namespace }} 82 | labels:{{ include "alloy-operator.labels" $ | nindent 4 }} 83 | roleRef: 84 | apiGroup: rbac.authorization.k8s.io 85 | kind: Role 86 | name: {{ include "alloy-operator.fullname" $ }} 87 | subjects: 88 | - kind: ServiceAccount 89 | name: {{ include "alloy-operator.serviceAccountName" $ }} 90 | namespace: {{ $.Release.Namespace }} 91 | {{- end }} 92 | {{- end }} 93 | {{- end }} 94 | -------------------------------------------------------------------------------- /operator/config/samples/collectors_v1alpha1_alloy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: collectors.grafana.com/v1alpha1 2 | kind: Alloy 3 | metadata: 4 | name: alloy-sample 5 | spec: 6 | # Default values copied from /helm-charts/alloy/values.yaml 7 | alloy: 8 | clustering: 9 | enabled: false 10 | name: "" 11 | portName: http 12 | configMap: 13 | content: "" 14 | create: true 15 | key: null 16 | name: null 17 | enableReporting: true 18 | envFrom: [] 19 | extraArgs: [] 20 | extraEnv: [] 21 | extraPorts: [] 22 | hostAliases: [] 23 | lifecycle: {} 24 | listenAddr: 0.0.0.0 25 | listenPort: 12345 26 | listenScheme: HTTP 27 | livenessProbe: {} 28 | mounts: 29 | dockercontainers: false 30 | extra: [] 31 | varlog: false 32 | resources: {} 33 | securityContext: {} 34 | stabilityLevel: generally-available 35 | storagePath: /tmp/alloy 36 | uiPathPrefix: / 37 | configReloader: 38 | customArgs: [] 39 | enabled: true 40 | image: 41 | digest: "" 42 | registry: ghcr.io 43 | repository: jimmidyson/configmap-reload 44 | tag: v0.14.0 45 | resources: 46 | requests: 47 | cpu: 1m 48 | memory: 5Mi 49 | securityContext: {} 50 | controller: 51 | affinity: {} 52 | autoscaling: 53 | enabled: false 54 | maxReplicas: 5 55 | minReplicas: 1 56 | scaleDown: 57 | policies: [] 58 | selectPolicy: Max 59 | stabilizationWindowSeconds: 300 60 | scaleUp: 61 | policies: [] 62 | selectPolicy: Max 63 | stabilizationWindowSeconds: 0 64 | targetCPUUtilizationPercentage: 0 65 | targetMemoryUtilizationPercentage: 80 66 | dnsPolicy: ClusterFirst 67 | enableStatefulSetAutoDeletePVC: false 68 | extraAnnotations: {} 69 | extraContainers: [] 70 | hostNetwork: false 71 | hostPID: false 72 | initContainers: [] 73 | nodeSelector: {} 74 | parallelRollout: true 75 | podAnnotations: {} 76 | podDisruptionBudget: 77 | enabled: false 78 | maxUnavailable: null 79 | minAvailable: null 80 | podLabels: {} 81 | priorityClassName: "" 82 | replicas: 1 83 | terminationGracePeriodSeconds: null 84 | tolerations: [] 85 | topologySpreadConstraints: [] 86 | type: daemonset 87 | updateStrategy: {} 88 | volumeClaimTemplates: [] 89 | volumes: 90 | extra: [] 91 | extraObjects: [] 92 | fullnameOverride: null 93 | global: 94 | image: 95 | pullSecrets: [] 96 | registry: "" 97 | podSecurityContext: {} 98 | image: 99 | digest: null 100 | pullPolicy: IfNotPresent 101 | pullSecrets: [] 102 | registry: docker.io 103 | repository: grafana/alloy 104 | tag: null 105 | ingress: 106 | annotations: {} 107 | enabled: false 108 | extraPaths: [] 109 | faroPort: 12347 110 | hosts: 111 | - chart-example.local 112 | labels: {} 113 | path: / 114 | pathType: Prefix 115 | tls: [] 116 | nameOverride: null 117 | namespaceOverride: null 118 | rbac: 119 | create: true 120 | service: 121 | annotations: {} 122 | clusterIP: "" 123 | enabled: true 124 | internalTrafficPolicy: Cluster 125 | nodePort: 31128 126 | type: ClusterIP 127 | serviceAccount: 128 | additionalLabels: {} 129 | annotations: {} 130 | automountServiceAccountToken: true 131 | create: true 132 | name: null 133 | serviceMonitor: 134 | additionalLabels: {} 135 | enabled: false 136 | interval: "" 137 | metricRelabelings: [] 138 | relabelings: [] 139 | tlsConfig: {} 140 | 141 | 142 | -------------------------------------------------------------------------------- /tests/integration/daemonset-with-volumes/alloy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: collectors.grafana.com/v1alpha1 3 | kind: Alloy 4 | metadata: 5 | name: alloy-logs 6 | spec: 7 | controller: 8 | type: daemonset 9 | tolerations: 10 | - effect: NoSchedule 11 | operator: Exists 12 | 13 | alloy: 14 | mounts: 15 | varlog: true 16 | extraEnv: 17 | - name: CLUSTER_NAME 18 | value: daemonset-test-cluster 19 | configMap: 20 | content: |- 21 | prometheus.exporter.self "default" {} 22 | 23 | prometheus.scrape "default" { 24 | targets = prometheus.exporter.self.default.targets 25 | forward_to = [prometheus.remote_write.local_prom.receiver] 26 | } 27 | 28 | prometheus.remote_write "local_prom" { 29 | endpoint { 30 | url = "http://prometheus-server.prometheus.svc:9090/api/v1/write" 31 | 32 | write_relabel_config { 33 | replacement = sys.env("CLUSTER_NAME") 34 | target_label = "cluster" 35 | } 36 | } 37 | } 38 | 39 | discovery.kubernetes "pod" { 40 | role = "pod" 41 | } 42 | 43 | discovery.relabel "pod_logs" { 44 | targets = discovery.kubernetes.pod.targets 45 | 46 | rule { 47 | source_labels = ["__meta_kubernetes_namespace"] 48 | target_label = "namespace" 49 | } 50 | 51 | rule { 52 | source_labels = ["__meta_kubernetes_pod_name"] 53 | target_label = "pod" 54 | } 55 | 56 | rule { 57 | source_labels = ["__meta_kubernetes_pod_container_name"] 58 | target_label = "container" 59 | } 60 | 61 | rule { 62 | source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_name"] 63 | target_label = "app" 64 | } 65 | 66 | rule { 67 | source_labels = ["__meta_kubernetes_namespace", "__meta_kubernetes_pod_container_name"] 68 | separator = "/" 69 | replacement = "$1" 70 | target_label = "job" 71 | } 72 | 73 | rule { 74 | source_labels = ["__meta_kubernetes_pod_uid", "__meta_kubernetes_pod_container_name"] 75 | separator = "/" 76 | replacement = "/var/log/pods/*$1/*.log" 77 | target_label = "__path__" 78 | } 79 | 80 | rule { 81 | source_labels = ["__meta_kubernetes_pod_container_id"] 82 | target_label = "container_runtime" 83 | regex = "^(\\S+):\\/\\/.+$" 84 | replacement = "$1" 85 | } 86 | } 87 | 88 | loki.source.kubernetes "pod_logs" { 89 | targets = discovery.relabel.pod_logs.output 90 | forward_to = [loki.process.pod_logs.receiver] 91 | } 92 | 93 | loki.process "pod_logs" { 94 | stage.static_labels { 95 | values = { 96 | cluster = sys.env("CLUSTER_NAME"), 97 | } 98 | } 99 | 100 | forward_to = [loki.write.loki.receiver] 101 | } 102 | 103 | remote.kubernetes.secret "loki" { 104 | name = "loki" 105 | namespace = "loki" 106 | } 107 | 108 | loki.write "loki" { 109 | endpoint { 110 | url = "http://loki.loki.svc:3100/loki/api/v1/push" 111 | tenant_id = convert.nonsensitive(remote.kubernetes.secret.loki.data["tenantId"]) 112 | basic_auth { 113 | username = convert.nonsensitive(remote.kubernetes.secret.loki.data["username"]) 114 | password = remote.kubernetes.secret.loki.data["password"] 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- $values := (mustMergeOverwrite .Values.alloy (or .Values.agent dict)) -}} 2 | {{- if and (or (eq .Values.controller.type "deployment") (eq .Values.controller.type "statefulset" )) (or .Values.controller.autoscaling.horizontal.enabled .Values.controller.autoscaling.enabled) }} 3 | {{ $autoscaling := .Values.controller.autoscaling }} 4 | {{- if .Values.controller.autoscaling.horizontal.enabled }} 5 | {{- $autoscaling = .Values.controller.autoscaling.horizontal }} 6 | {{- end }} 7 | {{- if (not (empty $autoscaling.targetMemoryUtilizationPercentage)) }} 8 | {{- $_ := $values.resources.requests | required ".Values.alloy.resources.requests is required when using autoscaling." -}} 9 | {{- $_ := $values.resources.requests.memory | required ".Values.alloy.resources.requests.memory is required when using autoscaling based on memory utilization." -}} 10 | {{- $_ := .Values.configReloader.resources.requests | required ".Values.configReloader.resources.requests is required when using autoscaling." -}} 11 | {{- $_ := .Values.configReloader.resources.requests.memory | required ".Values.configReloader.resources.requests.memory is required when using autoscaling based on memory utilization." -}} 12 | {{- end}} 13 | {{- if (not (empty $autoscaling.targetCPUUtilizationPercentage)) }} 14 | {{- $_ := $values.resources.requests | required ".Values.alloy.resources.requests is required when using autoscaling." -}} 15 | {{- $_ := $values.resources.requests.cpu | required ".Values.alloy.resources.requests.cpu is required when using autoscaling based on cpu utilization." -}} 16 | {{- $_ := .Values.configReloader.resources.requests | required ".Values.configReloader.resources.requests is required when using autoscaling." -}} 17 | {{- $_ := .Values.configReloader.resources.requests.cpu | required ".Values.configReloader.resources.requests.cpu is required when using autoscaling based on cpu utilization." -}} 18 | {{- end}} 19 | apiVersion: autoscaling/v2 20 | kind: HorizontalPodAutoscaler 21 | metadata: 22 | name: {{ include "alloy.fullname" . }} 23 | namespace: {{ include "alloy.namespace" . }} 24 | labels: 25 | {{- include "alloy.labels" . | nindent 4 }} 26 | app.kubernetes.io/component: availability 27 | spec: 28 | scaleTargetRef: 29 | apiVersion: apps/v1 30 | kind: {{ .Values.controller.type }} 31 | name: {{ include "alloy.fullname" . }} 32 | {{- with $autoscaling }} 33 | minReplicas: {{ .minReplicas }} 34 | maxReplicas: {{ .maxReplicas }} 35 | behavior: 36 | {{- with .scaleDown }} 37 | scaleDown: 38 | {{- if .policies }} 39 | policies: 40 | {{- range .policies }} 41 | - type: {{ .type }} 42 | value: {{ .value }} 43 | periodSeconds: {{ .periodSeconds }} 44 | {{- end }} 45 | selectPolicy: {{ .selectPolicy }} 46 | {{- end }} 47 | stabilizationWindowSeconds: {{ .stabilizationWindowSeconds }} 48 | {{- end }} 49 | {{- with .scaleUp }} 50 | scaleUp: 51 | {{- if .policies }} 52 | policies: 53 | {{- range .policies }} 54 | - type: {{ .type }} 55 | value: {{ .value }} 56 | periodSeconds: {{ .periodSeconds }} 57 | {{- end }} 58 | selectPolicy: {{ .selectPolicy }} 59 | {{- end }} 60 | stabilizationWindowSeconds: {{ .stabilizationWindowSeconds }} 61 | {{- end }} 62 | metrics: 63 | # Changing the order of the metrics will cause ArgoCD to go into a sync loop 64 | # memory needs to be first. 65 | # More info in: https://github.com/argoproj/argo-cd/issues/1079 66 | {{- with .targetMemoryUtilizationPercentage }} 67 | - type: Resource 68 | resource: 69 | name: memory 70 | target: 71 | type: Utilization 72 | averageUtilization: {{ . }} 73 | {{- end }} 74 | {{- with .targetCPUUtilizationPercentage }} 75 | - type: Resource 76 | resource: 77 | name: cpu 78 | target: 79 | type: Utilization 80 | averageUtilization: {{ . }} 81 | {{- end }} 82 | {{- end }} 83 | {{- end }} 84 | -------------------------------------------------------------------------------- /charts/alloy-operator/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: {{ include "alloy-operator.fullname" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "alloy-operator.labels" . | nindent 4 }} 9 | spec: 10 | replicas: {{ .Values.replicaCount }} 11 | selector: 12 | matchLabels: 13 | {{- include "alloy-operator.selectorLabels" . | nindent 6 }} 14 | template: 15 | metadata: 16 | {{- with .Values.podAnnotations }} 17 | annotations: 18 | {{ toYaml . | nindent 8 }} 19 | {{- end }} 20 | labels: 21 | {{- include "alloy-operator.labels" . | nindent 8 }} 22 | {{- with .Values.podLabels }} 23 | {{ toYaml . | nindent 8 }} 24 | {{- end }} 25 | spec: 26 | {{- if or .Values.global.image.pullSecrets .Values.image.pullSecrets }} 27 | imagePullSecrets: 28 | {{- if .Values.global.image.pullSecrets }} 29 | {{ toYaml .Values.global.image.pullSecrets | nindent 8 }} 30 | {{- else }} 31 | {{ toYaml .Values.image.pullSecrets | nindent 8 }} 32 | {{- end }} 33 | {{- end }} 34 | serviceAccountName: {{ include "alloy-operator.serviceAccountName" . }} 35 | {{- with .Values.podSecurityContext }} 36 | securityContext:{{ toYaml . | nindent 8 }} 37 | {{- end }} 38 | {{- if .Values.priorityClassName }} 39 | priorityClassName: {{ .Values.priorityClassName | quote }} 40 | {{- end }} 41 | containers: 42 | - name: {{ .Chart.Name }} 43 | image: "{{ .Values.global.image.registry | default .Values.image.registry }}/{{ .Values.image.repository }}{{ include "alloy-operator.imageIdentifier" . }}" 44 | imagePullPolicy: {{ .Values.image.pullPolicy }} 45 | args: 46 | - --health-probe-bind-address=:{{ .Values.service.health.port }} 47 | - --metrics-bind-address=:{{ .Values.service.metrics.port }} 48 | {{- if .Values.leaderElection.enabled }} 49 | - --leader-elect 50 | - --leader-election-id={{ include "alloy-operator.fullname" . }} 51 | {{- end }} 52 | {{ range .Values.extraArgs }} 53 | - {{ . | quote }} 54 | {{- end }} 55 | {{- $namespaces := .Values.namespaces }} 56 | {{- if .Values.ownNamespaceOnly }} 57 | {{- $namespaces = list .Release.Namespace }} 58 | {{- end }} 59 | {{- if $namespaces }} 60 | env: 61 | - name: WATCH_NAMESPACE 62 | value: {{ $namespaces | join "," | quote }} 63 | {{- end }} 64 | ports: 65 | - name: http 66 | containerPort: {{ .Values.service.health.port }} 67 | protocol: TCP 68 | - name: metrics 69 | containerPort: {{ .Values.service.metrics.port }} 70 | protocol: TCP 71 | livenessProbe: 72 | httpGet: 73 | path: /healthz 74 | port: {{ .Values.service.health.port }} 75 | initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} 76 | periodSeconds: {{ .Values.livenessProbe.periodSeconds }} 77 | readinessProbe: 78 | httpGet: 79 | path: /readyz 80 | port: {{ .Values.service.health.port }} 81 | initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} 82 | periodSeconds: {{ .Values.readinessProbe.periodSeconds }} 83 | {{- with .Values.resources }} 84 | resources: 85 | {{- toYaml . | nindent 12 }} 86 | {{- end }} 87 | {{- with .Values.securityContext }} 88 | securityContext:{{- toYaml . | nindent 12 }} 89 | {{- end }} 90 | {{- with .Values.nodeSelector }} 91 | nodeSelector: 92 | {{- toYaml . | nindent 8 }} 93 | {{- end }} 94 | {{- with .Values.affinity }} 95 | affinity: 96 | {{- toYaml . | nindent 8 }} 97 | {{- end }} 98 | {{- with .Values.tolerations }} 99 | tolerations: 100 | {{- toYaml . | nindent 8 }} 101 | {{- end }} 102 | -------------------------------------------------------------------------------- /.github/workflows/release-alloy-operator.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release Alloy Operator Helm chart 3 | # yamllint disable-line rule:truthy 4 | on: 5 | workflow_dispatch: 6 | 7 | env: 8 | CR_INDEX_PATH: "${{ github.workspace }}/.cr-index" 9 | CR_PACKAGE_PATH: "${{ github.workspace }}/.cr-release-packages" 10 | CR_TOOL_PATH: "${{ github.workspace }}/.cr-tool" 11 | 12 | permissions: {} 13 | 14 | jobs: 15 | release-chart: 16 | uses: grafana/helm-charts/.github/workflows/update-helm-repo.yaml@e5ee6963cce3ab19519dc6dbd5f250aa14dc6f12 17 | permissions: 18 | contents: write 19 | id-token: write 20 | packages: write 21 | with: 22 | charts_dir: charts 23 | cr_configfile: cr.yaml 24 | ct_configfile: charts/alloy-operator/ct.yaml 25 | secrets: 26 | vault_repo_secret_name: github-app 27 | 28 | release: 29 | runs-on: ubuntu-latest 30 | needs: release-chart 31 | 32 | # These permissions are needed to assume roles from GitHub's OIDC. 33 | permissions: 34 | contents: read 35 | id-token: write 36 | 37 | steps: 38 | - name: Checkout 39 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 40 | with: 41 | fetch-depth: 0 42 | path: source 43 | persist-credentials: 'false' 44 | 45 | - name: Configure Git 46 | run: | 47 | cd source 48 | git config user.name "$GITHUB_ACTOR" 49 | git config user.email "$GITHUB_ACTOR@users.noreply.github.com" 50 | 51 | - name: Install Helm 52 | uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1 53 | 54 | - name: Parse Chart.yaml 55 | id: parse-chart 56 | working-directory: source/charts/alloy-operator 57 | run: | 58 | name=$(yq ".name" < Chart.yaml) 59 | version=$(yq ".version" < Chart.yaml) 60 | { 61 | echo "chartpath=charts/$(basename "$(pwd)")"; 62 | echo "tagname=${name}-${version}"; 63 | echo "packagename=${name}-${version}"; 64 | } >> "${GITHUB_OUTPUT}" 65 | 66 | - name: Add dependency chart repos 67 | env: 68 | CHARTPATH: ${{ steps.parse-chart.outputs.chartpath }} 69 | working-directory: source 70 | run: | 71 | # Skip the header line and make sure that tabs are expanded into spaces 72 | deps=$(helm dependency list "${CHARTPATH}" | tail +2 | expand) 73 | while read -r row; do 74 | IFS=' ' read -ra parts <<< "$row" 75 | name="${parts[0]}" 76 | repo="${parts[2]}" 77 | case "$repo" in 78 | "https://"*) helm repo add "$name" "$repo" ;; 79 | *) echo >&2 "Skipping dependency $name: unsupported schema for \"$repo\"" ;; 80 | esac 81 | done <<< "$deps" 82 | 83 | - name: Retrieve GitHub App credentials from Vault 84 | id: get-secrets 85 | uses: grafana/shared-workflows/actions/get-vault-secrets@28361cdb22223e5f1e34358c86c20908e7248760 # v1.1.0 86 | env: 87 | VAULT_REPO_SECRET_NAME: github-app 88 | with: 89 | repo_secrets: | 90 | GITHUB_APP_ID=${{ env.VAULT_REPO_SECRET_NAME }}:app-id 91 | PRIVATE_KEY=${{ env.VAULT_REPO_SECRET_NAME }}:private-key 92 | 93 | - name: Generate GitHub App Token 94 | if: env.GITHUB_APP_ID != '' 95 | id: app-token 96 | uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 97 | with: 98 | # Variables generated by the previous step get-secrets 99 | app-id: ${{ env.GITHUB_APP_ID }} 100 | private-key: ${{ env.PRIVATE_KEY }} 101 | owner: ${{ github.repository_owner }} 102 | 103 | - name: Set the token 104 | env: 105 | APP_TOKEN: ${{ steps.app-token.outputs.token }} 106 | run: echo "AUTHTOKEN=${APP_TOKEN}" >> "${GITHUB_ENV}" 107 | 108 | - name: Install CR tool 109 | env: 110 | GITHUB_TOKEN: ${{ env.AUTHTOKEN }} 111 | run: | 112 | mkdir "${CR_TOOL_PATH}" 113 | mkdir "${CR_PACKAGE_PATH}" 114 | mkdir "${CR_INDEX_PATH}" 115 | crVersion=$(gh release list --repo helm/chart-releaser --exclude-pre-releases --json tagName --jq '.[0].tagName' | sed 's/v//') 116 | curl -sSLo cr.tar.gz "https://github.com/helm/chart-releaser/releases/download/v${crVersion}/chart-releaser_${crVersion}_linux_amd64.tar.gz" 117 | tar -xzf cr.tar.gz -C "${CR_TOOL_PATH}" 118 | rm -f cr.tar.gz 119 | 120 | - name: Create helm package 121 | env: 122 | CHARTPATH: ${{ steps.parse-chart.outputs.chartpath }} 123 | working-directory: source 124 | run: | 125 | "${CR_TOOL_PATH}/cr" package "${CHARTPATH}" --config cr.yaml --package-path "${CR_PACKAGE_PATH}" 126 | echo "Result of chart package:" 127 | cp charts/alloy-crd/crds/collectors.grafana.com_alloy.yaml "${CR_PACKAGE_PATH}/collectors.grafana.com_alloy.yaml" 128 | ls -l "${CR_PACKAGE_PATH}" 129 | 130 | - name: Make github release 131 | uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 132 | with: 133 | name: ${{ steps.parse-chart.outputs.tagname }} 134 | repository: grafana/alloy-operator 135 | generate_release_notes: true 136 | files: | 137 | ${{ env.CR_PACKAGE_PATH }}/${{ steps.parse-chart.outputs.packagename }}.tgz 138 | ${{ env.CR_PACKAGE_PATH }}/collectors.grafana.com_alloy.yaml 139 | tag_name: ${{ steps.parse-chart.outputs.tagname }} 140 | token: ${{ env.AUTHTOKEN }} 141 | fail_on_unmatched_files: true 142 | -------------------------------------------------------------------------------- /operator/helm-charts/alloy/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "alloy.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "alloy.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "alloy.chart" -}} 30 | {{- if index .Values "$chart_tests" }} 31 | {{- printf "%s" .Chart.Name | replace "+" "_" | trunc 63 | trimSuffix "-" }} 32 | {{- else }} 33 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 34 | {{- end }} 35 | {{- end }} 36 | 37 | {{/* 38 | Allow the release namespace to be overridden for multi-namespace deployments in combined charts 39 | */}} 40 | {{- define "alloy.namespace" -}} 41 | {{- if .Values.namespaceOverride }} 42 | {{- .Values.namespaceOverride }} 43 | {{- else }} 44 | {{- .Release.Namespace }} 45 | {{- end }} 46 | {{- end }} 47 | 48 | {{/* 49 | Common labels 50 | */}} 51 | {{- define "alloy.labels" -}} 52 | helm.sh/chart: {{ include "alloy.chart" . }} 53 | {{ include "alloy.selectorLabels" . }} 54 | {{- if index .Values "$chart_tests" }} 55 | app.kubernetes.io/version: "vX.Y.Z" 56 | app.kubernetes.io/managed-by: {{ .Release.Service }} 57 | {{- else -}} 58 | {{/* substr trims delimeter prefix char from alloy.imageId output 59 | e.g. ':' for tags and '@' for digests. 60 | For digests, we crop the string to a 7-char (short) sha. */}} 61 | app.kubernetes.io/version: {{ (include "alloy.imageId" .) | trimPrefix "@sha256" | trimPrefix ":" | trunc 63 | quote }} 62 | app.kubernetes.io/managed-by: {{ .Release.Service }} 63 | app.kubernetes.io/part-of: alloy 64 | {{- end }} 65 | {{- end }} 66 | 67 | {{/* 68 | Selector labels 69 | */}} 70 | {{- define "alloy.selectorLabels" -}} 71 | app.kubernetes.io/name: {{ include "alloy.name" . }} 72 | app.kubernetes.io/instance: {{ .Release.Name }} 73 | {{- end }} 74 | 75 | {{/* 76 | Create the name of the service account to use 77 | */}} 78 | {{- define "alloy.serviceAccountName" -}} 79 | {{- if .Values.serviceAccount.create }} 80 | {{- default (include "alloy.fullname" .) .Values.serviceAccount.name }} 81 | {{- else }} 82 | {{- default "default" .Values.serviceAccount.name }} 83 | {{- end }} 84 | {{- end }} 85 | 86 | {{/* 87 | Calculate name of image ID to use for "alloy. 88 | */}} 89 | {{- define "alloy.imageId" -}} 90 | {{- if .Values.image.digest }} 91 | {{- $digest := .Values.image.digest }} 92 | {{- if not (hasPrefix "sha256:" $digest) }} 93 | {{- $digest = printf "sha256:%s" $digest }} 94 | {{- end }} 95 | {{- printf "@%s" $digest }} 96 | {{- else if .Values.image.tag }} 97 | {{- printf ":%s" .Values.image.tag }} 98 | {{- else }} 99 | {{- printf ":%s" .Chart.AppVersion }} 100 | {{- end }} 101 | {{- end }} 102 | 103 | {{/* 104 | Calculate name of image ID to use for "config-reloader". 105 | */}} 106 | {{- define "config-reloader.imageId" -}} 107 | {{- if .Values.configReloader.image.digest }} 108 | {{- $digest := .Values.configReloader.image.digest }} 109 | {{- if not (hasPrefix "sha256:" $digest) }} 110 | {{- $digest = printf "sha256:%s" $digest }} 111 | {{- end }} 112 | {{- printf "@%s" $digest }} 113 | {{- else if .Values.configReloader.image.tag }} 114 | {{- printf ":%s" .Values.configReloader.image.tag }} 115 | {{- else }} 116 | {{- printf ":%s" "v0.8.0" }} 117 | {{- end }} 118 | {{- end }} 119 | 120 | {{/* 121 | Return the appropriate apiVersion for ingress. 122 | */}} 123 | {{- define "alloy.ingress.apiVersion" -}} 124 | {{- if and ($.Capabilities.APIVersions.Has "networking.k8s.io/v1") (semverCompare ">= 1.19-0" .Capabilities.KubeVersion.Version) }} 125 | {{- print "networking.k8s.io/v1" }} 126 | {{- else if $.Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" }} 127 | {{- print "networking.k8s.io/v1beta1" }} 128 | {{- else }} 129 | {{- print "extensions/v1beta1" }} 130 | {{- end }} 131 | {{- end }} 132 | 133 | {{/* 134 | Return if ingress is stable. 135 | */}} 136 | {{- define "alloy.ingress.isStable" -}} 137 | {{- eq (include "alloy.ingress.apiVersion" .) "networking.k8s.io/v1" }} 138 | {{- end }} 139 | 140 | {{/* 141 | Return if ingress supports ingressClassName. 142 | */}} 143 | {{- define "alloy.ingress.supportsIngressClassName" -}} 144 | {{- or (eq (include "alloy.ingress.isStable" .) "true") (and (eq (include "alloy.ingress.apiVersion" .) "networking.k8s.io/v1beta1") (semverCompare ">= 1.18-0" .Capabilities.KubeVersion.Version)) }} 145 | {{- end }} 146 | {{/* 147 | Return if ingress supports pathType. 148 | */}} 149 | {{- define "alloy.ingress.supportsPathType" -}} 150 | {{- or (eq (include "alloy.ingress.isStable" .) "true") (and (eq (include "alloy.ingress.apiVersion" .) "networking.k8s.io/v1beta1") (semverCompare ">= 1.18-0" .Capabilities.KubeVersion.Version)) }} 151 | {{- end }} 152 | 153 | {{/* 154 | Return the appropriate apiVersion for PodDisruptionBudget. 155 | */}} 156 | {{- define "alloy.controller.pdb.apiVersion" -}} 157 | {{- if and (.Capabilities.APIVersions.Has "policy/v1") (semverCompare ">=1.21-0" .Capabilities.KubeVersion.Version) -}} 158 | {{- print "policy/v1" -}} 159 | {{- else -}} 160 | {{- print "policy/v1beta1" -}} 161 | {{- end -}} 162 | {{- end -}} 163 | -------------------------------------------------------------------------------- /charts/alloy-operator/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # -- Overrides the chart's name. Used to change the infix in the resource names. 3 | nameOverride: "" 4 | # -- Overrides the chart's computed fullname. Used to change the full prefix of 5 | # resource names. 6 | fullnameOverride: "" 7 | 8 | global: 9 | image: 10 | # -- Global image registry override. 11 | # @section -- Image Settings 12 | registry: "" 13 | 14 | # -- Global image pull secrets. 15 | # @section -- Image Settings 16 | pullSecrets: [] 17 | 18 | # -- How many replicas to use for the Alloy Operator Deployment. 19 | # @section -- Deployment Settings 20 | replicaCount: 1 21 | 22 | # -- Restrict the Alloy Operator to only manage Alloy instances in the given list of namespaces. 23 | # @section -- Alloy Management Settings 24 | namespaces: [] 25 | 26 | # -- Restrict the Alloy Operator to its own namespace only. Overrides the `namespaces` setting. 27 | # @section -- Alloy Management Settings 28 | ownNamespaceOnly: false 29 | 30 | # Leader election settings. 31 | leaderElection: 32 | # -- Whether to enable leader election for the Alloy Operator. This is important when using multiple replicas or 33 | # when rolling updates. If set to false, you risk having split-brain scenarios where multiple instances of the 34 | # Alloy Operator try to manage the same Alloy instances. 35 | # @section -- Leader Election Settings 36 | enabled: true 37 | 38 | # -- Additional arguments to pass to the Alloy Operator. 39 | # @section -- Operator Settings 40 | extraArgs: [] 41 | 42 | image: 43 | # -- Alloy Operator image registry 44 | # @section -- Image Settings 45 | registry: ghcr.io 46 | 47 | # -- Alloy Operator image repository 48 | # @section -- Image Settings 49 | repository: grafana/alloy-operator 50 | 51 | # -- Alloy Operator image tag. When empty, the Chart's appVersion is used. 52 | # @section -- Image Settings 53 | tag: "" 54 | 55 | # -- Alloy Operator image digest. If set, will override the tag. Format: sha256:<digest>. 56 | # @section -- Image Settings 57 | digest: "" 58 | 59 | # -- The pull policy for images. 60 | # @section -- Image Settings 61 | pullPolicy: IfNotPresent 62 | 63 | # -- Optional set of image pull secrets. 64 | # @section -- Image Settings 65 | pullSecrets: [] 66 | 67 | # Service Account settings 68 | serviceAccount: 69 | # -- Whether to create a service account for the Alloy Operator deployment. 70 | # @section -- Service Account Settings 71 | create: true 72 | # -- Whether the Alloy Operator pod should automatically mount the service account token. 73 | # @section -- Service Account Settings 74 | automount: true 75 | # -- Annotations to add to the service account 76 | # @section -- Service Account Settings 77 | annotations: {} 78 | # -- Additional labels to add to the service account 79 | # @section -- Service Account Settings 80 | labels: {} 81 | # -- The name of the service account to use. 82 | # If not set and create is true, a name is generated using the fullname template 83 | # @section -- Service Account Settings 84 | name: "" 85 | 86 | # RBAC settings 87 | rbac: 88 | # -- Whether to create the necessary RBAC resources for the Alloy Operator. 89 | # @section -- RBAC Settings 90 | create: true 91 | 92 | # -- Create ClusterRoles for the Alloy Operator. If set to false, only Roles and RoleBindings will be created. This 93 | # setting requires the use of `namespaces` or `ownNamespaceOnly` to be set. 94 | # @section -- RBAC Settings 95 | createClusterRoles: true 96 | 97 | # -- Additional annotations to add to the Alloy Operator pods. 98 | # @section -- Pod Settings 99 | podAnnotations: {} 100 | 101 | # -- Additional labels to add to the Alloy Operator pods. 102 | # @section -- Pod Settings 103 | podLabels: {} 104 | 105 | # -- Sets the priority class name for the Alloy Operator pods. 106 | # @section -- Pod Settings 107 | priorityClassName: "" 108 | 109 | # -- Set the security context for the operator container. 110 | # @section -- Container Settings 111 | securityContext: 112 | allowPrivilegeEscalation: false 113 | readOnlyRootFilesystem: true 114 | capabilities: 115 | drop: ["ALL"] 116 | 117 | # -- Set the security context for the Alloy Operator pods. 118 | # @section -- Pod Settings 119 | podSecurityContext: 120 | fsGroup: 1000 121 | runAsGroup: 1000 122 | runAsNonRoot: true 123 | runAsUser: 1000 124 | seccompProfile: 125 | type: RuntimeDefault 126 | 127 | service: 128 | # -- The type of service to create for the operator. 129 | # @section -- Service 130 | type: ClusterIP 131 | 132 | health: 133 | # -- The port number for the health probes. 134 | # @section -- Service 135 | port: 8081 136 | 137 | metrics: 138 | # -- The port number for the metrics service. 139 | # @section -- Service 140 | port: 8082 141 | 142 | # Sets the resources for the Alloy Operator pods. 143 | resources: 144 | # -- Set the resource requests for the Alloy Operator pods. 145 | # @section -- Resources 146 | requests: {} 147 | # cpu: 10m 148 | # memory: 64Mi 149 | 150 | # -- Set the resource limits for the Alloy Operator pods. 151 | # @section -- Resources 152 | limits: {} 153 | # cpu: 500m 154 | # memory: 128Mi 155 | 156 | # -- Liveness probe settings 157 | # @section -- Probes 158 | livenessProbe: 159 | initialDelaySeconds: 15 160 | periodSeconds: 20 161 | 162 | # -- Readiness probe settings 163 | # @section -- Probes 164 | readinessProbe: 165 | initialDelaySeconds: 5 166 | periodSeconds: 10 167 | 168 | # -- Set the node selector for the Alloy Operator pods. 169 | # @section -- Pod Settings 170 | nodeSelector: 171 | kubernetes.io/os: linux 172 | 173 | # -- Set the tolerations for the Alloy Operator pods. 174 | # @section -- Pod Settings 175 | tolerations: [] 176 | 177 | # -- Set the affinity for the Alloy Operator pods. 178 | # @section -- Pod Settings 179 | affinity: {} 180 | 181 | serviceMonitor: 182 | # -- Whether to create a ServiceMonitor 183 | # @section -- Service Monitor 184 | enabled: false 185 | # -- Set of labels to transfer from the Kubernetes Service onto the target 186 | # @section -- Service Monitor 187 | additionalLabels: {} 188 | # -- Set how frequently Prometheus should scrape 189 | # @section -- Service Monitor 190 | interval: 60s 191 | # -- Set timeout for scrape 192 | # @section -- Service Monitor 193 | scrapeTimeout: 10s 194 | # -- Set path to metrics path 195 | # @section -- Service Monitor 196 | telemetryPath: /metrics 197 | # -- Set of labels to transfer from the Kubernetes Service onto the target 198 | # @section -- Service Monitor 199 | targetLabels: [] 200 | # -- Set ServiceMonitor metricRelabelings to apply to metrics after scraping. 201 | # @section -- Service Monitor 202 | metricRelabelings: [] 203 | # -- Set ServiceMonitor relabelings to apply before scraping. 204 | # @section -- Service Monitor 205 | relabelings: [] 206 | 207 | # Handling of the CustomResourceDefinitions (CRDs) 208 | crds: 209 | # -- Should this chart deploy the Alloy CRD? 210 | # @section -- CRDs 211 | deployAlloyCRD: true 212 | 213 | # -- Should this chart deploy the PodLogs CRD? 214 | # @section -- CRDs 215 | deployPodLogsCRD: false 216 | -------------------------------------------------------------------------------- /.markdownlint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Default state for all rules 3 | default: true 4 | 5 | # Path to configuration file to extend 6 | extends: null 7 | 8 | # MD001/heading-increment/header-increment - Heading levels should only increment by one level at a time 9 | MD001: true 10 | 11 | # MD002/first-heading-h1/first-header-h1 - First heading should be a top-level heading 12 | MD002: 13 | # Heading level 14 | level: 1 15 | 16 | # MD003/heading-style/header-style - Heading style 17 | MD003: 18 | # Heading style 19 | style: "consistent" 20 | 21 | # MD004/ul-style - Unordered list style 22 | MD004: 23 | # List style 24 | style: "consistent" 25 | 26 | # MD005/list-indent - Inconsistent indentation for list items at the same level 27 | MD005: true 28 | 29 | # MD006/ul-start-left - Consider starting bulleted lists at the beginning of the line 30 | MD006: true 31 | 32 | # MD007/ul-indent - Unordered list indentation 33 | MD007: 34 | # Spaces for indent 35 | indent: 4 36 | # Whether to indent the first level of the list 37 | start_indented: false 38 | # Spaces for first level indent (when start_indented is set) 39 | start_indent: 2 40 | 41 | # MD009/no-trailing-spaces - Trailing spaces 42 | MD009: 43 | # Spaces for line break 44 | br_spaces: 2 45 | # Allow spaces for empty lines in list items 46 | list_item_empty_lines: false 47 | # Include unnecessary breaks 48 | strict: false 49 | 50 | # MD010/no-hard-tabs - Hard tabs 51 | MD010: 52 | # Include code blocks 53 | code_blocks: true 54 | # Fenced code languages to ignore 55 | ignore_code_languages: [] 56 | # Number of spaces for each hard tab 57 | spaces_per_tab: 2 58 | 59 | # MD011/no-reversed-links - Reversed link syntax 60 | MD011: true 61 | 62 | # MD012/no-multiple-blanks - Multiple consecutive blank lines 63 | MD012: 64 | # Consecutive blank lines 65 | maximum: 1 66 | 67 | # MD013/line-length - Line length 68 | MD013: 69 | # Number of characters 70 | line_length: 500 71 | # Number of characters for headings 72 | heading_line_length: 80 73 | # Number of characters for code blocks 74 | code_block_line_length: 500 75 | # Include code blocks 76 | code_blocks: true 77 | # Include tables 78 | tables: false 79 | # Include headings 80 | headings: true 81 | # Strict length checking 82 | strict: false 83 | # Stern length checking 84 | stern: false 85 | 86 | # MD014/commands-show-output - Dollar signs used before commands without showing output 87 | MD014: false 88 | 89 | # MD018/no-missing-space-atx - No space after hash on atx style heading 90 | MD018: true 91 | 92 | # MD019/no-multiple-space-atx - Multiple spaces after hash on atx style heading 93 | MD019: true 94 | 95 | # MD020/no-missing-space-closed-atx - No space inside hashes on closed atx style heading 96 | MD020: true 97 | 98 | # MD021/no-multiple-space-closed-atx - Multiple spaces inside hashes on closed atx style heading 99 | MD021: true 100 | 101 | # MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines 102 | MD022: 103 | # Blank lines above heading 104 | lines_above: 1 105 | # Blank lines below heading 106 | lines_below: 1 107 | 108 | # MD023/heading-start-left/header-start-left - Headings must start at the beginning of the line 109 | MD023: true 110 | 111 | # MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content 112 | MD024: false 113 | 114 | # MD025/single-title/single-h1 - Multiple top-level headings in the same document 115 | MD025: 116 | # Heading level 117 | level: 1 118 | # RegExp for matching title in front matter 119 | front_matter_title: "^\\s*title\\s*[:=]" 120 | 121 | # MD026/no-trailing-punctuation - Trailing punctuation in heading 122 | MD026: 123 | # Punctuation characters not allowed at end of headings 124 | punctuation: ".,;:!。,;:!" 125 | 126 | # MD027/no-multiple-space-blockquote - Multiple spaces after blockquote symbol 127 | MD027: true 128 | 129 | # MD028/no-blanks-blockquote - Blank line inside blockquote 130 | MD028: true 131 | 132 | # MD029/ol-prefix - Ordered list item prefix 133 | MD029: 134 | # List style 135 | style: "one_or_ordered" 136 | 137 | # MD031/blanks-around-fences - Fenced code blocks should be surrounded by blank lines 138 | MD031: 139 | # Include list items 140 | list_items: true 141 | 142 | # MD032/blanks-around-lists - Lists should be surrounded by blank lines 143 | MD032: true 144 | 145 | # MD033/no-inline-html - Inline HTML 146 | MD033: 147 | # Allowed elements 148 | allowed_elements: 149 | - div 150 | - br 151 | - hr 152 | 153 | # MD034/no-bare-urls - Bare URL used 154 | MD034: true 155 | 156 | # MD035/hr-style - Horizontal rule style 157 | MD035: 158 | # Horizontal rule style 159 | style: "consistent" 160 | 161 | # MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading 162 | MD036: false 163 | 164 | # MD037/no-space-in-emphasis - Spaces inside emphasis markers 165 | MD037: true 166 | 167 | # MD038/no-space-in-code - Spaces inside code span elements 168 | MD038: true 169 | 170 | # MD039/no-space-in-links - Spaces inside link text 171 | MD039: true 172 | 173 | # MD040/fenced-code-language - Fenced code blocks should have a language specified 174 | MD040: 175 | # List of languages 176 | allowed_languages: [] 177 | # Require language only 178 | language_only: false 179 | 180 | # MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading 181 | MD041: 182 | # Heading level 183 | level: 1 184 | # RegExp for matching title in front matter 185 | front_matter_title: "^\\s*title\\s*[:=]" 186 | 187 | # MD042/no-empty-links - No empty links 188 | MD042: true 189 | 190 | # MD043/required-headings/required-headers - Required heading structure 191 | MD043: 192 | # List of headings 193 | headings: 194 | - "*" 195 | # Match case of headings 196 | match_case: false 197 | 198 | # MD044/proper-names - Proper names should have the correct capitalization 199 | MD044: 200 | # List of proper names 201 | names: [] 202 | # Include code blocks 203 | code_blocks: true 204 | # Include HTML elements 205 | html_elements: true 206 | 207 | # MD045/no-alt-text - Images should have alternate text (alt text) 208 | MD045: true 209 | 210 | # MD046/code-block-style - Code block style 211 | MD046: 212 | # Block style 213 | style: "consistent" 214 | 215 | # MD047/single-trailing-newline - Files should end with a single newline character 216 | MD047: true 217 | 218 | # MD048/code-fence-style - Code fence style 219 | MD048: 220 | # Code fence style 221 | style: "consistent" 222 | 223 | # MD049/emphasis-style - Emphasis style should be consistent 224 | MD049: 225 | # Emphasis style should be consistent 226 | style: "consistent" 227 | 228 | # MD050/strong-style - Strong style should be consistent 229 | MD050: 230 | # Strong style should be consistent 231 | style: "consistent" 232 | 233 | # MD051/link-fragments - Link fragments should be valid 234 | MD051: false 235 | 236 | # MD052/reference-links-images - Reference links and images should use a label that is defined 237 | MD052: true 238 | 239 | # MD053/link-image-reference-definitions - Link and image reference definitions should be needed 240 | MD053: 241 | # Ignored definitions 242 | ignored_definitions: [ 243 | "//" 244 | ] 245 | 246 | # MD060/table-column-style - Disabling because helm-docs is inconsistent 247 | MD060: false 248 | --------------------------------------------------------------------------------