Skip to content

Commit 5f5e961

Browse files
committed
add logging for debugging TLS for servicemesh training, make port configurable, seperate root html template from code
1 parent d638cd3 commit 5f5e961

13 files changed

Lines changed: 286 additions & 199 deletions

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ FROM golang:1.24.3-alpine AS builder
22
WORKDIR /src
33
COPY src/go.mod src/go.sum ./
44
RUN go mod download
5-
COPY src/*.go ./
5+
COPY src/*.go src/root.html ./
66
RUN go build -o training-application
77

88
FROM alpine:3.21.3

Dockerfile-A

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ FROM golang:1.24.3-bookworm AS builder
22
WORKDIR /src
33
COPY src/go.mod src/go.sum ./
44
RUN go mod download
5-
COPY src/*.go ./
5+
COPY src/*.go src/root.html ./
66
RUN go build -o training-application
77

88
FROM ubuntu:24.04

Dockerfile-B

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ FROM golang:1.24.3-bookworm AS builder
22
WORKDIR /src
33
COPY src/go.mod src/go.sum ./
44
RUN go mod download
5-
COPY src/*.go ./
5+
COPY src/*.go src/root.html ./
66
RUN go build -o training-application
77

88
FROM ubuntu:24.04

Dockerfile-distroless

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ FROM golang:1.24.3-alpine AS builder
22
WORKDIR /src
33
COPY src/go.mod src/go.sum ./
44
RUN go mod download
5-
COPY src/*.go ./
5+
COPY src/*.go src/root.html ./
66
RUN CGO_ENABLED=0 go build -o training-application
77

88
FROM gcr.io/distroless/static-debian12

Makefile

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ update-dependencies:
1212

1313
.PHONY: lint
1414
lint:
15-
cd src && golangci-lint run --timeout=5m -v
15+
# cd src && golangci-lint run --timeout=5m -v
16+
cd src && golangci-lint run --timeout=5m
1617

1718
.PHONY: build
18-
build:
19+
build: lint
1920
cd src && go build -o ../${APPLICATION_NAME} .
2021

2122
.PHONY: run
@@ -25,9 +26,13 @@ run: build
2526
.PHONY: docker-lint
2627
docker-lint:
2728
hadolint Dockerfile
29+
30+
.PHONY: docker-lint-all
31+
docker-lint-all:
32+
hadolint Dockerfile
2833
hadolint Dockerfile-A
29-
hadolint Dockerfile-B
30-
hadolint Dockerfile-distroless
34+
hadolint Dockerfile-B --ignore DL3025
35+
hadolint Dockerfile-distroless --ignore DL3006
3136

3237
.PHONY: docker-build
3338
docker-build: build
@@ -38,7 +43,7 @@ docker-run: docker-build
3843
docker run -it --rm -p 8080:8080 -m=10m --cpus=".5" --name ${APPLICATION_NAME} ${IMAGE_REPOSITORY}/${APPLICATION_NAME}:${BUILD_VERSION}
3944

4045
.PHONY: docker-build-all
41-
docker-build-all:
46+
docker-build-all: lint docker-lint
4247
docker build -t ${IMAGE_REPOSITORY}/${APPLICATION_NAME}:${BUILD_VERSION} .
4348
docker build -f Dockerfile-A -t ${IMAGE_REPOSITORY}/${APPLICATION_NAME}:${BUILD_VERSION_A} .
4449
docker build -f Dockerfile-B -t ${IMAGE_REPOSITORY}/${APPLICATION_NAME}:${BUILD_VERSION_B} .

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ spec:
8282
- **Default Value**: "./training-application.conf"
8383
- **Usage**: application arg, you can set this via eg `./training-application --configFilePath my.conf`
8484

85+
### `port`
86+
87+
- **Description**: Port on which the application provides its services
88+
- **Type**: int
89+
- **Default Value**: 8080
90+
- **Usage**: via config file
91+
8592
### `alive`
8693

8794
- **Description**: Flag to indicate the applications liveness

src/cli.go

Lines changed: 94 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package main
22

33
import (
44
"bufio"
5+
"crypto/x509"
6+
"encoding/base64"
57
"fmt"
68
"io"
79
"net/http"
@@ -25,37 +27,39 @@ func newCli(appConfig *appConfig) *cli {
2527
}
2628
}
2729

28-
func logHelp() {
29-
log.Info("Available Commands:")
30-
log.Info(" help: get info about available commands and endpoints")
31-
log.Info(" init: set readiness true, liveness true and delay 0")
32-
log.Info(" config: print out the current application configuration")
33-
log.Info(" set ready: application readiness probe will be successful")
34-
log.Info(" set unready: application readiness probe will fail")
35-
log.Info(" set alive: application liveness probe will be successful")
36-
log.Info(" set dead: application liveness probe will fail")
37-
log.Info(" leak mem: leak memory")
38-
log.Info(" leak cpu: leak cpu")
39-
log.Info(" request <url>: request a url, eg 'request https://www.kubermatic.com/'")
40-
log.Info(" delay / <seconds>: set delay for the root endpoint ('/') in seconds, eg 'delay / 5'")
41-
log.Info("Available Endpoints:")
42-
log.Info(" /: root endpoint, the output is depending on the application configuration")
43-
log.Info(" /liveness: liveness probe")
44-
log.Info(" /readiness: readiness probe")
30+
func createHelpText() string {
31+
var sb strings.Builder
32+
sb.WriteString("\nAvailable Commands:\n")
33+
sb.WriteString("\thelp: get info about available commands and endpoints\n")
34+
sb.WriteString("\tinit: set readiness true, liveness true and delay 0\n")
35+
sb.WriteString("\tconfig: print out the current application configuration\n")
36+
sb.WriteString("\tset ready: application readiness probe will be successful\n")
37+
sb.WriteString("\tset unready: application readiness probe will fail\n")
38+
sb.WriteString("\tset alive: application liveness probe will be successful\n")
39+
sb.WriteString("\tset dead: application liveness probe will fail\n")
40+
sb.WriteString("\tleak mem: leak memory\n")
41+
sb.WriteString("\tleak cpu: leak cpu\n")
42+
sb.WriteString("\trequest <url>: request a url, eg 'request https://www.kubermatic.com/'\n")
43+
sb.WriteString("\tdelay / <seconds>: set delay for the root endpoint ('/') in seconds, eg 'delay / 5'\n")
44+
sb.WriteString("\table Endpoints:\n")
45+
sb.WriteString("\t/: root endpoint, the output is depending on the application configuration\n")
46+
sb.WriteString("\t/liveness: liveness probe\n")
47+
sb.WriteString("\t/readiness: readiness probe\n")
48+
return sb.String()
4549
}
4650

4751
func (cli *cli) handleStdin() {
4852
reader := bufio.NewReader(os.Stdin)
4953
for {
5054
text, err := reader.ReadString('\n')
5155
if err != nil {
52-
log.Errorf("Error on reading from stdin: '%s'", err)
56+
log.Errorf("error on reading from stdin: '%s'", err)
5357
}
5458
text = strings.ReplaceAll(text, "\n", "")
5559
if text != "" {
5660
err = cli.executeCommand(text)
5761
if err != nil {
58-
log.Errorf("Error on handling command '%s': %s", text, err)
62+
log.Errorf("error on handling command '%s': %s", text, err)
5963
}
6064
}
6165
}
@@ -64,14 +68,14 @@ func (cli *cli) handleStdin() {
6468
func (cli *cli) executeCommand(command string) error {
6569

6670
if command == "help" {
67-
logHelp()
71+
log.Info(createHelpText())
6872
} else if command == "init" {
6973
log.Info("Re-initializing the application configuration")
7074
cli.config.initAppConfig(true)
7175
cli.config.ready = true
72-
cli.config.logAppConfig()
76+
log.Info(cli.config)
7377
} else if command == "config" {
74-
cli.config.logAppConfig()
78+
log.Info(cli.config)
7579
} else if command == "set ready" {
7680
cli.config.ready = true
7781
log.Info("Set the application to ready")
@@ -107,10 +111,10 @@ func (cli *cli) executeCommand(command string) error {
107111
log.Infof("Set delay for the root endpoint ('/') to '%d' seconds", cli.config.rootDelaySeconds)
108112
} else if strings.HasPrefix(command, "disable /") {
109113
cli.config.rootEnabled = false
110-
log.Info("Disabled the root endpoint ('/')")
114+
log.Info("Disabled the root endpoint ('/')")
111115
} else if strings.HasPrefix(command, "enable /") {
112116
cli.config.rootEnabled = true
113-
log.Info("Enabled the root endpoint ('/')")
117+
log.Info("Enabled the root endpoint ('/')")
114118
} else {
115119
return fmt.Errorf("unknown command '%s'", command)
116120
}
@@ -123,24 +127,35 @@ func request(url string) error {
123127
if err != nil {
124128
return err
125129
}
126-
defer resp.Body.Close()
130+
defer func() {
131+
if err := resp.Body.Close(); err != nil {
132+
log.Errorf("error on closing: %v", err)
133+
}
134+
}()
127135

128136
log.Infof("StatusCode of response %d", resp.StatusCode)
129137

130138
if resp.TLS == nil {
131139
log.Info("Response is not encrypted")
140+
clientCertHeader := resp.Header.Get("X-Client-Cert")
141+
if clientCertHeader != "" {
142+
certData, err := base64.StdEncoding.DecodeString(clientCertHeader)
143+
if err != nil {
144+
log.Errorf("error decoding proxied certificate: %s", err)
145+
}
146+
cert, err := x509.ParseCertificate(certData)
147+
if err != nil {
148+
log.Errorf("error parsing proxied certificate: %s", err)
149+
}
150+
log.Info(getCertString("Proxied certificate", cert))
151+
} else {
152+
log.Infof("No proxied certificate found")
153+
}
132154
} else {
133155
log.Info("Response is encrypted")
134156
log.Infof("TLS Version: %d", resp.TLS.Version)
135-
for _, cert := range resp.TLS.PeerCertificates {
136-
log.Infof("Certificate Subject: %s", cert.Subject.String())
137-
log.Infof("Certificate Issuer: %s", cert.Issuer.String())
138-
log.Infof("Certificate Serial Number: %s", cert.SerialNumber.String())
139-
log.Infof("Certificate Not Before: %s", cert.NotBefore.String())
140-
log.Infof("Certificate Not After: %s", cert.NotAfter.String())
141-
log.Infof("Certificate DNS Names: %v", cert.DNSNames)
142-
log.Infof("Certificate Email Addresses: %v", cert.EmailAddresses)
143-
log.Infof("Certificate IP Addresses: %v", cert.IPAddresses)
157+
for i, cert := range resp.TLS.PeerCertificates {
158+
log.Info(getCertString(fmt.Sprintf("Certificate %d", i+1), cert))
144159
}
145160
}
146161

@@ -156,6 +171,20 @@ func request(url string) error {
156171
return nil
157172
}
158173

174+
func getCertString(header string, cert *x509.Certificate) string {
175+
var sb strings.Builder
176+
sb.WriteString(fmt.Sprintf("%s: \n", header))
177+
sb.WriteString(fmt.Sprintf("\tCertificate Subject: %s\n", cert.Subject.String()))
178+
sb.WriteString(fmt.Sprintf("\tCertificate Issuer: %s\n", cert.Issuer.String()))
179+
sb.WriteString(fmt.Sprintf("\tCertificate Serial Number: %s\n", cert.SerialNumber.String()))
180+
sb.WriteString(fmt.Sprintf("\tCertificate Not Before: %s\n", cert.NotBefore.String()))
181+
sb.WriteString(fmt.Sprintf("\tCertificate Not After: %s\n", cert.NotAfter.String()))
182+
sb.WriteString(fmt.Sprintf("\tCertificate DNS Names: %v\n", cert.DNSNames))
183+
sb.WriteString(fmt.Sprintf("\tCertificate Email Addresses: %v\n", cert.EmailAddresses))
184+
sb.WriteString(fmt.Sprintf("\tCertificate IP Addresses: %v\n", cert.IPAddresses))
185+
return sb.String()
186+
}
187+
159188
func leakMem() {
160189
memLeak := make([]string, 0)
161190
count := 0
@@ -170,7 +199,7 @@ func leakMem() {
170199
}
171200
time.Sleep(time.Nanosecond)
172201
count++
173-
memLeak = append(memLeak, "THIS IS A MEM LEAK")
202+
memLeak = append(memLeak, "THIS IS A MEM LEAK") //nolint:staticcheck
174203
}
175204
}
176205

@@ -188,7 +217,7 @@ func leakCpu() {
188217
waitGroup.Add(1)
189218
go cpuIntensiveTask(&waitGroup, numGoroutines+1)
190219
}
191-
}
220+
}
192221

193222
func cpuIntensiveTask(waitGroup *sync.WaitGroup, id int) {
194223
defer waitGroup.Done()
@@ -200,34 +229,34 @@ func cpuIntensiveTask(waitGroup *sync.WaitGroup, id int) {
200229
fmt.Printf("Goroutine %d finished\n", id)
201230
}
202231

203-
// // TODO is this really the smartest way to create a CPU leak?
204-
205-
// writer, err := os.Open(os.DevNull)
206-
// if err != nil {
207-
// log.Errorf("Error on opening /dev/null: %s", err)
208-
// return err
209-
// }
210-
// defer writer.Close()
211-
// n := runtime.NumCPU()
212-
// runtime.GOMAXPROCS(n)
213-
214-
// for i := 0; i < n; i++ {
215-
// go func() {
216-
// for {
217-
// var usage syscall.Rusage
218-
// err = syscall.Getrusage(syscall.RUSAGE_SELF, &usage)
219-
// if err != nil {
220-
// log.Errorf("Error on cpu usage: %s", err)
221-
// }
222-
// log.Infof("User CPU Time: %v\n", usage.Utime)
223-
// log.Infof("System CPU Time: %v\n", usage.Stime)
224-
// fmt.Fprintf(writer, ".")
225-
// }
226-
// }()
227-
// }
228-
229-
// // TODO do I need this?
230-
// // time.Sleep(10 * time.Second)
231-
// return nil
232+
// // TODO is this really the smartest way to create a CPU leak?
233+
234+
// writer, err := os.Open(os.DevNull)
235+
// if err != nil {
236+
// log.Errorf("error on opening /dev/null: %s", err)
237+
// return err
238+
// }
239+
// defer writer.Close()
240+
// n := runtime.NumCPU()
241+
// runtime.GOMAXPROCS(n)
242+
243+
// for i := 0; i < n; i++ {
244+
// go func() {
245+
// for {
246+
// var usage syscall.Rusage
247+
// err = syscall.Getrusage(syscall.RUSAGE_SELF, &usage)
248+
// if err != nil {
249+
// log.Errorf("error on cpu usage: %s", err)
250+
// }
251+
// log.Infof("User CPU Time: %v\n", usage.Utime)
252+
// log.Infof("System CPU Time: %v\n", usage.Stime)
253+
// fmt.Fprintf(writer, ".")
254+
// }
255+
// }()
256+
// }
257+
258+
// // TODO do I need this?
259+
// // time.Sleep(10 * time.Second)
260+
// return nil
232261

233262
// }

0 commit comments

Comments
 (0)