diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 6997679..0d31de4 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -127,10 +127,62 @@ jobs: chmod +x ${{ env.linkerd-buoyant-bin }} ./${{ env.linkerd-buoyant-bin }} version --cli + helm_tests: + name: Helm Tests + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up Helm + uses: azure/setup-helm@v1 + with: + version: v3.7.1 + + - uses: actions/setup-python@v2 + with: + python-version: 3.7 + + - name: Set up chart-testing + uses: helm/chart-testing-action@v2.1.0 + + - name: Validate chart README.md is up to date + run: | + bin/helm-docs --chart-search-root=charts + git diff --exit-code charts/linkerd-buoyant/README.md + + - name: Update Chart version + run: | + sed -i "s/0.0.0-undefined/0.0.0/g" charts/linkerd-buoyant/Chart.yaml + + - name: Run chart-testing (list-changed) + id: list-changed + run: | + changed=$(ct list-changed --config charts/ct.yaml) + if [[ -n "$changed" ]]; then + echo "::set-output name=changed::true" + fi + + - name: Run chart-testing (lint) + run: ct lint --config charts/ct.yaml + + - name: Create kind cluster + uses: helm/kind-action@v1.2.0 + if: steps.list-changed.outputs.changed == 'true' + + - name: Create buoyant-cloud namespace + run: | + kubectl create ns buoyant-cloud + + - name: Run chart-testing (install) + run: ct install --config charts/ct.yaml + # everything below here for main merges or releases (tags) docker_build_and_push: name: Docker Build and Push - if: startsWith(github.ref, 'refs/tags') || github.ref == 'refs/heads/main' + if: startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' needs: - go_unit_tests - go_lint @@ -164,7 +216,7 @@ jobs: echo "platforms=linux/amd64" >> $GITHUB_ENV echo "docker_tags=ghcr.io/buoyantio/linkerd-buoyant:latest" >> $GITHUB_ENV - name: Generate Docker tags (for tagged release) - if: startsWith(github.ref, 'refs/tags') + if: startsWith(github.ref, 'refs/tags/v') run: | echo "platforms=linux/amd64,linux/arm64,linux/arm/v6,linux/arm/v7" >> $GITHUB_ENV echo "docker_tags=ghcr.io/buoyantio/linkerd-buoyant:latest,ghcr.io/buoyantio/linkerd-buoyant:${GITHUB_REF##*/}" >> $GITHUB_ENV @@ -189,12 +241,13 @@ jobs: # everything below here for releases (tags) create_release: - if: startsWith(github.ref, 'refs/tags') + if: startsWith(github.ref, 'refs/tags/v') name: Create Release needs: - go_unit_tests - go_lint - smoke_test_cli + - helm_tests - docker_build_and_push runs-on: ubuntu-20.04 outputs: @@ -212,7 +265,7 @@ jobs: prerelease: false upload_release_assets: - if: startsWith(github.ref, 'refs/tags') + if: startsWith(github.ref, 'refs/tags/v') name: Upload Release Assets needs: - create_release @@ -253,3 +306,57 @@ jobs: asset_path: ./${{ env.linkerd-buoyant-bin }} asset_name: ${{ env.linkerd-buoyant-bin }} asset_content_type: application/octet-stream + + create_helm_release: + if: startsWith(github.ref, 'refs/tags/v') + name: Create Helm Release + needs: + - create_release + - upload_release_assets + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set Helm tag version + run: | + echo "helm_tag=${GITHUB_REF##*/v}" >> $GITHUB_ENV + + - name: Set Helm package name + run: echo "helm_package=linkerd-buoyant-${{ env.helm_tag }}.tgz" >> $GITHUB_ENV + + # this step modifies the Helm chart in-place, in preparation for release + - name: Update Chart version + env: + helm_tag: ${{ env.helm_tag }} + run: | + sed -i "s/0.0.0-undefined/$helm_tag/g" charts/linkerd-buoyant/Chart.yaml + + - name: Configure Git + run: | + git config user.name "Helm Releaser" + git config user.email info@buoyant.io + + - name: Set up Helm + uses: azure/setup-helm@v1 + with: + version: v3.4.0 + + - name: Run chart-releaser + uses: helm/chart-releaser-action@v1.2.1 + with: + config: charts/cr.yaml + env: + CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + + - name: Upload Helm chart to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create_release.outputs.upload_url }} + asset_path: ./.cr-release-packages/${{ env.helm_package }} + asset_name: ${{ env.helm_package }} + asset_content_type: application/octet-stream diff --git a/README.md b/README.md index cba2e3c..c629caf 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,30 @@ go test -race -cover ./... bin/lint ``` +### Helm + +Test template: +```bash +helm template --namespace buoyant-cloud --values charts/linkerd-buoyant/ci/fake-values.yaml linkerd-buoyant charts/linkerd-buoyant +``` + +Install: +```bash +helm install --create-namespace --namespace buoyant-cloud --values charts/linkerd-buoyant/ci/fake-values.yaml linkerd-buoyant charts/linkerd-buoyant +``` + +To install a live agent from buoyant.cloud, register a new agent, get its +`values.yml`: +```bash +VALLUES_URL=https://buoyant.cloud/agent-helm-values/buoyant-cloud-k8s-XXX.yml +helm install --create-namespace --namespace buoyant-cloud --values $VALUES_URL linkerd-buoyant charts/linkerd-buoyant +``` + +Update chart README.md: +```bash +bin/helm-docs --chart-search-root=charts +``` + ## Release Note the latest release: diff --git a/bin/helm-docs b/bin/helm-docs new file mode 100755 index 0000000..5d2d60e --- /dev/null +++ b/bin/helm-docs @@ -0,0 +1,57 @@ +#!/usr/bin/env sh + +set -eu + +helmdocsv=1.5.0 +bindir=$( cd "${0%/*}" && pwd ) # Change to script dir and set bin dir to this +targetbin=$( cd "$bindir"/.. && pwd )/target/bin +helmdocsbin=$targetbin/helm-docs-$helmdocsv +os="" +arch="" + +if [ ! -f "$helmdocsbin" ]; then + case $(uname | tr '[:upper:]' '[:lower:]') in + darwin*) + os=darwin + arch=x86_64 + ;; + linux*) + os=linux + case $(uname -m) in + x86_64) arch=x86_64 ;; + amd64) arch=amd64 ;; + arm) + tmp=$(dpkg --print-architecture) + if echo "$tmp" | grep -q arm64; then + arch=arm64 + elif echo "$tmp" | grep -q armv7; then + arch=armv7 + elif echo "$tmp" | grep -q armv6; then + arch=armv6 + fi + ;; + esac + ;; + msys*) + os=windows + arch=x86_64 + ;; + esac + + if [ -z "$os" ]; then + echo "Couldn't find a matching binary" + exit 126 + fi + helmdocscurl="https://github.com/norwoodj/helm-docs/releases/download/v$helmdocsv/helm-docs_${helmdocsv}_${os}_${arch}.tar.gz" + tmp=$(mktemp -d -t helm-docs.XXX) + mkdir -p "$targetbin" + ( + cd "$tmp" + curl -Lsf -o "./helm-docs.tar.gz" "$helmdocscurl" + tar zf "./helm-docs.tar.gz" -x "helm-docs" + chmod +x "helm-docs" + ) + mv "$tmp/helm-docs" "$helmdocsbin" +fi + +"$helmdocsbin" "$@" diff --git a/charts/chart_schema.yaml b/charts/chart_schema.yaml new file mode 100644 index 0000000..cfb0290 --- /dev/null +++ b/charts/chart_schema.yaml @@ -0,0 +1,37 @@ +name: str() +home: str(required=False) +version: str() +apiVersion: str() +appVersion: any(str(), num(), required=False) +description: str() +keywords: list(str(), required=False) +sources: list(str(), required=False) +maintainers: list(include('maintainer'), required=False) +dependencies: list(include('dependency'), required=False) +icon: str(required=False) +engine: str(required=False) +condition: str(required=False) +tags: str(required=False) +deprecated: bool(required=False) +kubeVersion: str(required=False) +annotations: map(str(), str(), required=False) +type: str(required=False) +--- +maintainer: + name: str() + email: str(required=False) + url: str(required=False) +--- +dependency: + name: str() + version: str() + repository: str(required=False) + condition: str(required=False) + tags: list(str(), required=False) + enabled: bool(required=False) + import-values: any(list(str()), list(include('import-value')), required=False) + alias: str(required=False) +--- +import-value: + child: str() + parent: str() diff --git a/charts/cr.yaml b/charts/cr.yaml new file mode 100644 index 0000000..a6a945f --- /dev/null +++ b/charts/cr.yaml @@ -0,0 +1,2 @@ +release-name-template: "v{{ .Version }}" +skip-existing: true diff --git a/charts/ct.yaml b/charts/ct.yaml new file mode 100644 index 0000000..9afe603 --- /dev/null +++ b/charts/ct.yaml @@ -0,0 +1,5 @@ +chart-yaml-schema: charts/chart_schema.yaml +lint-conf: charts/lintconf.yaml +namespace: buoyant-cloud +release-label: test-release-label +target-branch: main diff --git a/charts/linkerd-buoyant/Chart.yaml b/charts/linkerd-buoyant/Chart.yaml new file mode 100644 index 0000000..8e4a895 --- /dev/null +++ b/charts/linkerd-buoyant/Chart.yaml @@ -0,0 +1,19 @@ +apiVersion: v2 +appVersion: 0.0.0-undefined +description: The Linkerd Buoyant extension connects your Linkerd-enabled Kubernetes cluster to Buoyant Cloud, the global platform health dashboard for Linkerd. +home: https://buoyant.io/cloud +icon: https://buoyant.io/images/buoyant_logo.svg +keywords: +- linkerd +- Buoyant +- Service Mesh +kubeVersion: '>=1.20.0-0' +maintainers: +- email: info@buoyant.io + name: BuoyantIO + url: https://buoyant.io/cloud +name: linkerd-buoyant +sources: +- https://github.com/BuoyantIO/linkerd-buoyant +type: application +version: 0.0.0-undefined diff --git a/charts/linkerd-buoyant/README.md b/charts/linkerd-buoyant/README.md new file mode 100644 index 0000000..c75e877 --- /dev/null +++ b/charts/linkerd-buoyant/README.md @@ -0,0 +1,40 @@ +# linkerd-buoyant + +![Version: 0.0.0-undefined](https://img.shields.io/badge/Version-0.0.0--undefined-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.0-undefined](https://img.shields.io/badge/AppVersion-0.0.0--undefined-informational?style=flat-square) + +The Linkerd Buoyant extension connects your Linkerd-enabled Kubernetes cluster to Buoyant Cloud, the global platform health dashboard for Linkerd. + +**Homepage:** + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| BuoyantIO | info@buoyant.io | https://buoyant.io/cloud | + +## Source Code + +* + +## Requirements + +Kubernetes: `>=1.20.0-0` + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| agent.apiAddress | string | `"api.buoyant.cloud:443"` | API address for Buoyant Cloud | +| agent.imageVersion | string | `nil` | imageVersion of the buoyant-cloud-agent pod | +| agent.insecure | bool | `false` | Disable TLS. Only use for testing. | +| agent.logLevel | string | `"info"` | Log level for the buoyant-cloud-agent pod | +| agentClusterName | string | `nil` | The name of the cluster observed by this agent, as it should appear in Buoyant Cloud. Required. | +| agentDownloadKey | string | `nil` | The download key for this agent. Required. | +| agentID | string | `nil` | The ID for this agent. Required. | +| agentKey | string | `nil` | The key for this agent. Required. | +| metrics.apiAddress | string | `"api.buoyant.cloud:443"` | API address for Buoyant Cloud | +| metrics.insecure | bool | `false` | Disable TLS. Only use for testing. | +| metrics.logLevel | string | `"info"` | Log level for the buoyant-cloud-metrics pod | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.5.0](https://github.com/norwoodj/helm-docs/releases/v1.5.0) diff --git a/charts/linkerd-buoyant/ci/dev-values.yaml b/charts/linkerd-buoyant/ci/dev-values.yaml new file mode 100644 index 0000000..72c4bef --- /dev/null +++ b/charts/linkerd-buoyant/ci/dev-values.yaml @@ -0,0 +1,13 @@ +agentID: fake-agent-id +agentKey: fake-agent-key +agentDownloadKey: fake-agent-download-key +agentClusterName: fake-agent-cluster-name +agent: + apiAddress: dockerhost:8086 + imageVersion: latest + insecure: true + logLevel: debug +metrics: + apiAddress: dockerhost:8087 + insecure: true + logLevel: debug diff --git a/charts/linkerd-buoyant/ci/fake-values.yaml b/charts/linkerd-buoyant/ci/fake-values.yaml new file mode 100644 index 0000000..e9daf52 --- /dev/null +++ b/charts/linkerd-buoyant/ci/fake-values.yaml @@ -0,0 +1,6 @@ +agentID: fake-agent-id +agentKey: fake-agent-key +agentDownloadKey: fake-agent-download-key +agentClusterName: fake-agent-cluster-name +agent: + imageVersion: latest diff --git a/charts/linkerd-buoyant/templates/NOTES.txt b/charts/linkerd-buoyant/templates/NOTES.txt new file mode 100644 index 0000000..63a02e9 --- /dev/null +++ b/charts/linkerd-buoyant/templates/NOTES.txt @@ -0,0 +1,22 @@ +Thank you for installing {{ .Chart.Name }}. + +Your release is named {{ .Release.Name }}. + +To help you manage your Buoyant Cloud agent, you can install the CLI extension +by running: + + curl -sL https://buoyant.cloud/install | sh + +Alternatively, you can download the CLI directly via the linkerd-buoyant +releases page: + + https://github.com/BuoyantIO/linkerd-buoyant/releases + +To make sure everything works as expected, run the following: + + linkerd-buoyant check + +Need help? Message us in the #buoyant-cloud Slack channel: +https://linkerd.slack.com/archives/C01QSTM20BY + +Looking for more? Visit https://buoyant.io/cloud/ diff --git a/charts/linkerd-buoyant/templates/bcloud-agent.yaml b/charts/linkerd-buoyant/templates/bcloud-agent.yaml new file mode 100644 index 0000000..f599acb --- /dev/null +++ b/charts/linkerd-buoyant/templates/bcloud-agent.yaml @@ -0,0 +1,79 @@ +# +# Buoyant Cloud Agent +# +apiVersion: apps/v1 +kind: Deployment +metadata: + name: buoyant-cloud-agent + {{- if eq .Release.Service "Buoyant Cloud" }} + namespace: {{.Release.Namespace}} + {{- end }} + annotations: + buoyant.cloud/is-agent: "true" + buoyant.cloud/version: v{{ .Chart.Version }} + buoyant.cloud/service-name: agent + labels: + app.kubernetes.io/name: agent + app.kubernetes.io/version: v{{ .Chart.Version }} + app.kubernetes.io/part-of: buoyant-cloud + linkerd.io/extension: buoyant +spec: + replicas: 1 + selector: + matchLabels: + app: buoyant-cloud-agent + template: + metadata: + labels: + app: buoyant-cloud-agent + annotations: + config.linkerd.io/skip-outbound-ports: "4191" + spec: + securityContext: + fsGroup: 1000 + serviceAccount: buoyant-cloud-agent + containers: + - name: buoyant-cloud-agent + image: ghcr.io/buoyantio/linkerd-buoyant:{{ default (printf "v%s" .Chart.Version) .Values.agent.imageVersion }} + args: + - "-grpc-addr={{ .Values.agent.apiAddress }}" + - "-log-level={{ .Values.agent.logLevel }}" + {{- if .Values.agent.insecure }} + - "-insecure" + {{- end }} + env: + - name: BUOYANT_CLOUD_ID + valueFrom: + secretKeyRef: + key: id + name: buoyant-cloud-id + - name: BUOYANT_CLOUD_KEY + valueFrom: + secretKeyRef: + key: key + name: buoyant-cloud-id + ports: + - name: admin + containerPort: 9990 + livenessProbe: + httpGet: + path: /ping + port: 9990 + readinessProbe: + httpGet: + path: /ready + port: 9990 + resources: + requests: + cpu: 10m + memory: 10Mi + limits: + cpu: "2" + memory: 5000Mi + securityContext: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: true + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 diff --git a/charts/linkerd-buoyant/templates/metrics-agent.yaml b/charts/linkerd-buoyant/templates/metrics-agent.yaml new file mode 100644 index 0000000..59276fb --- /dev/null +++ b/charts/linkerd-buoyant/templates/metrics-agent.yaml @@ -0,0 +1,394 @@ +# +# Metrics Agent config +# +kind: ConfigMap +metadata: + name: buoyant-cloud-metrics + {{- if eq .Release.Service "Buoyant Cloud" }} + namespace: {{.Release.Namespace}} + {{- end }} + labels: + app.kubernetes.io/part-of: buoyant-cloud +apiVersion: v1 +data: + agent.yml: | + server: + log_level: {{ .Values.metrics.logLevel }} + http_listen_port: 9991 + metrics: + wal_directory: /tmp/wal + global: + scrape_interval: 10s + external_labels: + cluster_id: ${BUOYANT_CLOUD_ID} + cluster_name: ${BUOYANT_CLOUD_NAME} + configs: + - host_filter: true + name: buoyant-cloud-metrics + wal_truncate_frequency: "1m" + remote_write: + - url: http{{- if not .Values.metrics.insecure}}s{{- end}}://{{.Values.metrics.apiAddress}}/remote-write + basic_auth: + username: ${BUOYANT_CLOUD_ID} + password: ${BUOYANT_CLOUD_KEY} + queue_config: + capacity: 1500 + max_shards: 20 + max_backoff: 10s + + scrape_configs: + + - job_name: 'buoyant-cloud-agent' + kubernetes_sd_configs: + - role: pod + namespaces: + names: + - 'buoyant-cloud' + relabel_configs: + - source_labels: [__meta_kubernetes_pod_container_port_name] + regex: ^admin$ + action: keep + - source_labels: [__meta_kubernetes_pod_container_name] + regex: ^buoyant-cloud-agent|buoyant-cloud-metrics$ + action: keep + - source_labels: [__meta_kubernetes_namespace] + action: replace + target_label: namespace + - source_labels: [__meta_kubernetes_pod_name] + action: replace + target_label: pod + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + + # scrape_configs copied from `linkerd install` + + - job_name: 'kubernetes-nodes-cadvisor' + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + kubernetes_sd_configs: + - role: node + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - target_label: __address__ + replacement: kubernetes.default.svc:443 + - source_labels: [__meta_kubernetes_node_name] + regex: (.+) + target_label: __metrics_path__ + replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor + metric_relabel_configs: + - source_labels: [__name__] + regex: ^container_cpu_usage_seconds_total|container_memory_working_set_bytes|machine_cpu_cores|machine_memory_bytes$ + action: keep + - source_labels: [pod] + target_label: workload_kind + regex: ^(.*)-[bcdfghjklmnpqrstvwxz2456789]{5,15}$ + action: replace + replacement: Deployment + - source_labels: [pod] + target_label: workload_kind + regex: ^(.*)-[0-9]+$ + action: replace + replacement: StatefulSet + - source_labels: [pod] + target_label: workload_kind + regex: ^(.*)-[bcdfghjklmnpqrstvwxz2456789]{5}$ + action: replace + replacement: DaemonSet + - source_labels: [pod] + target_label: workload_kind + regex: ^(.*)-[456789bcdf]{1,10}-[bcdfghjklmnpqrstvwxz2456789]{5}$ + action: replace + replacement: Deployment + - source_labels: [pod] + target_label: workload_name + regex: ^(.*)-[bcdfghjklmnpqrstvwxz2456789]{5,15}$ + action: replace + replacement: $1 + - source_labels: [pod] + target_label: workload_name + regex: ^(.*)-[0-9]+$ + action: replace + replacement: $1 + - source_labels: [pod] + target_label: workload_name + regex: ^(.*)-[bcdfghjklmnpqrstvwxz2456789]{5}$ + action: replace + replacement: $1 + - source_labels: [pod] + target_label: workload_name + regex: ^(.*)-[456789bcdf]{1,10}-[bcdfghjklmnpqrstvwxz2456789]{5}$ + action: replace + replacement: $1 + + - job_name: 'linkerd-proxy' + kubernetes_sd_configs: + - role: pod + relabel_configs: + - source_labels: + - __meta_kubernetes_pod_container_name + - __meta_kubernetes_pod_container_port_name + action: keep + regex: ^linkerd-proxy;linkerd-admin$ + - source_labels: [__meta_kubernetes_namespace] + action: replace + target_label: namespace + - source_labels: [__meta_kubernetes_pod_name] + action: replace + target_label: pod + # special case k8s' "job" label, to not interfere with prometheus' "job" + # label + # __meta_kubernetes_pod_label_linkerd_io_proxy_job=foo => + # k8s_job=foo + - source_labels: [__meta_kubernetes_pod_label_linkerd_io_proxy_job] + action: replace + target_label: k8s_job + # drop __meta_kubernetes_pod_label_linkerd_io_proxy_job + - action: labeldrop + regex: __meta_kubernetes_pod_label_linkerd_io_proxy_job + # __meta_kubernetes_pod_label_linkerd_io_proxy_deployment=foo => + # deployment=foo + - action: labelmap + regex: __meta_kubernetes_pod_label_linkerd_io_proxy_(.+) + # drop all labels that we just made copies of in the previous labelmap + - action: labeldrop + regex: __meta_kubernetes_pod_label_linkerd_io_proxy_(.+) + # __meta_kubernetes_pod_label_linkerd_io_foo=bar => + # foo=bar + - action: labelmap + regex: __meta_kubernetes_pod_label_linkerd_io_(.+) + + # __meta_kubernetes_pod_controller_kind=DaemonSet => workload_kind=DaemonSet + # __meta_kubernetes_pod_controller_name=foo => workload_name=foo + - source_labels: [__meta_kubernetes_pod_controller_kind] + action: replace + target_label: workload_kind + - source_labels: [__meta_kubernetes_pod_controller_name] + action: replace + target_label: workload_name + + # __meta_kubernetes_pod_controller_kind=ReplicaSet => workload_kind=Deployment + # __meta_kubernetes_pod_controller_name=foo-bar-123 => workload_name=foo-bar + - source_labels: [__meta_kubernetes_pod_controller_kind] + action: replace + regex: ^ReplicaSet$ + target_label: workload_kind + replacement: Deployment + - source_labels: + - __meta_kubernetes_pod_controller_kind + - __meta_kubernetes_pod_controller_name + action: replace + regex: ^ReplicaSet;(.*)-[^-]+$ + target_label: workload_name + + metric_relabel_configs: + + # keep linkerd metrics relevant to buoyant cloud + - source_labels: [__name__] + regex: ^response_total|response_latency_ms_bucket|route_response_total|route_response_latency_ms_bucket|tcp_open_connections|tcp_read_bytes_total|tcp_write_bytes_total|inbound_http_authz_allow_total|inbound_http_authz_deny_total|inbound_tcp_authz_allow_total|inbound_tcp_authz_deny_total$ + action: keep + + # drop high-cardinality outbound latency histograms + - source_labels: + - __name__ + - direction + regex: 'response_latency_ms_bucket;outbound' + action: drop + + # drop high-cardinality outbound tcp open connections + - source_labels: + - __name__ + - direction + regex: 'tcp_open_connections;outbound' + action: drop + + # drop high-cardinality outbound tcp read bytes + - source_labels: + - __name__ + - direction + regex: 'tcp_read_bytes_total;outbound' + action: drop + + # drop high-cardinality outbound tcp write bytes + - source_labels: + - __name__ + - direction + regex: 'tcp_write_bytes_total;outbound' + action: drop + + # drop linkerd workload labels (superseded by workload_kind, workload_name) + - action: labeldrop + regex: 'deployment' + - action: labeldrop + regex: 'daemonset' + - action: labeldrop + regex: 'statefulset' + + # foo{direction="outbound"} => outbound_foo{} + - source_labels: + - __name__ + - direction + regex: ^(.+);(inbound|outbound)$ + action: replace + target_label: __name__ + replacement: $${2}_$${1} + - action: labeldrop + regex: direction + + # dst_daemonset=foo => dst_workload_name=foo + # dst_daemonset=foo => dst_workload_kind=DaemonSet + - source_labels: [dst_daemonset] + regex: (.+) + action: replace + target_label: dst_workload_name + - source_labels: [dst_daemonset] + regex: (.+) + action: replace + target_label: dst_workload_kind + replacement: DaemonSet + - action: labeldrop + regex: 'dst_daemonset' + + # dst_deployment=foo => dst_workload_name=foo + # dst_deployment=foo => dst_workload_kind=Deployment + - source_labels: [dst_deployment] + regex: (.+) + action: replace + target_label: dst_workload_name + - source_labels: [dst_deployment] + regex: (.+) + action: replace + target_label: dst_workload_kind + replacement: Deployment + - action: labeldrop + regex: 'dst_deployment' + + # dst_statefulset=foo => dst_workload_name=foo + # dst_statefulset=foo => dst_workload_kind=StatefulSet + - source_labels: [dst_statefulset] + regex: (.+) + action: replace + target_label: dst_workload_name + - source_labels: [dst_statefulset] + regex: (.+) + action: replace + target_label: dst_workload_kind + replacement: StatefulSet + - action: labeldrop + regex: 'dst_statefulset' + + # drop remaining high-cardinality linkerd metrics and labels + - action: labeldrop + regex: 'pod_template_hash' + - action: labeldrop + regex: 'dst_pod_template_hash' + - action: labeldrop + regex: 'dst_serviceaccount' + - action: labeldrop + regex: 'server_id' + - action: labeldrop + regex: 'control_plane_ns' + - action: labeldrop + regex: 'dst_control_plane_ns' + - action: labeldrop + regex: 'workload_ns' +--- +# +# Metrics Agent +# +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: buoyant-cloud-metrics + {{- if eq .Release.Service "Buoyant Cloud" }} + namespace: {{.Release.Namespace}} + {{- end }} + annotations: + buoyant.cloud/is-metrics: "true" + buoyant.cloud/version: v{{ .Chart.Version }} + buoyant.cloud/service-name: metrics + labels: + app.kubernetes.io/name: metrics + app.kubernetes.io/version: v{{ .Chart.Version }} + app.kubernetes.io/part-of: buoyant-cloud + linkerd.io/extension: buoyant +spec: + selector: + matchLabels: + app: buoyant-cloud-metrics + template: + metadata: + labels: + app: buoyant-cloud-metrics + spec: + securityContext: + fsGroup: 1000 + serviceAccount: buoyant-cloud-agent + tolerations: + - operator: Exists + effect: NoSchedule + containers: + - name: buoyant-cloud-metrics + image: grafana/agent:v0.20.0 + args: + - -config.file=/buoyant-cloud-metrics/agent.yml + - -config.expand-env + ports: + - name: admin + containerPort: 9991 + livenessProbe: + httpGet: + path: /-/healthy + port: 9991 + readinessProbe: + httpGet: + path: /-/ready + port: 9991 + resources: + requests: + cpu: 10m + memory: 10Mi + limits: + cpu: "2" + memory: 5000Mi + securityContext: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: true + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + env: + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: BUOYANT_CLOUD_ID + valueFrom: + secretKeyRef: + key: id + name: buoyant-cloud-id + - name: BUOYANT_CLOUD_KEY + valueFrom: + secretKeyRef: + key: key + name: buoyant-cloud-id + - name: BUOYANT_CLOUD_NAME + valueFrom: + secretKeyRef: + key: name + name: buoyant-cloud-id + volumeMounts: + - mountPath: /buoyant-cloud-metrics + name: buoyant-cloud-metrics + - mountPath: /tmp + name: tmp + volumes: + - configMap: + name: buoyant-cloud-metrics + name: buoyant-cloud-metrics + - name: tmp + emptyDir: {} diff --git a/charts/linkerd-buoyant/templates/namespace-metadata.yaml b/charts/linkerd-buoyant/templates/namespace-metadata.yaml new file mode 100644 index 0000000..82817c8 --- /dev/null +++ b/charts/linkerd-buoyant/templates/namespace-metadata.yaml @@ -0,0 +1,110 @@ +{{- /* +This pre-install hook is based on: +https://github.com/linkerd/linkerd2/blob/140c197032b7b11a670f80039ec5ed79aa29bdf3/viz/charts/linkerd-viz/templates/namespace-metadata.yaml + +To create a namespace for the `linkerd-buoyant` extension, we instruct the +user to run `helm install --create-namespace`, or create the namespace +themselves. This pre-install hook then ensures the following: +1. The `linkerd-buoyant` extension is installed in the `buoyant-cloud` + namespace. +2. The `buoyant-cloud` namespace has the following annotations and labels: + annotations: + linkerd.io/inject: enabled + labels: + app.kubernetes.io/part-of: buoyant-cloud + linkerd.io/extension: buoyant +*/}} +{{ if ne .Release.Namespace "buoyant-cloud" }} + {{ fail "Namespace must be 'buoyant-cloud'" }} +{{ end }} +kind: ServiceAccount +apiVersion: v1 +metadata: + name: namespace-metadata + annotations: + helm.sh/hook: pre-install + helm.sh/hook-weight: "0" + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: namespace-metadata + annotations: + helm.sh/hook: pre-install + helm.sh/hook-weight: "0" + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded +rules: +- apiGroups: [""] + resources: ["namespaces"] + verbs: ["get", "patch"] + resourceNames: ["{{.Release.Namespace}}"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: namespace-metadata + annotations: + helm.sh/hook: pre-install + helm.sh/hook-weight: "0" + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded +roleRef: + kind: Role + name: namespace-metadata + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: namespace-metadata + namespace: {{.Release.Namespace}} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: namespace-metadata + annotations: + helm.sh/hook: pre-install + helm.sh/hook-weight: "0" + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + labels: + app.kubernetes.io/name: namespace-metadata + app.kubernetes.io/part-of: buoyant-cloud + app.kubernetes.io/version: v{{ .Chart.Version }} +spec: + template: + metadata: + annotations: + linkerd.io/inject: disabled + labels: + app.kubernetes.io/name: namespace-metadata + app.kubernetes.io/part-of: buoyant-cloud + app.kubernetes.io/version: v{{ .Chart.Version }} + spec: + restartPolicy: Never + serviceAccountName: namespace-metadata + containers: + - name: namespace-metadata + image: curlimages/curl:7.80.0 + command: ["/bin/sh"] + args: + - -c + - | + ops='' + token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + ns=$(curl -kfv -H "Authorization: Bearer $token" \ + "https://kubernetes.default.svc/api/v1/namespaces/{{.Release.Namespace}}") + + if echo "$ns" | grep -vq 'labels'; then + ops="$ops{\"op\": \"add\",\"path\": \"/metadata/labels\",\"value\": {}}," + fi + if echo "$ns" | grep -vq 'annotations'; then + ops="$ops{\"op\": \"add\", \"path\": \"/metadata/annotations\", \"value\": {}}," + fi + + ops="$ops{\"op\": \"add\", \"path\": \"/metadata/labels/app.kubernetes.io~1part-of\", \"value\": \"buoyant-cloud\"}," + ops="$ops{\"op\": \"add\", \"path\": \"/metadata/labels/linkerd.io~1extension\", \"value\": \"buoyant\"}," + + ops="$ops{\"op\": \"add\", \"path\": \"/metadata/annotations/linkerd.io~1inject\", \"value\": \"enabled\"}" + + curl -kfv -XPATCH -H "Content-Type: application/json-patch+json" -H "Authorization: Bearer $token" \ + -d "[$ops]" \ + "https://kubernetes.default.svc/api/v1/namespaces/{{.Release.Namespace}}?fieldManager=kubectl-label" diff --git a/charts/linkerd-buoyant/templates/namespace.yaml b/charts/linkerd-buoyant/templates/namespace.yaml new file mode 100644 index 0000000..f55143e --- /dev/null +++ b/charts/linkerd-buoyant/templates/namespace.yaml @@ -0,0 +1,14 @@ +{{- if eq .Release.Service "Buoyant Cloud" -}} +# +# Buoyant Cloud Namespace +# +kind: Namespace +apiVersion: v1 +metadata: + name: {{ .Release.Namespace }} + annotations: + linkerd.io/inject: enabled + labels: + app.kubernetes.io/part-of: buoyant-cloud + linkerd.io/extension: buoyant +{{ end -}} diff --git a/charts/linkerd-buoyant/templates/rbac.yaml b/charts/linkerd-buoyant/templates/rbac.yaml new file mode 100644 index 0000000..be9d5fc --- /dev/null +++ b/charts/linkerd-buoyant/templates/rbac.yaml @@ -0,0 +1,76 @@ +kind: ServiceAccount +apiVersion: v1 +metadata: + name: buoyant-cloud-agent + {{- if eq .Release.Service "Buoyant Cloud" }} + namespace: {{.Release.Namespace}} + {{- end }} + labels: + app.kubernetes.io/part-of: buoyant-cloud +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: buoyant-cloud-agent + labels: + app.kubernetes.io/part-of: buoyant-cloud +rules: +- apiGroups: [""] + resources: ["services", "pods", "events", "nodes", "nodes/proxy", "pods/log"] + verbs: ["get", "list", "watch"] +- apiGroups: ["policy.linkerd.io"] + resources: ["servers", "serverauthorizations"] + verbs: ["get", "list", "watch"] +- apiGroups: ["linkerd.io"] + resources: ["serviceprofiles"] + verbs: ["list", "get", "watch"] +- apiGroups: ["split.smi-spec.io"] + resources: ["trafficsplits"] + verbs: ["list", "get", "watch"] +- apiGroups: ["multicluster.linkerd.io"] + resources: ["links"] + verbs: ["list", "get", "watch"] +- apiGroups: [""] + resources: ["configmaps"] + resourceNames: ["linkerd-config", "linkerd-identity-trust-roots"] + verbs: ["get"] +- apiGroups: ["apps"] + resources: ["daemonsets", "deployments", "replicasets", "statefulsets"] + verbs: ["list", "get", "watch"] +- apiGroups: ["metrics.k8s.io"] + resources: ["pods"] + verbs: ["list", "get"] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: buoyant-cloud-agent + labels: + app.kubernetes.io/part-of: buoyant-cloud +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: buoyant-cloud-agent +subjects: +- kind: ServiceAccount + name: buoyant-cloud-agent + namespace: {{.Release.Namespace}} +--- +# +# Secrets to identify and authenticate this agent ({{ .Values.agentClusterName }}) +# +kind: Secret +apiVersion: v1 +metadata: + name: buoyant-cloud-id + {{- if eq .Release.Service "Buoyant Cloud" }} + namespace: {{.Release.Namespace}} + {{- end }} + labels: + app.kubernetes.io/part-of: buoyant-cloud +type: Opaque +data: + id: {{ b64enc (required "Please provide the ID for this agent in the agentID value" .Values.agentID | trim) }} + key: {{ b64enc (required "Please provide the key for this agent in the agentKey value" .Values.agentKey | trim) }} + downloadKey: {{ b64enc (required "Please provide the download key for this agent in the agentDownloadKey value" .Values.agentDownloadKey | trim) }} + name: {{ b64enc (required "Please provide the cluster name for this agent in the agentClusterName value" .Values.agentClusterName | trim) }} diff --git a/charts/linkerd-buoyant/values.yaml b/charts/linkerd-buoyant/values.yaml new file mode 100644 index 0000000..f99a7d5 --- /dev/null +++ b/charts/linkerd-buoyant/values.yaml @@ -0,0 +1,34 @@ +# -- The ID for this agent. Required. +agentID: + +# -- The key for this agent. Required. +agentKey: + +# -- The download key for this agent. Required. +agentDownloadKey: + +# -- The name of the cluster observed by this agent, as it should appear in Buoyant Cloud. Required. +agentClusterName: + +agent: + # -- API address for Buoyant Cloud + apiAddress: api.buoyant.cloud:443 + + # -- imageVersion of the buoyant-cloud-agent pod + imageVersion: + + # -- Disable TLS. Only use for testing. + insecure: false + + # -- Log level for the buoyant-cloud-agent pod + logLevel: info + +metrics: + # -- API address for Buoyant Cloud + apiAddress: api.buoyant.cloud:443 + + # -- Disable TLS. Only use for testing. + insecure: false + + # -- Log level for the buoyant-cloud-metrics pod + logLevel: info diff --git a/charts/lintconf.yaml b/charts/lintconf.yaml new file mode 100644 index 0000000..90f48c8 --- /dev/null +++ b/charts/lintconf.yaml @@ -0,0 +1,42 @@ +--- +rules: + braces: + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + brackets: + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + colons: + max-spaces-before: 0 + max-spaces-after: 1 + commas: + max-spaces-before: 0 + min-spaces-after: 1 + max-spaces-after: 1 + comments: + require-starting-space: true + min-spaces-from-content: 2 + document-end: disable + document-start: disable # No --- to start a file + empty-lines: + max: 2 + max-start: 0 + max-end: 0 + hyphens: + max-spaces-after: 1 + indentation: + spaces: consistent + indent-sequences: whatever # - list indentation will handle both indentation and without + check-multi-line-strings: false + key-duplicates: enable + line-length: disable # Lines can be any length + new-line-at-end-of-file: enable + new-lines: + type: unix + trailing-spaces: enable + truthy: + level: warning