mirror of
https://github.com/stefanprodan/podinfo.git
synced 2026-04-07 03:26:54 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b501abd1f0 | ||
|
|
e0a79a4ddd | ||
|
|
be8baac695 | ||
|
|
f539517440 | ||
|
|
01219a196e | ||
|
|
b9acae4064 | ||
|
|
64a8da1836 | ||
|
|
420d0db8bf | ||
|
|
6b67f2bdd6 | ||
|
|
095b1cd251 | ||
|
|
0a27dbe40c | ||
|
|
2da74a4ec2 | ||
|
|
c7ffdba3bd | ||
|
|
06f7cd3777 | ||
|
|
620b9b7e2c | ||
|
|
83deb7fcb7 | ||
|
|
550ee9f7b9 | ||
|
|
dd185df435 | ||
|
|
07a524ba01 | ||
|
|
5d97df9c89 | ||
|
|
a8cadef09b | ||
|
|
32f6e3d8c9 | ||
|
|
77dc46241d | ||
|
|
3a31e973c0 | ||
|
|
e15511a92d | ||
|
|
4656ca0517 | ||
|
|
1f66430364 | ||
|
|
117533e329 |
4
.github/workflows/cve-scan.yml
vendored
4
.github/workflows/cve-scan.yml
vendored
@@ -16,10 +16,10 @@ jobs:
|
|||||||
govulncheck:
|
govulncheck:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- uses: ./.github/actions/runner-cleanup
|
- uses: ./.github/actions/runner-cleanup
|
||||||
- name: Vulnerability scan
|
- name: Vulnerability scan
|
||||||
id: govulncheck
|
id: govulncheck
|
||||||
uses: golang/govulncheck-action@v1
|
uses: golang/govulncheck-action@b625fbe08f3bccbe446d94fbf87fcc875a4f50ee # v1.0.4
|
||||||
with:
|
with:
|
||||||
repo-checkout: false
|
repo-checkout: false
|
||||||
|
|||||||
12
.github/workflows/e2e.yml
vendored
12
.github/workflows/e2e.yml
vendored
@@ -14,11 +14,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Disk Cleanup
|
- name: Disk Cleanup
|
||||||
uses: ./.github/actions/runner-cleanup
|
uses: ./.github/actions/runner-cleanup
|
||||||
- name: Setup Kubernetes
|
- name: Setup Kubernetes
|
||||||
uses: helm/kind-action@v1.14.0
|
uses: helm/kind-action@ef37e7f390d99f746eb8b610417061a60e82a6cc # v1.14.0
|
||||||
with:
|
with:
|
||||||
cluster_name: kind
|
cluster_name: kind
|
||||||
- name: Build container image
|
- name: Build container image
|
||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
./test/build.sh
|
./test/build.sh
|
||||||
kind load docker-image test/podinfo:latest
|
kind load docker-image test/podinfo:latest
|
||||||
- name: Setup Helm
|
- name: Setup Helm
|
||||||
uses: azure/setup-helm@v4
|
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
|
||||||
with:
|
with:
|
||||||
version: v4.1.0
|
version: v4.1.0
|
||||||
- name: Deploy
|
- name: Deploy
|
||||||
@@ -49,12 +49,12 @@ jobs:
|
|||||||
PODINFO_MODULE_URL: "oci://localhost:5000/podinfo"
|
PODINFO_MODULE_URL: "oci://localhost:5000/podinfo"
|
||||||
PODINFO_VERSION: "0.0.0-devel"
|
PODINFO_VERSION: "0.0.0-devel"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- uses: ./.github/actions/runner-cleanup
|
- uses: ./.github/actions/runner-cleanup
|
||||||
- name: Setup Timoni
|
- name: Setup Timoni
|
||||||
uses: stefanprodan/timoni/actions/setup@main
|
uses: stefanprodan/timoni/actions/setup@c68e33a34f17c7ca93c7fc6717d61a14819276dc # v0.26.0
|
||||||
- name: Setup Kubernetes
|
- name: Setup Kubernetes
|
||||||
uses: helm/kind-action@v1.14.0
|
uses: helm/kind-action@ef37e7f390d99f746eb8b610417061a60e82a6cc # v1.14.0
|
||||||
with:
|
with:
|
||||||
cluster_name: kind
|
cluster_name: kind
|
||||||
- name: Build container
|
- name: Build container
|
||||||
|
|||||||
39
.github/workflows/release.yml
vendored
39
.github/workflows/release.yml
vendored
@@ -15,16 +15,15 @@ jobs:
|
|||||||
contents: write # needed to write releases
|
contents: write # needed to write releases
|
||||||
id-token: write # needed for keyless signing
|
id-token: write # needed for keyless signing
|
||||||
packages: write # needed for ghcr access
|
packages: write # needed for ghcr access
|
||||||
|
attestations: write # needed for provenance
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- uses: ./.github/actions/runner-cleanup
|
- uses: ./.github/actions/runner-cleanup
|
||||||
- uses: sigstore/cosign-installer@v4.0.0
|
- uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
|
||||||
with:
|
- uses: fluxcd/flux2/action@871be9b40d53627786d3a3835a3ddba1e3234bd2 # v2.8.3
|
||||||
cosign-release: v2.6.1
|
- uses: stefanprodan/timoni/actions/setup@c68e33a34f17c7ca93c7fc6717d61a14819276dc # v0.26.0
|
||||||
- uses: fluxcd/flux2/action@v2.8.1
|
|
||||||
- uses: stefanprodan/timoni/actions/setup@v0.26.0
|
|
||||||
- name: Setup Notation CLI
|
- name: Setup Notation CLI
|
||||||
uses: notaryproject/notation-action/setup@v1
|
uses: notaryproject/notation-action/setup@b6fee73110795d6793253c673bd723f12bcf9bbb # v1.2.2
|
||||||
with:
|
with:
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
- name: Setup Notation signing keys
|
- name: Setup Notation signing keys
|
||||||
@@ -36,28 +35,28 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
NOTATION_KEY: ${{ secrets.NOTATION_SIGNING_KEY }}
|
NOTATION_KEY: ${{ secrets.NOTATION_SIGNING_KEY }}
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||||
with:
|
with:
|
||||||
go-version: 1.26.x
|
go-version: 1.26.x
|
||||||
- name: Setup Helm
|
- name: Setup Helm
|
||||||
uses: azure/setup-helm@v4
|
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
|
||||||
with:
|
with:
|
||||||
version: v4.1.1
|
version: v4.1.1
|
||||||
- name: Setup QEMU
|
- name: Setup QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||||
with:
|
with:
|
||||||
platforms: all
|
platforms: all
|
||||||
- name: Setup Docker Buildx
|
- name: Setup Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
@@ -73,7 +72,7 @@ jobs:
|
|||||||
echo "REVISION=${GITHUB_SHA}" >> $GITHUB_OUTPUT
|
echo "REVISION=${GITHUB_SHA}" >> $GITHUB_OUTPUT
|
||||||
- name: Generate images meta
|
- name: Generate images meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
docker.io/stefanprodan/podinfo
|
docker.io/stefanprodan/podinfo
|
||||||
@@ -82,7 +81,7 @@ jobs:
|
|||||||
type=raw,value=${{ steps.prep.outputs.VERSION }}
|
type=raw,value=${{ steps.prep.outputs.VERSION }}
|
||||||
type=raw,value=latest
|
type=raw,value=latest
|
||||||
- name: Publish multi-arch image
|
- name: Publish multi-arch image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||||
with:
|
with:
|
||||||
sbom: true
|
sbom: true
|
||||||
provenance: true
|
provenance: true
|
||||||
@@ -125,7 +124,7 @@ jobs:
|
|||||||
cosign sign ghcr.io/stefanprodan/charts/podinfo:${{ steps.prep.outputs.VERSION }} --yes
|
cosign sign ghcr.io/stefanprodan/charts/podinfo:${{ steps.prep.outputs.VERSION }} --yes
|
||||||
cosign sign ghcr.io/stefanprodan/manifests/podinfo:${{ steps.prep.outputs.VERSION }} --yes
|
cosign sign ghcr.io/stefanprodan/manifests/podinfo:${{ steps.prep.outputs.VERSION }} --yes
|
||||||
- name: Publish base image
|
- name: Publish base image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
@@ -134,7 +133,7 @@ jobs:
|
|||||||
file: ./Dockerfile.base
|
file: ./Dockerfile.base
|
||||||
tags: docker.io/stefanprodan/podinfo-base:latest
|
tags: docker.io/stefanprodan/podinfo-base:latest
|
||||||
- name: Publish helm chart
|
- name: Publish helm chart
|
||||||
uses: stefanprodan/helm-gh-pages@master
|
uses: stefanprodan/helm-gh-pages@0ad2bb377311d61ac04ad9eb6f252fb68e207260 # v1.7.0
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Publish config artifact
|
- name: Publish config artifact
|
||||||
@@ -160,9 +159,13 @@ jobs:
|
|||||||
notation sign --signature-format cose ghcr.io/stefanprodan/podinfo-deploy:${{ steps.prep.outputs.VERSION }}
|
notation sign --signature-format cose ghcr.io/stefanprodan/podinfo-deploy:${{ steps.prep.outputs.VERSION }}
|
||||||
notation sign --signature-format cose ghcr.io/stefanprodan/podinfo-deploy:latest
|
notation sign --signature-format cose ghcr.io/stefanprodan/podinfo-deploy:latest
|
||||||
- name: Publish release
|
- name: Publish release
|
||||||
uses: goreleaser/goreleaser-action@v7
|
uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
args: release --skip=validate
|
args: release --skip=validate
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Attest release
|
||||||
|
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
|
||||||
|
with:
|
||||||
|
subject-checksums: ./dist/podinfo_${{ steps.prep.outputs.VERSION }}_checksums.txt
|
||||||
|
|||||||
12
.github/workflows/test.yml
vendored
12
.github/workflows/test.yml
vendored
@@ -17,29 +17,29 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- uses: ./.github/actions/runner-cleanup
|
- uses: ./.github/actions/runner-cleanup
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||||
with:
|
with:
|
||||||
go-version: 1.26.x
|
go-version: 1.26.x
|
||||||
cache-dependency-path: |
|
cache-dependency-path: |
|
||||||
**/go.sum
|
**/go.sum
|
||||||
**/go.mod
|
**/go.mod
|
||||||
- name: Setup kubectl
|
- name: Setup kubectl
|
||||||
uses: azure/setup-kubectl@v4
|
uses: azure/setup-kubectl@15650b3ad78fff148532a140b8a4c821796b2d7b # v5.0.0
|
||||||
with:
|
with:
|
||||||
version: v${{ env.KUBERNETES_VERSION }}
|
version: v${{ env.KUBERNETES_VERSION }}
|
||||||
- name: Setup kubeconform
|
- name: Setup kubeconform
|
||||||
uses: ./.github/actions/kubeconform
|
uses: ./.github/actions/kubeconform
|
||||||
- name: Setup Helm
|
- name: Setup Helm
|
||||||
uses: azure/setup-helm@v4
|
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
|
||||||
with:
|
with:
|
||||||
version: v${{ env.HELM_VERSION }}
|
version: v${{ env.HELM_VERSION }}
|
||||||
- name: Setup CUE
|
- name: Setup CUE
|
||||||
uses: cue-lang/setup-cue@v1.0.1
|
uses: cue-lang/setup-cue@a93fa358375740cd8b0078f76355512b9208acb1 # v1.0.1
|
||||||
- name: Setup Timoni
|
- name: Setup Timoni
|
||||||
uses: stefanprodan/timoni/actions/setup@v0.26.0
|
uses: stefanprodan/timoni/actions/setup@c68e33a34f17c7ca93c7fc6717d61a14819276dc # v0.26.0
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
run: make test
|
run: make test
|
||||||
- name: Validate Helm chart
|
- name: Validate Helm chart
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
version: 6.10.2
|
version: 6.11.2
|
||||||
appVersion: 6.10.2
|
appVersion: 6.11.2
|
||||||
name: podinfo
|
name: podinfo
|
||||||
engine: gotpl
|
engine: gotpl
|
||||||
description: Podinfo Helm chart for Kubernetes
|
description: Podinfo Helm chart for Kubernetes
|
||||||
|
|||||||
42
charts/podinfo/templates/grpcroute.yaml
Normal file
42
charts/podinfo/templates/grpcroute.yaml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{{- if .Values.grpcRoute.enabled -}}
|
||||||
|
{{- $fullName := include "podinfo.fullname" . -}}
|
||||||
|
{{- $grpcPort := .Values.service.grpcPort -}}
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
kind: GRPCRoute
|
||||||
|
metadata:
|
||||||
|
name: {{ $fullName }}
|
||||||
|
namespace: {{ include "podinfo.namespace" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "podinfo.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.grpcRoute.additionalLabels }}
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.grpcRoute.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
parentRefs:
|
||||||
|
{{- with .Values.grpcRoute.parentRefs }}
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.grpcRoute.hostnames }}
|
||||||
|
hostnames:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
rules:
|
||||||
|
{{- range .Values.grpcRoute.rules }}
|
||||||
|
- backendRefs:
|
||||||
|
- name: {{ $fullName }}
|
||||||
|
port: {{ $grpcPort }}
|
||||||
|
weight: 1
|
||||||
|
{{- with .matches }}
|
||||||
|
matches:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .filters }}
|
||||||
|
filters:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -8,7 +8,7 @@ backends: []
|
|||||||
|
|
||||||
image:
|
image:
|
||||||
repository: ghcr.io/stefanprodan/podinfo
|
repository: ghcr.io/stefanprodan/podinfo
|
||||||
tag: 6.10.2
|
tag: 6.11.2
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
ui:
|
ui:
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ backends: []
|
|||||||
|
|
||||||
image:
|
image:
|
||||||
repository: ghcr.io/stefanprodan/podinfo
|
repository: ghcr.io/stefanprodan/podinfo
|
||||||
tag: 6.10.2
|
tag: 6.11.2
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
pullSecrets: []
|
pullSecrets: []
|
||||||
|
|
||||||
@@ -232,6 +232,28 @@ httpRoute:
|
|||||||
type: PathPrefix
|
type: PathPrefix
|
||||||
value: /
|
value: /
|
||||||
|
|
||||||
|
# -- Expose the gRPC service via Gateway GRPCRoute
|
||||||
|
# Requires a Gateway controller with GRPCRoute support
|
||||||
|
# Docs https://gateway-api.sigs.k8s.io/guides/grpc-routing/
|
||||||
|
grpcRoute:
|
||||||
|
# GRPCRoute enabled.
|
||||||
|
enabled: false
|
||||||
|
# Add additional labels to the GRPCRoute.
|
||||||
|
additionalLabels: {}
|
||||||
|
# GRPCRoute annotations.
|
||||||
|
annotations: {}
|
||||||
|
# Which Gateways this Route is attached to.
|
||||||
|
parentRefs:
|
||||||
|
- name: gateway
|
||||||
|
sectionName: http
|
||||||
|
# namespace: default
|
||||||
|
# Hostnames matching HTTP header.
|
||||||
|
hostnames:
|
||||||
|
- podinfo.local
|
||||||
|
# List of rules applied.
|
||||||
|
rules:
|
||||||
|
- {}
|
||||||
|
|
||||||
# create Prometheus Operator monitor
|
# create Prometheus Operator monitor
|
||||||
serviceMonitor:
|
serviceMonitor:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|||||||
@@ -12,10 +12,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
"google.golang.org/grpc/health/grpc_health_v1"
|
"google.golang.org/grpc/health/grpc_health_v1"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
@@ -27,6 +30,7 @@ var (
|
|||||||
body string
|
body string
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
grpcServiceName string
|
grpcServiceName string
|
||||||
|
grpcTLS bool
|
||||||
)
|
)
|
||||||
|
|
||||||
var checkCmd = &cobra.Command{
|
var checkCmd = &cobra.Command{
|
||||||
@@ -63,6 +67,13 @@ var checkgRPCCmd = &cobra.Command{
|
|||||||
RunE: runCheckgPRC,
|
RunE: runCheckgPRC,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var checkWsCmd = &cobra.Command{
|
||||||
|
Use: `ws [address]`,
|
||||||
|
Short: "WebSocket round-trip health check",
|
||||||
|
Example: ` check ws ws://localhost:9898/ws/echo --retry=1 --delay=2s --timeout=5s`,
|
||||||
|
RunE: runCheckWs,
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
checkUrlCmd.Flags().StringVar(&method, "method", "GET", "HTTP method")
|
checkUrlCmd.Flags().StringVar(&method, "method", "GET", "HTTP method")
|
||||||
checkUrlCmd.Flags().StringVar(&body, "body", "", "HTTP POST/PUT content")
|
checkUrlCmd.Flags().StringVar(&body, "body", "", "HTTP POST/PUT content")
|
||||||
@@ -80,10 +91,16 @@ func init() {
|
|||||||
checkgRPCCmd.Flags().DurationVar(&retryDelay, "delay", 1*time.Second, "wait duration between retries")
|
checkgRPCCmd.Flags().DurationVar(&retryDelay, "delay", 1*time.Second, "wait duration between retries")
|
||||||
checkgRPCCmd.Flags().DurationVar(&timeout, "timeout", 5*time.Second, "timeout")
|
checkgRPCCmd.Flags().DurationVar(&timeout, "timeout", 5*time.Second, "timeout")
|
||||||
checkgRPCCmd.Flags().StringVar(&grpcServiceName, "service", "", "gRPC service name")
|
checkgRPCCmd.Flags().StringVar(&grpcServiceName, "service", "", "gRPC service name")
|
||||||
|
checkgRPCCmd.Flags().BoolVar(&grpcTLS, "tls", false, "use TLS for gRPC connection")
|
||||||
checkCmd.AddCommand(checkgRPCCmd)
|
checkCmd.AddCommand(checkgRPCCmd)
|
||||||
|
|
||||||
checkCmd.AddCommand(checkCertCmd)
|
checkCmd.AddCommand(checkCertCmd)
|
||||||
|
|
||||||
|
checkWsCmd.Flags().IntVar(&retryCount, "retry", 0, "times to retry the WebSocket check")
|
||||||
|
checkWsCmd.Flags().DurationVar(&retryDelay, "delay", 1*time.Second, "wait duration between retries")
|
||||||
|
checkWsCmd.Flags().DurationVar(&timeout, "timeout", 5*time.Second, "timeout")
|
||||||
|
checkCmd.AddCommand(checkWsCmd)
|
||||||
|
|
||||||
rootCmd.AddCommand(checkCmd)
|
rootCmd.AddCommand(checkCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,6 +279,72 @@ func fmtContentLength(b int64) string {
|
|||||||
return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp])
|
return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runCheckWs(cmd *cobra.Command, args []string) error {
|
||||||
|
if retryCount < 0 {
|
||||||
|
return fmt.Errorf("--retry is required")
|
||||||
|
}
|
||||||
|
if len(args) < 1 {
|
||||||
|
return fmt.Errorf("address is required! example: check ws wss://localhost:9898/ws/echo")
|
||||||
|
}
|
||||||
|
|
||||||
|
address := args[0]
|
||||||
|
if !strings.HasPrefix(address, "ws://") && !strings.HasPrefix(address, "wss://") {
|
||||||
|
return fmt.Errorf("address must start with ws:// or wss://")
|
||||||
|
}
|
||||||
|
|
||||||
|
for n := 0; n <= retryCount; n++ {
|
||||||
|
if n != 0 {
|
||||||
|
time.Sleep(retryDelay)
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer := websocket.Dialer{
|
||||||
|
HandshakeTimeout: timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, _, err := dialer.Dial(address, nil)
|
||||||
|
if err != nil {
|
||||||
|
logger.Info("check failed",
|
||||||
|
zap.String("address", address),
|
||||||
|
zap.Error(err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := "podinfo-check"
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
conn.SetWriteDeadline(start.Add(timeout))
|
||||||
|
if err := conn.WriteMessage(websocket.TextMessage, []byte(msg)); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
logger.Info("check failed",
|
||||||
|
zap.String("address", address),
|
||||||
|
zap.Error(err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.SetReadDeadline(time.Now().Add(timeout))
|
||||||
|
_, resp, err := conn.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
logger.Info("check failed",
|
||||||
|
zap.String("address", address),
|
||||||
|
zap.Error(err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rtt := time.Since(start)
|
||||||
|
conn.Close()
|
||||||
|
|
||||||
|
logger.Info("check succeed",
|
||||||
|
zap.String("address", address),
|
||||||
|
zap.Duration("round-trip", rtt),
|
||||||
|
zap.Int("response size", len(resp)))
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func runCheckgPRC(cmd *cobra.Command, args []string) error {
|
func runCheckgPRC(cmd *cobra.Command, args []string) error {
|
||||||
if retryCount < 0 {
|
if retryCount < 0 {
|
||||||
return fmt.Errorf("--retry is required")
|
return fmt.Errorf("--retry is required")
|
||||||
@@ -271,12 +354,19 @@ func runCheckgPRC(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
address := args[0]
|
address := args[0]
|
||||||
|
|
||||||
|
var creds grpc.DialOption
|
||||||
|
if grpcTLS {
|
||||||
|
creds = grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))
|
||||||
|
} else {
|
||||||
|
creds = grpc.WithTransportCredentials(insecure.NewCredentials())
|
||||||
|
}
|
||||||
|
|
||||||
for n := 0; n <= retryCount; n++ {
|
for n := 0; n <= retryCount; n++ {
|
||||||
if n != 1 {
|
if n != 0 {
|
||||||
time.Sleep(retryDelay)
|
time.Sleep(retryDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := grpc.Dial(address, grpc.WithInsecure())
|
conn, err := grpc.NewClient(address, creds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Info("check failed",
|
logger.Info("check failed",
|
||||||
zap.String("address", address),
|
zap.String("address", address),
|
||||||
@@ -291,13 +381,14 @@ func runCheckgPRC(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if stat, ok := status.FromError(err); ok && stat.Code() == codes.Unimplemented {
|
if stat, ok := status.FromError(err); ok && stat.Code() == codes.Unimplemented {
|
||||||
logger.Info("gPRC health protocol not implemented")
|
logger.Info("gRPC health protocol not implemented")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
} else {
|
} else {
|
||||||
logger.Info("check failed",
|
logger.Info("check failed",
|
||||||
zap.String("address", address),
|
zap.String("address", address),
|
||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
}
|
}
|
||||||
|
conn.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,7 +396,6 @@ func runCheckgPRC(cmd *cobra.Command, args []string) error {
|
|||||||
logger.Info("check succeed",
|
logger.Info("check succeed",
|
||||||
zap.String("status", resp.GetStatus().String()))
|
zap.String("status", resp.GetStatus().String()))
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -10,6 +11,11 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
"go.opentelemetry.io/contrib/bridges/otelzap"
|
||||||
|
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
|
||||||
|
sdklog "go.opentelemetry.io/otel/sdk/log"
|
||||||
|
"go.opentelemetry.io/otel/sdk/resource"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
|
|
||||||
@@ -53,7 +59,7 @@ func main() {
|
|||||||
fs.Int("stress-cpu", 0, "number of CPU cores with 100 load")
|
fs.Int("stress-cpu", 0, "number of CPU cores with 100 load")
|
||||||
fs.Int("stress-memory", 0, "MB of data to load into memory")
|
fs.Int("stress-memory", 0, "MB of data to load into memory")
|
||||||
fs.String("cache-server", "", "Redis address in the format 'tcp://<host>:<port>'")
|
fs.String("cache-server", "", "Redis address in the format 'tcp://<host>:<port>'")
|
||||||
fs.String("otel-service-name", "", "service name for reporting to open telemetry address, when not set tracing is disabled")
|
fs.String("otel-service-name", "", "service name for OpenTelemetry, when not set tracing and log export are disabled")
|
||||||
|
|
||||||
versionFlag := fs.BoolP("version", "v", false, "get version number")
|
versionFlag := fs.BoolP("version", "v", false, "get version number")
|
||||||
|
|
||||||
@@ -93,8 +99,18 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initialize OTel log provider if service name is set
|
||||||
|
var loggerProvider *sdklog.LoggerProvider
|
||||||
|
if otelServiceName := viper.GetString("otel-service-name"); otelServiceName != "" {
|
||||||
|
var err error
|
||||||
|
loggerProvider, err = initLoggerProvider(context.Background(), otelServiceName)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error initializing OTel log provider: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// configure logging
|
// configure logging
|
||||||
logger, _ := initZap(viper.GetString("level"))
|
logger, _ := initZap(viper.GetString("level"), loggerProvider)
|
||||||
defer logger.Sync()
|
defer logger.Sync()
|
||||||
stdLog := zap.RedirectStdLog(logger)
|
stdLog := zap.RedirectStdLog(logger)
|
||||||
defer stdLog()
|
defer stdLog()
|
||||||
@@ -163,10 +179,29 @@ func main() {
|
|||||||
// graceful shutdown
|
// graceful shutdown
|
||||||
stopCh := signals.SetupSignalHandler()
|
stopCh := signals.SetupSignalHandler()
|
||||||
sd, _ := signals.NewShutdown(srvCfg.ServerShutdownTimeout, logger)
|
sd, _ := signals.NewShutdown(srvCfg.ServerShutdownTimeout, logger)
|
||||||
|
sd.SetLoggerProvider(loggerProvider)
|
||||||
sd.Graceful(stopCh, httpServer, httpsServer, grpcServer, healthy, ready)
|
sd.Graceful(stopCh, httpServer, httpsServer, grpcServer, healthy, ready)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initZap(logLevel string) (*zap.Logger, error) {
|
func initLoggerProvider(ctx context.Context, serviceName string) (*sdklog.LoggerProvider, error) {
|
||||||
|
exporter, err := otlploggrpc.New(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("creating OTLP log exporter: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
provider := sdklog.NewLoggerProvider(
|
||||||
|
sdklog.WithProcessor(sdklog.NewBatchProcessor(exporter)),
|
||||||
|
sdklog.WithResource(resource.NewWithAttributes(
|
||||||
|
semconv.SchemaURL,
|
||||||
|
semconv.ServiceNameKey.String(serviceName),
|
||||||
|
semconv.ServiceVersionKey.String(version.VERSION),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
|
||||||
|
return provider, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initZap(logLevel string, loggerProvider *sdklog.LoggerProvider) (*zap.Logger, error) {
|
||||||
level := zap.NewAtomicLevelAt(zapcore.InfoLevel)
|
level := zap.NewAtomicLevelAt(zapcore.InfoLevel)
|
||||||
switch logLevel {
|
switch logLevel {
|
||||||
case "debug":
|
case "debug":
|
||||||
@@ -210,7 +245,21 @@ func initZap(logLevel string) (*zap.Logger, error) {
|
|||||||
ErrorOutputPaths: []string{"stderr"},
|
ErrorOutputPaths: []string{"stderr"},
|
||||||
}
|
}
|
||||||
|
|
||||||
return zapConfig.Build()
|
logger, err := zapConfig.Build()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if loggerProvider != nil {
|
||||||
|
otelCore := otelzap.NewCore("github.com/stefanprodan/podinfo",
|
||||||
|
otelzap.WithLoggerProvider(loggerProvider),
|
||||||
|
)
|
||||||
|
logger = logger.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core {
|
||||||
|
return zapcore.NewTee(core, otelCore)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return logger, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var stressMemoryPayload []byte
|
var stressMemoryPayload []byte
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: backend
|
- name: backend
|
||||||
image: ghcr.io/stefanprodan/podinfo:6.10.2
|
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ spec:
|
|||||||
restartPolicy: Never
|
restartPolicy: Never
|
||||||
containers:
|
containers:
|
||||||
- name: backup
|
- name: backup
|
||||||
image: ghcr.io/stefanprodan/podinfo:6.10.2
|
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
command:
|
command:
|
||||||
- /bin/sh
|
- /bin/sh
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ spec:
|
|||||||
restartPolicy: OnFailure
|
restartPolicy: OnFailure
|
||||||
containers:
|
containers:
|
||||||
- name: healthcheck
|
- name: healthcheck
|
||||||
image: ghcr.io/stefanprodan/podinfo:6.10.2
|
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
command:
|
command:
|
||||||
- /bin/sh
|
- /bin/sh
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ spec:
|
|||||||
restartPolicy: OnFailure
|
restartPolicy: OnFailure
|
||||||
containers:
|
containers:
|
||||||
- name: healthcheck
|
- name: healthcheck
|
||||||
image: ghcr.io/stefanprodan/podinfo:6.10.2
|
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
command:
|
command:
|
||||||
- /bin/sh
|
- /bin/sh
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ spec:
|
|||||||
serviceAccountName: database
|
serviceAccountName: database
|
||||||
containers:
|
containers:
|
||||||
- name: database
|
- name: database
|
||||||
image: ghcr.io/stefanprodan/podinfo:6.10.2
|
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
ports:
|
ports:
|
||||||
- name: db
|
- name: db
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ spec:
|
|||||||
serviceAccountName: database
|
serviceAccountName: database
|
||||||
containers:
|
containers:
|
||||||
- name: database
|
- name: database
|
||||||
image: ghcr.io/stefanprodan/podinfo:6.10.2
|
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
ports:
|
ports:
|
||||||
- name: db
|
- name: db
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: frontend
|
- name: frontend
|
||||||
image: ghcr.io/stefanprodan/podinfo:6.10.2
|
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ spec:
|
|||||||
serviceAccountName: webapp
|
serviceAccountName: webapp
|
||||||
containers:
|
containers:
|
||||||
- name: backend
|
- name: backend
|
||||||
image: ghcr.io/stefanprodan/podinfo:6.10.2
|
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ spec:
|
|||||||
serviceAccountName: webapp
|
serviceAccountName: webapp
|
||||||
containers:
|
containers:
|
||||||
- name: frontend
|
- name: frontend
|
||||||
image: ghcr.io/stefanprodan/podinfo:6.10.2
|
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
|
|||||||
6
go.mod
6
go.mod
@@ -16,6 +16,7 @@ require (
|
|||||||
github.com/spf13/viper v1.21.0
|
github.com/spf13/viper v1.21.0
|
||||||
github.com/swaggo/http-swagger v1.3.4
|
github.com/swaggo/http-swagger v1.3.4
|
||||||
github.com/swaggo/swag v1.16.6
|
github.com/swaggo/swag v1.16.6
|
||||||
|
go.opentelemetry.io/contrib/bridges/otelzap v0.15.0
|
||||||
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.65.0
|
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.65.0
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.65.0
|
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.65.0
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0
|
||||||
@@ -24,13 +25,15 @@ require (
|
|||||||
go.opentelemetry.io/contrib/propagators/jaeger v1.40.0
|
go.opentelemetry.io/contrib/propagators/jaeger v1.40.0
|
||||||
go.opentelemetry.io/contrib/propagators/ot v1.40.0
|
go.opentelemetry.io/contrib/propagators/ot v1.40.0
|
||||||
go.opentelemetry.io/otel v1.40.0
|
go.opentelemetry.io/otel v1.40.0
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0
|
||||||
go.opentelemetry.io/otel/sdk v1.40.0
|
go.opentelemetry.io/otel/sdk v1.40.0
|
||||||
|
go.opentelemetry.io/otel/sdk/log v0.16.0
|
||||||
go.opentelemetry.io/otel/trace v1.40.0
|
go.opentelemetry.io/otel/trace v1.40.0
|
||||||
go.uber.org/zap v1.27.1
|
go.uber.org/zap v1.27.1
|
||||||
golang.org/x/net v0.51.0
|
golang.org/x/net v0.51.0
|
||||||
google.golang.org/grpc v1.79.1
|
google.golang.org/grpc v1.79.3
|
||||||
google.golang.org/protobuf v1.36.11
|
google.golang.org/protobuf v1.36.11
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -66,6 +69,7 @@ require (
|
|||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/swaggo/files v1.0.1 // indirect
|
github.com/swaggo/files v1.0.1 // indirect
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||||
|
go.opentelemetry.io/otel/log v0.16.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
|
|||||||
16
go.sum
16
go.sum
@@ -117,6 +117,8 @@ github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4
|
|||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
|
go.opentelemetry.io/contrib/bridges/otelzap v0.15.0 h1:x4qzjKkTl2hXmLl+IviSXvzaTyCJSYvpFZL5SRVLBxs=
|
||||||
|
go.opentelemetry.io/contrib/bridges/otelzap v0.15.0/go.mod h1:h7dZHJgqkzUiKFXCTJBrPWH0LEZaZXBFzKWstjWBRxw=
|
||||||
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.65.0 h1:LIMn2KWRS0jRDDHYyIEYgKWsMwufA9GXusJiwik0u64=
|
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.65.0 h1:LIMn2KWRS0jRDDHYyIEYgKWsMwufA9GXusJiwik0u64=
|
||||||
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.65.0/go.mod h1:JwJa4o3Wq+4Yz2BjlYFGWyx2h0Fw1lnoj5kpsaTI97o=
|
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.65.0/go.mod h1:JwJa4o3Wq+4Yz2BjlYFGWyx2h0Fw1lnoj5kpsaTI97o=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.65.0 h1:ab5U7DpTjjN8pNgwqlA/s0Csb+N2Raqo9eTSDhfg4Z8=
|
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.65.0 h1:ab5U7DpTjjN8pNgwqlA/s0Csb+N2Raqo9eTSDhfg4Z8=
|
||||||
@@ -133,16 +135,26 @@ go.opentelemetry.io/contrib/propagators/ot v1.40.0 h1:Lon8J5SPmWaL1Ko2TIlCNHJ42/
|
|||||||
go.opentelemetry.io/contrib/propagators/ot v1.40.0/go.mod h1:dKWtJTlp1Yj+8Cneye5idO46eRPIbi23qVuJYKjNnvY=
|
go.opentelemetry.io/contrib/propagators/ot v1.40.0/go.mod h1:dKWtJTlp1Yj+8Cneye5idO46eRPIbi23qVuJYKjNnvY=
|
||||||
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||||
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0 h1:ZVg+kCXxd9LtAaQNKBxAvJ5NpMf7LpvEr4MIZqb0TMQ=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0/go.mod h1:hh0tMeZ75CCXrHd9OXRYxTlCAdxcXioWHFIpYw2rZu8=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 h1:MzfofMZN8ulNqobCmCAVbqVL5syHw+eB2qPRkCMA/fQ=
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 h1:MzfofMZN8ulNqobCmCAVbqVL5syHw+eB2qPRkCMA/fQ=
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0/go.mod h1:E73G9UFtKRXrxhBsHtG00TB5WxX57lpsQzogDkqBTz8=
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0/go.mod h1:E73G9UFtKRXrxhBsHtG00TB5WxX57lpsQzogDkqBTz8=
|
||||||
|
go.opentelemetry.io/otel/log v0.16.0 h1:DeuBPqCi6pQwtCK0pO4fvMB5eBq6sNxEnuTs88pjsN4=
|
||||||
|
go.opentelemetry.io/otel/log v0.16.0/go.mod h1:rWsmqNVTLIA8UnwYVOItjyEZDbKIkMxdQunsIhpUMes=
|
||||||
|
go.opentelemetry.io/otel/log/logtest v0.16.0 h1:jr1CG3Z6FD9pwUaL/D0s0X4lY2ZVm1jP3JfCtzGxUmE=
|
||||||
|
go.opentelemetry.io/otel/log/logtest v0.16.0/go.mod h1:qeeZw+cI/rAtCzZ03Kq1ozq6C4z/PCa+K+bb0eJfKNs=
|
||||||
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||||
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
||||||
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
||||||
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
||||||
|
go.opentelemetry.io/otel/sdk/log v0.16.0 h1:e/b4bdlQwC5fnGtG3dlXUrNOnP7c8YLVSpSfEBIkTnI=
|
||||||
|
go.opentelemetry.io/otel/sdk/log v0.16.0/go.mod h1:JKfP3T6ycy7QEuv3Hj8oKDy7KItrEkus8XJE6EoSzw4=
|
||||||
|
go.opentelemetry.io/otel/sdk/log/logtest v0.16.0 h1:/XVkpZ41rVRTP4DfMgYv1nEtNmf65XPPyAdqV90TMy4=
|
||||||
|
go.opentelemetry.io/otel/sdk/log/logtest v0.16.0/go.mod h1:iOOPgQr5MY9oac/F5W86mXdeyWZGleIx3uXO98X2R6Y=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
|
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
||||||
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||||
@@ -205,8 +217,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:
|
|||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: podinfod
|
- name: podinfod
|
||||||
image: ghcr.io/stefanprodan/podinfo:6.10.2
|
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
|
|||||||
@@ -1,27 +1,34 @@
|
|||||||
# Tracing Demo
|
# Tracing & Logging Demo
|
||||||
|
|
||||||
The directory contains sample [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector)
|
The directory contains sample [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector)
|
||||||
and [Jaeger](https://www.jaegertracing.io) configurations for a tracing demo.
|
and [Jaeger](https://www.jaegertracing.io) / [Loki](https://grafana.com/oss/loki/) configurations for a tracing and logging demo.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
The provided [docker-compose.yaml](docker-compose.yaml) sets up 4 Containers
|
The provided [docker-compose.yaml](docker-compose.yaml) sets up 6 containers:
|
||||||
|
|
||||||
1. PodInfo Frontend on port 9898
|
1. PodInfo Frontend on port 9898
|
||||||
2. PodInfo Backend on port 9899
|
2. PodInfo Backend on port 9899
|
||||||
3. OpenTelemetry Collector listening on port 4317 for GRPC
|
3. OpenTelemetry Collector listening on port 4317 for GRPC
|
||||||
4. Jaeger all-in-one listening on multiple ports
|
4. Jaeger all-in-one with UI on port 16686
|
||||||
|
5. Loki on port 3100
|
||||||
|
6. Grafana on port 3000
|
||||||
|
|
||||||
## How does it work?
|
## How does it work?
|
||||||
|
|
||||||
The frontend pods are configured to call onto the backend pods. Both the podinfo
|
The frontend pod is configured to call the backend pod. Both podinfo pods send traces
|
||||||
pods are configured to send traces over to the collector at port 4317 using GRPC.
|
and logs to the collector at port 4317 using OTLP gRPC.
|
||||||
The collector forwards all received spans to Jaeger over port 14250 and Jaeger
|
|
||||||
exposes a UI over port `16686`.
|
The collector forwards:
|
||||||
|
- **Traces** to Jaeger via OTLP gRPC on port 4317
|
||||||
|
- **Logs** to Loki via OTLP HTTP on port 3100
|
||||||
|
|
||||||
|
Jaeger exposes its UI on port `16686`. Grafana exposes its UI on port `3000` and is
|
||||||
|
pre-configured with both Jaeger and Loki as datasources.
|
||||||
|
|
||||||
## Running it locally
|
## Running it locally
|
||||||
|
|
||||||
1. Start all the Containers
|
1. Start all the containers
|
||||||
```shell
|
```shell
|
||||||
make run
|
make run
|
||||||
```
|
```
|
||||||
@@ -30,8 +37,9 @@ make run
|
|||||||
curl -v http://localhost:9898/status/200
|
curl -v http://localhost:9898/status/200
|
||||||
curl -X POST -v http://localhost:9898/api/echo
|
curl -X POST -v http://localhost:9898/api/echo
|
||||||
```
|
```
|
||||||
3. Visit `http://localhost:16686/` to see the spans
|
3. Visit `http://localhost:16686/` to see traces in Jaeger
|
||||||
4. Stop all the containers
|
4. Visit `http://localhost:3000/` to explore logs in Grafana (Explore → Loki) and traces (Explore → Jaeger)
|
||||||
|
5. Stop all the containers
|
||||||
```shell
|
```shell
|
||||||
make stop
|
make stop
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -5,31 +5,38 @@ services:
|
|||||||
build: ..
|
build: ..
|
||||||
command: ./podinfo --backend-url http://podinfo_backend:9899/status/200 --otel-service-name=podinfo_frontend
|
command: ./podinfo --backend-url http://podinfo_backend:9899/status/200 --otel-service-name=podinfo_frontend
|
||||||
environment:
|
environment:
|
||||||
- OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://otel:4317
|
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otel:4317
|
||||||
ports:
|
ports:
|
||||||
- "9898:9898"
|
- "9898:9898"
|
||||||
podinfo_backend:
|
podinfo_backend:
|
||||||
build: ..
|
build: ..
|
||||||
command: ./podinfo --port 9899 --otel-service-name=podinfo_backend
|
command: ./podinfo --port 9899 --otel-service-name=podinfo_backend
|
||||||
environment:
|
environment:
|
||||||
- OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://otel:4317
|
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otel:4317
|
||||||
ports:
|
ports:
|
||||||
- "9899:9899"
|
- "9899:9899"
|
||||||
otel:
|
otel:
|
||||||
command: --config otel-config.yaml
|
command: --config otel-config.yaml
|
||||||
image: otel/opentelemetry-collector:0.41.0
|
image: otel/opentelemetry-collector-contrib:0.116.1
|
||||||
ports:
|
ports:
|
||||||
- "4317:4317"
|
- "4317:4317"
|
||||||
volumes:
|
volumes:
|
||||||
- ${PWD}/otel-config.yaml:/otel-config.yaml
|
- ${PWD}/otel-config.yaml:/otel-config.yaml
|
||||||
jaeger:
|
loki:
|
||||||
image: jaegertracing/all-in-one:1.29.0
|
image: grafana/loki:3.0.0
|
||||||
|
ports:
|
||||||
|
- "3100:3100"
|
||||||
|
command: -config.file=/etc/loki/local-config.yaml
|
||||||
|
grafana:
|
||||||
|
image: grafana/grafana:10.4.0
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
- GF_AUTH_ANONYMOUS_ENABLED=true
|
||||||
|
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
|
||||||
|
volumes:
|
||||||
|
- ${PWD}/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml
|
||||||
|
jaeger:
|
||||||
|
image: jaegertracing/all-in-one:1.57.0
|
||||||
ports:
|
ports:
|
||||||
- "5775:5775/udp"
|
|
||||||
- "6831:6831/udp"
|
|
||||||
- "6832:6832/udp"
|
|
||||||
- "5778:5778"
|
|
||||||
- "16686:16686"
|
- "16686:16686"
|
||||||
- "14268:14268"
|
|
||||||
- "14250:14250"
|
|
||||||
- "9411:9411"
|
|
||||||
|
|||||||
10
otel/grafana-datasources.yaml
Normal file
10
otel/grafana-datasources.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: 1
|
||||||
|
|
||||||
|
datasources:
|
||||||
|
- name: Loki
|
||||||
|
type: loki
|
||||||
|
url: http://loki:3100
|
||||||
|
isDefault: true
|
||||||
|
- name: Jaeger
|
||||||
|
type: jaeger
|
||||||
|
url: http://jaeger:16686
|
||||||
@@ -2,15 +2,18 @@ receivers:
|
|||||||
otlp:
|
otlp:
|
||||||
protocols:
|
protocols:
|
||||||
grpc:
|
grpc:
|
||||||
|
endpoint: 0.0.0.0:4317
|
||||||
http:
|
http:
|
||||||
|
|
||||||
processors:
|
processors:
|
||||||
|
|
||||||
exporters:
|
exporters:
|
||||||
jaeger:
|
otlp/jaeger:
|
||||||
endpoint: jaeger:14250
|
endpoint: jaeger:4317
|
||||||
tls:
|
tls:
|
||||||
insecure: true
|
insecure: true
|
||||||
|
otlphttp/loki:
|
||||||
|
endpoint: http://loki:3100/otlp
|
||||||
|
|
||||||
extensions:
|
extensions:
|
||||||
health_check:
|
health_check:
|
||||||
@@ -23,4 +26,8 @@ service:
|
|||||||
traces:
|
traces:
|
||||||
receivers: [otlp]
|
receivers: [otlp]
|
||||||
processors: []
|
processors: []
|
||||||
exporters: [jaeger]
|
exporters: [otlp/jaeger]
|
||||||
|
logs:
|
||||||
|
receivers: [otlp]
|
||||||
|
processors: []
|
||||||
|
exporters: [otlphttp/loki]
|
||||||
|
|||||||
@@ -7,11 +7,14 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var validHash = regexp.MustCompile(`^[a-f0-9]{40}$`)
|
||||||
|
|
||||||
// Store godoc
|
// Store godoc
|
||||||
// @Summary Upload file
|
// @Summary Upload file
|
||||||
// @Description writes the posted content to disk at /data/hash and returns the SHA1 hash of the content
|
// @Description writes the posted content to disk at /data/hash and returns the SHA1 hash of the content
|
||||||
@@ -54,12 +57,19 @@ func (s *Server) storeReadHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
hash := mux.Vars(r)["hash"]
|
hash := mux.Vars(r)["hash"]
|
||||||
|
if !validHash.MatchString(hash) {
|
||||||
|
s.ErrorResponse(w, r, span, "invalid hash", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
content, err := os.ReadFile(path.Join(s.config.DataPath, hash))
|
content, err := os.ReadFile(path.Join(s.config.DataPath, hash))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Warn("reading file failed", zap.Error(err), zap.String("file", path.Join(s.config.DataPath, hash)))
|
s.logger.Warn("reading file failed", zap.Error(err), zap.String("file", path.Join(s.config.DataPath, hash)))
|
||||||
s.ErrorResponse(w, r, span, "reading file failed", http.StatusInternalServerError)
|
s.ErrorResponse(w, r, span, "reading file failed", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/octet-stream")
|
||||||
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
|
w.Header().Set("Content-Security-Policy", "default-src 'none'")
|
||||||
w.WriteHeader(http.StatusAccepted)
|
w.WriteHeader(http.StatusAccepted)
|
||||||
w.Write([]byte(content))
|
w.Write([]byte(content))
|
||||||
}
|
}
|
||||||
|
|||||||
82
pkg/api/http/store_test.go
Normal file
82
pkg/api/http/store_test.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStoreReadHandler_ContentType(t *testing.T) {
|
||||||
|
dataDir := t.TempDir()
|
||||||
|
srv := NewMockServer()
|
||||||
|
srv.config.DataPath = dataDir
|
||||||
|
|
||||||
|
// Write an HTML payload to the store.
|
||||||
|
writeReq, err := http.NewRequest("POST", "/store", strings.NewReader("<html><script>alert(1)</script></html>"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
writeRR := httptest.NewRecorder()
|
||||||
|
http.HandlerFunc(srv.storeWriteHandler).ServeHTTP(writeRR, writeReq)
|
||||||
|
|
||||||
|
if writeRR.Code != http.StatusAccepted {
|
||||||
|
t.Fatalf("store write returned status %d, want %d", writeRR.Code, http.StatusAccepted)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read it back and verify Content-Type is application/octet-stream, not text/html.
|
||||||
|
hash := hash("<html><script>alert(1)</script></html>")
|
||||||
|
readReq, err := http.NewRequest("GET", "/store/"+hash, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
readReq = mux.SetURLVars(readReq, map[string]string{"hash": hash})
|
||||||
|
|
||||||
|
readRR := httptest.NewRecorder()
|
||||||
|
http.HandlerFunc(srv.storeReadHandler).ServeHTTP(readRR, readReq)
|
||||||
|
|
||||||
|
if readRR.Code != http.StatusAccepted {
|
||||||
|
t.Fatalf("store read returned status %d, want %d", readRR.Code, http.StatusAccepted)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedHeaders := map[string]string{
|
||||||
|
"Content-Type": "application/octet-stream",
|
||||||
|
"X-Content-Type-Options": "nosniff",
|
||||||
|
"Content-Security-Policy": "default-src 'none'",
|
||||||
|
}
|
||||||
|
for header, want := range expectedHeaders {
|
||||||
|
if got := readRR.Header().Get(header); got != want {
|
||||||
|
t.Errorf("%s = %q, want %q", header, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreReadHandler_PathTraversal(t *testing.T) {
|
||||||
|
srv := NewMockServer()
|
||||||
|
srv.config.DataPath = t.TempDir()
|
||||||
|
|
||||||
|
traversalPaths := []string{
|
||||||
|
"../../../../etc/passwd",
|
||||||
|
"../../../etc/shadow",
|
||||||
|
"..%2f..%2f..%2fetc%2fpasswd",
|
||||||
|
"abc123",
|
||||||
|
"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzg", // 40 chars but not hex
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tp := range traversalPaths {
|
||||||
|
req, err := http.NewRequest("GET", "/store/"+tp, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
req = mux.SetURLVars(req, map[string]string{"hash": tp})
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
http.HandlerFunc(srv.storeReadHandler).ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
if !strings.Contains(rr.Body.String(), "invalid hash") {
|
||||||
|
t.Errorf("path %q: expected 'invalid hash' error, got %q", tp, rr.Body.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gomodule/redigo/redis"
|
"github.com/gomodule/redigo/redis"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
sdklog "go.opentelemetry.io/otel/sdk/log"
|
||||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
@@ -17,9 +18,14 @@ type Shutdown struct {
|
|||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
pool *redis.Pool
|
pool *redis.Pool
|
||||||
tracerProvider *sdktrace.TracerProvider
|
tracerProvider *sdktrace.TracerProvider
|
||||||
|
loggerProvider *sdklog.LoggerProvider
|
||||||
serverShutdownTimeout time.Duration
|
serverShutdownTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Shutdown) SetLoggerProvider(lp *sdklog.LoggerProvider) {
|
||||||
|
s.loggerProvider = lp
|
||||||
|
}
|
||||||
|
|
||||||
func NewShutdown(serverShutdownTimeout time.Duration, logger *zap.Logger) (*Shutdown, error) {
|
func NewShutdown(serverShutdownTimeout time.Duration, logger *zap.Logger) (*Shutdown, error) {
|
||||||
srv := &Shutdown{
|
srv := &Shutdown{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@@ -62,6 +68,13 @@ func (s *Shutdown) Graceful(stopCh <-chan struct{}, httpServer *http.Server, htt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stop OpenTelemetry logger provider
|
||||||
|
if s.loggerProvider != nil {
|
||||||
|
if err := s.loggerProvider.Shutdown(ctx); err != nil {
|
||||||
|
s.logger.Warn("stopping logger provider", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// determine if the GRPC was started
|
// determine if the GRPC was started
|
||||||
if grpcServer != nil {
|
if grpcServer != nil {
|
||||||
s.logger.Info("Shutting down GRPC server", zap.Duration("timeout", s.serverShutdownTimeout))
|
s.logger.Info("Shutting down GRPC server", zap.Duration("timeout", s.serverShutdownTimeout))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package version
|
package version
|
||||||
|
|
||||||
var VERSION = "6.10.2"
|
var VERSION = "6.11.2"
|
||||||
var REVISION = "unknown"
|
var REVISION = "unknown"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ package main
|
|||||||
values: {
|
values: {
|
||||||
image: {
|
image: {
|
||||||
repository: "ghcr.io/stefanprodan/podinfo"
|
repository: "ghcr.io/stefanprodan/podinfo"
|
||||||
tag: "6.10.2"
|
tag: "6.11.2"
|
||||||
digest: ""
|
digest: ""
|
||||||
}
|
}
|
||||||
test: image: {
|
test: image: {
|
||||||
|
|||||||
Reference in New Issue
Block a user