mirror of
https://github.com/stefanprodan/podinfo.git
synced 2026-04-07 03:26:54 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b501abd1f0 | ||
|
|
e0a79a4ddd | ||
|
|
be8baac695 | ||
|
|
f539517440 | ||
|
|
01219a196e | ||
|
|
b9acae4064 | ||
|
|
64a8da1836 | ||
|
|
420d0db8bf | ||
|
|
6b67f2bdd6 | ||
|
|
095b1cd251 | ||
|
|
0a27dbe40c | ||
|
|
2da74a4ec2 | ||
|
|
c7ffdba3bd | ||
|
|
06f7cd3777 | ||
|
|
620b9b7e2c | ||
|
|
83deb7fcb7 | ||
|
|
550ee9f7b9 | ||
|
|
dd185df435 |
4
.github/workflows/cve-scan.yml
vendored
4
.github/workflows/cve-scan.yml
vendored
@@ -16,10 +16,10 @@ jobs:
|
||||
govulncheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: ./.github/actions/runner-cleanup
|
||||
- name: Vulnerability scan
|
||||
id: govulncheck
|
||||
uses: golang/govulncheck-action@v1
|
||||
uses: golang/govulncheck-action@b625fbe08f3bccbe446d94fbf87fcc875a4f50ee # v1.0.4
|
||||
with:
|
||||
repo-checkout: false
|
||||
|
||||
12
.github/workflows/e2e.yml
vendored
12
.github/workflows/e2e.yml
vendored
@@ -14,11 +14,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Disk Cleanup
|
||||
uses: ./.github/actions/runner-cleanup
|
||||
- name: Setup Kubernetes
|
||||
uses: helm/kind-action@v1.14.0
|
||||
uses: helm/kind-action@ef37e7f390d99f746eb8b610417061a60e82a6cc # v1.14.0
|
||||
with:
|
||||
cluster_name: kind
|
||||
- name: Build container image
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
./test/build.sh
|
||||
kind load docker-image test/podinfo:latest
|
||||
- name: Setup Helm
|
||||
uses: azure/setup-helm@v4
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
|
||||
with:
|
||||
version: v4.1.0
|
||||
- name: Deploy
|
||||
@@ -49,12 +49,12 @@ jobs:
|
||||
PODINFO_MODULE_URL: "oci://localhost:5000/podinfo"
|
||||
PODINFO_VERSION: "0.0.0-devel"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: ./.github/actions/runner-cleanup
|
||||
- name: Setup Timoni
|
||||
uses: stefanprodan/timoni/actions/setup@main
|
||||
uses: stefanprodan/timoni/actions/setup@c68e33a34f17c7ca93c7fc6717d61a14819276dc # v0.26.0
|
||||
- name: Setup Kubernetes
|
||||
uses: helm/kind-action@v1.14.0
|
||||
uses: helm/kind-action@ef37e7f390d99f746eb8b610417061a60e82a6cc # v1.14.0
|
||||
with:
|
||||
cluster_name: kind
|
||||
- name: Build container
|
||||
|
||||
37
.github/workflows/release.yml
vendored
37
.github/workflows/release.yml
vendored
@@ -15,14 +15,15 @@ jobs:
|
||||
contents: write # needed to write releases
|
||||
id-token: write # needed for keyless signing
|
||||
packages: write # needed for ghcr access
|
||||
attestations: write # needed for provenance
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: ./.github/actions/runner-cleanup
|
||||
- uses: sigstore/cosign-installer@v4.0.0
|
||||
- uses: fluxcd/flux2/action@v2.8.1
|
||||
- uses: stefanprodan/timoni/actions/setup@v0.26.0
|
||||
- uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
|
||||
- uses: fluxcd/flux2/action@871be9b40d53627786d3a3835a3ddba1e3234bd2 # v2.8.3
|
||||
- uses: stefanprodan/timoni/actions/setup@c68e33a34f17c7ca93c7fc6717d61a14819276dc # v0.26.0
|
||||
- name: Setup Notation CLI
|
||||
uses: notaryproject/notation-action/setup@v1
|
||||
uses: notaryproject/notation-action/setup@b6fee73110795d6793253c673bd723f12bcf9bbb # v1.2.2
|
||||
with:
|
||||
version: "1.1.0"
|
||||
- name: Setup Notation signing keys
|
||||
@@ -34,28 +35,28 @@ jobs:
|
||||
env:
|
||||
NOTATION_KEY: ${{ secrets.NOTATION_SIGNING_KEY }}
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||
with:
|
||||
go-version: 1.26.x
|
||||
- name: Setup Helm
|
||||
uses: azure/setup-helm@v4
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
|
||||
with:
|
||||
version: v4.1.1
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||
with:
|
||||
platforms: all
|
||||
- name: Setup Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
@@ -71,7 +72,7 @@ jobs:
|
||||
echo "REVISION=${GITHUB_SHA}" >> $GITHUB_OUTPUT
|
||||
- name: Generate images meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
|
||||
with:
|
||||
images: |
|
||||
docker.io/stefanprodan/podinfo
|
||||
@@ -80,7 +81,7 @@ jobs:
|
||||
type=raw,value=${{ steps.prep.outputs.VERSION }}
|
||||
type=raw,value=latest
|
||||
- name: Publish multi-arch image
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
sbom: true
|
||||
provenance: true
|
||||
@@ -123,7 +124,7 @@ jobs:
|
||||
cosign sign ghcr.io/stefanprodan/charts/podinfo:${{ steps.prep.outputs.VERSION }} --yes
|
||||
cosign sign ghcr.io/stefanprodan/manifests/podinfo:${{ steps.prep.outputs.VERSION }} --yes
|
||||
- name: Publish base image
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
push: true
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
@@ -132,7 +133,7 @@ jobs:
|
||||
file: ./Dockerfile.base
|
||||
tags: docker.io/stefanprodan/podinfo-base:latest
|
||||
- name: Publish helm chart
|
||||
uses: stefanprodan/helm-gh-pages@master
|
||||
uses: stefanprodan/helm-gh-pages@0ad2bb377311d61ac04ad9eb6f252fb68e207260 # v1.7.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Publish config artifact
|
||||
@@ -158,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:latest
|
||||
- name: Publish release
|
||||
uses: goreleaser/goreleaser-action@v7
|
||||
uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
|
||||
with:
|
||||
version: latest
|
||||
args: release --skip=validate
|
||||
env:
|
||||
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:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: ./.github/actions/runner-cleanup
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||
with:
|
||||
go-version: 1.26.x
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
- name: Setup kubectl
|
||||
uses: azure/setup-kubectl@v4
|
||||
uses: azure/setup-kubectl@15650b3ad78fff148532a140b8a4c821796b2d7b # v5.0.0
|
||||
with:
|
||||
version: v${{ env.KUBERNETES_VERSION }}
|
||||
- name: Setup kubeconform
|
||||
uses: ./.github/actions/kubeconform
|
||||
- name: Setup Helm
|
||||
uses: azure/setup-helm@v4
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
|
||||
with:
|
||||
version: v${{ env.HELM_VERSION }}
|
||||
- name: Setup CUE
|
||||
uses: cue-lang/setup-cue@v1.0.1
|
||||
uses: cue-lang/setup-cue@a93fa358375740cd8b0078f76355512b9208acb1 # v1.0.1
|
||||
- name: Setup Timoni
|
||||
uses: stefanprodan/timoni/actions/setup@v0.26.0
|
||||
uses: stefanprodan/timoni/actions/setup@c68e33a34f17c7ca93c7fc6717d61a14819276dc # v0.26.0
|
||||
- name: Run unit tests
|
||||
run: make test
|
||||
- name: Validate Helm chart
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
apiVersion: v1
|
||||
version: 6.11.0
|
||||
appVersion: 6.11.0
|
||||
version: 6.11.2
|
||||
appVersion: 6.11.2
|
||||
name: podinfo
|
||||
engine: gotpl
|
||||
description: Podinfo Helm chart for Kubernetes
|
||||
|
||||
@@ -8,7 +8,7 @@ backends: []
|
||||
|
||||
image:
|
||||
repository: ghcr.io/stefanprodan/podinfo
|
||||
tag: 6.11.0
|
||||
tag: 6.11.2
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
ui:
|
||||
|
||||
@@ -8,7 +8,7 @@ backends: []
|
||||
|
||||
image:
|
||||
repository: ghcr.io/stefanprodan/podinfo
|
||||
tag: 6.11.0
|
||||
tag: 6.11.2
|
||||
pullPolicy: IfNotPresent
|
||||
pullSecrets: []
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -10,6 +11,11 @@ import (
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"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/zapcore"
|
||||
|
||||
@@ -53,7 +59,7 @@ func main() {
|
||||
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.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")
|
||||
|
||||
@@ -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
|
||||
logger, _ := initZap(viper.GetString("level"))
|
||||
logger, _ := initZap(viper.GetString("level"), loggerProvider)
|
||||
defer logger.Sync()
|
||||
stdLog := zap.RedirectStdLog(logger)
|
||||
defer stdLog()
|
||||
@@ -163,10 +179,29 @@ func main() {
|
||||
// graceful shutdown
|
||||
stopCh := signals.SetupSignalHandler()
|
||||
sd, _ := signals.NewShutdown(srvCfg.ServerShutdownTimeout, logger)
|
||||
sd.SetLoggerProvider(loggerProvider)
|
||||
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)
|
||||
switch logLevel {
|
||||
case "debug":
|
||||
@@ -210,7 +245,21 @@ func initZap(logLevel string) (*zap.Logger, error) {
|
||||
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
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.0
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: backup
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.0
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /bin/sh
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: healthcheck
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.0
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /bin/sh
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: healthcheck
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.0
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /bin/sh
|
||||
|
||||
@@ -25,7 +25,7 @@ spec:
|
||||
serviceAccountName: database
|
||||
containers:
|
||||
- name: database
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.0
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: db
|
||||
|
||||
@@ -22,7 +22,7 @@ spec:
|
||||
serviceAccountName: database
|
||||
containers:
|
||||
- name: database
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.0
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: db
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: frontend
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.0
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
|
||||
@@ -25,7 +25,7 @@ spec:
|
||||
serviceAccountName: webapp
|
||||
containers:
|
||||
- name: backend
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.0
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
|
||||
@@ -25,7 +25,7 @@ spec:
|
||||
serviceAccountName: webapp
|
||||
containers:
|
||||
- name: frontend
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.0
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
|
||||
6
go.mod
6
go.mod
@@ -16,6 +16,7 @@ require (
|
||||
github.com/spf13/viper v1.21.0
|
||||
github.com/swaggo/http-swagger v1.3.4
|
||||
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/net/http/httptrace/otelhttptrace 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/ot 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/otlptracegrpc 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.uber.org/zap v1.27.1
|
||||
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
|
||||
)
|
||||
|
||||
@@ -66,6 +69,7 @@ require (
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/swaggo/files v1.0.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/proto/otlp v1.9.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=
|
||||
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/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/go.mod h1:JwJa4o3Wq+4Yz2BjlYFGWyx2h0Fw1lnoj5kpsaTI97o=
|
||||
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/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||
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/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/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/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/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/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/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
||||
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/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/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
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/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: podinfod
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.0
|
||||
image: ghcr.io/stefanprodan/podinfo:6.11.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
|
||||
@@ -1,27 +1,34 @@
|
||||
# Tracing Demo
|
||||
# Tracing & Logging Demo
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
2. PodInfo Backend on port 9899
|
||||
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?
|
||||
|
||||
The frontend pods are configured to call onto the backend pods. Both the podinfo
|
||||
pods are configured to send traces over to the collector at port 4317 using GRPC.
|
||||
The collector forwards all received spans to Jaeger over port 14250 and Jaeger
|
||||
exposes a UI over port `16686`.
|
||||
The frontend pod is configured to call the backend pod. Both podinfo pods send traces
|
||||
and logs to the collector at port 4317 using OTLP gRPC.
|
||||
|
||||
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
|
||||
|
||||
1. Start all the Containers
|
||||
1. Start all the containers
|
||||
```shell
|
||||
make run
|
||||
```
|
||||
@@ -30,8 +37,9 @@ make run
|
||||
curl -v http://localhost:9898/status/200
|
||||
curl -X POST -v http://localhost:9898/api/echo
|
||||
```
|
||||
3. Visit `http://localhost:16686/` to see the spans
|
||||
4. Stop all the containers
|
||||
3. Visit `http://localhost:16686/` to see traces in Jaeger
|
||||
4. Visit `http://localhost:3000/` to explore logs in Grafana (Explore → Loki) and traces (Explore → Jaeger)
|
||||
5. Stop all the containers
|
||||
```shell
|
||||
make stop
|
||||
```
|
||||
|
||||
@@ -5,31 +5,38 @@ services:
|
||||
build: ..
|
||||
command: ./podinfo --backend-url http://podinfo_backend:9899/status/200 --otel-service-name=podinfo_frontend
|
||||
environment:
|
||||
- OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://otel:4317
|
||||
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otel:4317
|
||||
ports:
|
||||
- "9898:9898"
|
||||
podinfo_backend:
|
||||
build: ..
|
||||
command: ./podinfo --port 9899 --otel-service-name=podinfo_backend
|
||||
environment:
|
||||
- OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://otel:4317
|
||||
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otel:4317
|
||||
ports:
|
||||
- "9899:9899"
|
||||
otel:
|
||||
command: --config otel-config.yaml
|
||||
image: otel/opentelemetry-collector:0.41.0
|
||||
image: otel/opentelemetry-collector-contrib:0.116.1
|
||||
ports:
|
||||
- "4317:4317"
|
||||
volumes:
|
||||
- ${PWD}/otel-config.yaml:/otel-config.yaml
|
||||
jaeger:
|
||||
image: jaegertracing/all-in-one:1.29.0
|
||||
loki:
|
||||
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:
|
||||
- "5775:5775/udp"
|
||||
- "6831:6831/udp"
|
||||
- "6832:6832/udp"
|
||||
- "5778:5778"
|
||||
- "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:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
http:
|
||||
|
||||
processors:
|
||||
|
||||
exporters:
|
||||
jaeger:
|
||||
endpoint: jaeger:14250
|
||||
otlp/jaeger:
|
||||
endpoint: jaeger:4317
|
||||
tls:
|
||||
insecure: true
|
||||
otlphttp/loki:
|
||||
endpoint: http://loki:3100/otlp
|
||||
|
||||
extensions:
|
||||
health_check:
|
||||
@@ -23,4 +26,8 @@ service:
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
processors: []
|
||||
exporters: [jaeger]
|
||||
exporters: [otlp/jaeger]
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
processors: []
|
||||
exporters: [otlphttp/loki]
|
||||
|
||||
@@ -7,11 +7,14 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var validHash = regexp.MustCompile(`^[a-f0-9]{40}$`)
|
||||
|
||||
// Store godoc
|
||||
// @Summary Upload file
|
||||
// @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()
|
||||
|
||||
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))
|
||||
if err != nil {
|
||||
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)
|
||||
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.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/spf13/viper"
|
||||
sdklog "go.opentelemetry.io/otel/sdk/log"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
@@ -17,9 +18,14 @@ type Shutdown struct {
|
||||
logger *zap.Logger
|
||||
pool *redis.Pool
|
||||
tracerProvider *sdktrace.TracerProvider
|
||||
loggerProvider *sdklog.LoggerProvider
|
||||
serverShutdownTimeout time.Duration
|
||||
}
|
||||
|
||||
func (s *Shutdown) SetLoggerProvider(lp *sdklog.LoggerProvider) {
|
||||
s.loggerProvider = lp
|
||||
}
|
||||
|
||||
func NewShutdown(serverShutdownTimeout time.Duration, logger *zap.Logger) (*Shutdown, error) {
|
||||
srv := &Shutdown{
|
||||
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
|
||||
if grpcServer != nil {
|
||||
s.logger.Info("Shutting down GRPC server", zap.Duration("timeout", s.serverShutdownTimeout))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package version
|
||||
|
||||
var VERSION = "6.11.0"
|
||||
var VERSION = "6.11.2"
|
||||
var REVISION = "unknown"
|
||||
|
||||
@@ -9,7 +9,7 @@ package main
|
||||
values: {
|
||||
image: {
|
||||
repository: "ghcr.io/stefanprodan/podinfo"
|
||||
tag: "6.11.0"
|
||||
tag: "6.11.2"
|
||||
digest: ""
|
||||
}
|
||||
test: image: {
|
||||
|
||||
Reference in New Issue
Block a user