mirror of
https://github.com/stefanprodan/podinfo.git
synced 2026-04-07 03:26:54 +00:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af6868a8de | ||
|
|
910e7139f9 | ||
|
|
fe65869b6b | ||
|
|
2a319d9d0d | ||
|
|
48402eff7e | ||
|
|
15600cc7d3 | ||
|
|
ed2a774e10 | ||
|
|
1d590c07cb | ||
|
|
948de81ed3 | ||
|
|
78658c0311 | ||
|
|
7b6f11780a | ||
|
|
d65044ff2e | ||
|
|
18c63ad7f7 | ||
|
|
a8260081d9 | ||
|
|
0ff49e5057 | ||
|
|
79cfe56484 | ||
|
|
7e36892e26 | ||
|
|
3d6d0bed69 | ||
|
|
b213e0af0a | ||
|
|
42ad3faf5a | ||
|
|
939fd5b24d | ||
|
|
36ec3ef378 | ||
|
|
287e005129 | ||
|
|
0b3e88d6de | ||
|
|
10139749da | ||
|
|
f891e0683b | ||
|
|
647b4cba04 | ||
|
|
c5df50c774 | ||
|
|
2b1d325343 | ||
|
|
319d57cb68 | ||
|
|
087da02dbb | ||
|
|
7d00f68180 | ||
|
|
87c9bb8ba2 | ||
|
|
5fb970b526 | ||
|
|
56b404bd84 | ||
|
|
a12d0a1ed7 | ||
|
|
51979787b0 | ||
|
|
8b37756118 | ||
|
|
1eb1da110b | ||
|
|
d1ed907f1e | ||
|
|
8e6eccecda | ||
|
|
f3db1adb27 | ||
|
|
7f3e11c1ce | ||
|
|
a7eb7e4995 | ||
|
|
43194bb342 | ||
|
|
c7d21968e7 | ||
|
|
214a19fb0f | ||
|
|
82ea2fa993 | ||
|
|
d84913c31e | ||
|
|
6bac5ffaa2 | ||
|
|
eacf909c4a | ||
|
|
f7c1669125 | ||
|
|
158d6e82da | ||
|
|
4d890382e5 | ||
|
|
83842e01f7 | ||
|
|
37b453fbbc | ||
|
|
53c6b472de | ||
|
|
c759f958c0 | ||
|
|
5d14183809 | ||
|
|
ab74d6ef0b |
@@ -11,7 +11,7 @@ jobs:
|
||||
name: Start Kubernetes Kind cluster
|
||||
command: e2e/bootstrap.sh
|
||||
- run:
|
||||
name: Install podinfo with Helm
|
||||
name: Install podinfo with Helm v3
|
||||
command: e2e/install.sh
|
||||
- run:
|
||||
name: Run Helm tests
|
||||
@@ -19,12 +19,12 @@ jobs:
|
||||
|
||||
push-container:
|
||||
docker:
|
||||
- image: circleci/golang:1.12
|
||||
- image: circleci/golang:1.13
|
||||
working_directory: ~/build
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: true
|
||||
docker_layer_caching: false
|
||||
- run: make build-container
|
||||
- run: |
|
||||
if [ -z "$CIRCLE_TAG" ]; then
|
||||
@@ -37,14 +37,14 @@ jobs:
|
||||
|
||||
push-binary:
|
||||
docker:
|
||||
- image: circleci/golang:1.12
|
||||
- image: circleci/golang:1.13
|
||||
steps:
|
||||
- checkout
|
||||
- run: curl -sL https://git.io/goreleaser | bash
|
||||
|
||||
push-helm-charts:
|
||||
docker:
|
||||
- image: circleci/golang:1.12
|
||||
- image: circleci/golang:1.13
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
|
||||
51
.github/policy/kubernetes.rego
vendored
Normal file
51
.github/policy/kubernetes.rego
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package kubernetes
|
||||
|
||||
name = input.metadata.name
|
||||
|
||||
kind = input.kind
|
||||
|
||||
is_service {
|
||||
input.kind = "Service"
|
||||
}
|
||||
|
||||
is_deployment {
|
||||
input.kind = "Deployment"
|
||||
}
|
||||
|
||||
is_pod {
|
||||
input.kind = "Pod"
|
||||
}
|
||||
|
||||
split_image(image) = [image, "latest"] {
|
||||
not contains(image, ":")
|
||||
}
|
||||
|
||||
split_image(image) = [image_name, tag] {
|
||||
[image_name, tag] = split(image, ":")
|
||||
}
|
||||
|
||||
pod_containers(pod) = all_containers {
|
||||
keys = {"containers", "initContainers"}
|
||||
all_containers = [c | keys[k]; c = pod.spec[k][_]]
|
||||
}
|
||||
|
||||
containers[container] {
|
||||
pods[pod]
|
||||
all_containers = pod_containers(pod)
|
||||
container = all_containers[_]
|
||||
}
|
||||
|
||||
containers[container] {
|
||||
all_containers = pod_containers(input)
|
||||
container = all_containers[_]
|
||||
}
|
||||
|
||||
pods[pod] {
|
||||
is_deployment
|
||||
pod = input.spec.template
|
||||
}
|
||||
|
||||
pods[pod] {
|
||||
is_pod
|
||||
pod = input
|
||||
}
|
||||
43
.github/policy/rules.rego
vendored
Normal file
43
.github/policy/rules.rego
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import data.kubernetes
|
||||
|
||||
name = input.metadata.name
|
||||
|
||||
# Deny containers with latest image tag
|
||||
deny[msg] {
|
||||
kubernetes.containers[container]
|
||||
[image_name, "latest"] = kubernetes.split_image(container.image)
|
||||
msg = sprintf("%s in the %s %s has an image %s, using the latest tag", [container.name, kubernetes.kind, kubernetes.name, image_name])
|
||||
}
|
||||
|
||||
# Deny services without app label selector
|
||||
service_labels {
|
||||
input.spec.selector["app"]
|
||||
}
|
||||
deny[msg] {
|
||||
kubernetes.is_service
|
||||
not service_labels
|
||||
msg = sprintf("Service %s should set app label selector", [name])
|
||||
}
|
||||
|
||||
# Deny deployments without app label selector
|
||||
match_labels {
|
||||
input.spec.selector.matchLabels["app"]
|
||||
}
|
||||
deny[msg] {
|
||||
kubernetes.is_deployment
|
||||
not match_labels
|
||||
msg = sprintf("Service %s should set app label selector", [name])
|
||||
}
|
||||
|
||||
# Warn if deployments have no prometheus pod annotations
|
||||
annotations {
|
||||
input.spec.template.metadata.annotations["prometheus.io/scrape"]
|
||||
input.spec.template.metadata.annotations["prometheus.io/port"]
|
||||
}
|
||||
warn[msg] {
|
||||
kubernetes.is_deployment
|
||||
not annotations
|
||||
msg = sprintf("Deployment %s should set prometheus.io/scrape and prometheus.io/port pod annotations", [name])
|
||||
}
|
||||
17
.github/workflows/test.yml
vendored
Normal file
17
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
on: [push, pull_request]
|
||||
name: test
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: kubeval
|
||||
uses: stefanprodan/kube-tools@v1
|
||||
with:
|
||||
command: |
|
||||
kustomize build ./kustomize | kubeval --strict
|
||||
- name: conftest
|
||||
uses: stefanprodan/kube-tools@v1
|
||||
with:
|
||||
command: |
|
||||
kustomize build ./kustomize | conftest test -p .github/policy -
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.12 as builder
|
||||
FROM golang:1.13 as builder
|
||||
|
||||
RUN mkdir -p /podinfo/
|
||||
|
||||
@@ -6,7 +6,7 @@ WORKDIR /podinfo
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN GOPROXY=https://proxy.golang.org go mod download
|
||||
RUN go mod download
|
||||
|
||||
RUN go test -v -race ./...
|
||||
|
||||
|
||||
5
Makefile
5
Makefile
@@ -8,9 +8,12 @@ DOCKER_REPOSITORY:=stefanprodan
|
||||
DOCKER_IMAGE_NAME:=$(DOCKER_REPOSITORY)/$(NAME)
|
||||
GIT_COMMIT:=$(shell git describe --dirty --always)
|
||||
VERSION:=$(shell grep 'VERSION' pkg/version/version.go | awk '{ print $$4 }' | tr -d '"')
|
||||
EXTRA_RUN_ARGS?=
|
||||
|
||||
run:
|
||||
GO111MODULE=on go run -ldflags "-s -w -X github.com/stefanprodan/podinfo/pkg/version.REVISION=$(GIT_COMMIT)" cmd/podinfo/* --level=debug
|
||||
GO111MODULE=on go run -ldflags "-s -w -X github.com/stefanprodan/podinfo/pkg/version.REVISION=$(GIT_COMMIT)" cmd/podinfo/* \
|
||||
--level=debug --grpc-port=9999 --backend-url=https://httpbin.org/status/401 --backend-url=https://httpbin.org/status/500 \
|
||||
--ui-logo=https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif --ui-color=#34577c $(EXTRA_RUN_ARGS)
|
||||
|
||||
test:
|
||||
GO111MODULE=on go test -v -race ./...
|
||||
|
||||
19
README.md
19
README.md
@@ -1,10 +1,11 @@
|
||||
# podinfo
|
||||
|
||||
[](https://circleci.com/gh/stefanprodan/podinfo)
|
||||
[](https://github.com/stefanprodan/podinfo/blob/master/.github/workflows/test.yml)
|
||||
[](https://goreportcard.com/report/github.com/stefanprodan/podinfo)
|
||||
[](https://hub.docker.com/r/stefanprodan/podinfo)
|
||||
|
||||
Podinfo is a tiny web application made with Go
|
||||
that showcases best practices of running microservices in Kubernetes.
|
||||
Podinfo is a tiny web application made with Go that showcases best practices of running microservices in Kubernetes.
|
||||
|
||||
Specifications:
|
||||
|
||||
@@ -13,12 +14,14 @@ Specifications:
|
||||
* File watcher for secrets and configmaps
|
||||
* Instrumented with Prometheus
|
||||
* Tracing with Istio and Jaeger
|
||||
* Linkerd service profile
|
||||
* Structured logging with zap
|
||||
* 12-factor app with viper
|
||||
* Fault injection (random errors and latency)
|
||||
* Swagger docs
|
||||
* Helm and Kustomize installers
|
||||
* End-to-End testing with Kubernetes Kind and Helm
|
||||
* Kustomize testing with GitHub Actions and Open Policy Agent
|
||||
|
||||
Web API:
|
||||
|
||||
@@ -44,9 +47,13 @@ Web API:
|
||||
* `GET /chunked/{seconds}` uses `transfer-encoding` type `chunked` to give a partial response and then waits for the specified period
|
||||
* `GET /swagger.json` returns the API Swagger docs, used for Linkerd service profiling and Gloo routes discovery
|
||||
|
||||
gRPC API:
|
||||
|
||||
* `/grpc.health.v1.Health/Check` health checking
|
||||
|
||||
Web UI:
|
||||
|
||||

|
||||

|
||||
|
||||
To access the Swagger UI open `<podinfo-host>/swagger/index.html` in a browser.
|
||||
|
||||
@@ -62,20 +69,20 @@ To access the Swagger UI open `<podinfo-host>/swagger/index.html` in a browser.
|
||||
Helm:
|
||||
|
||||
```bash
|
||||
helm repo add sp https://stefanprodan.github.io/podinfo
|
||||
helm repo add podinfo https://stefanprodan.github.io/podinfo
|
||||
|
||||
helm upgrade --install --wait frontend \
|
||||
--namespace test \
|
||||
--set replicaCount=2 \
|
||||
--set backend=http://backend-podinfo:9898/echo \
|
||||
sp/podinfo
|
||||
podinfo/podinfo
|
||||
|
||||
helm test frontend --cleanup
|
||||
|
||||
helm upgrade --install --wait backend \
|
||||
--namespace test \
|
||||
--set hpa.enabled=true \
|
||||
sp/podinfo
|
||||
podinfo/podinfo
|
||||
```
|
||||
|
||||
Kustomize:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
apiVersion: v1
|
||||
version: 2.1.1
|
||||
appVersion: 2.1.1
|
||||
version: 3.2.0
|
||||
appVersion: 3.2.0
|
||||
name: podinfo
|
||||
engine: gotpl
|
||||
description: Podinfo Helm chart for Kubernetes
|
||||
|
||||
@@ -8,7 +8,7 @@ that showcases best practices of running microservices in Kubernetes.
|
||||
To install the chart with the release name `my-release`:
|
||||
|
||||
```console
|
||||
$ helm repo add sp https://stefanprodan.github.io/k8s-podinfo
|
||||
$ helm repo add sp https://stefanprodan.github.io/podinfo
|
||||
$ helm upgrade my-release --install sp/podinfo
|
||||
```
|
||||
|
||||
@@ -32,20 +32,20 @@ The following tables lists the configurable parameters of the podinfo chart and
|
||||
Parameter | Description | Default
|
||||
--- | --- | ---
|
||||
`affinity` | node/pod affinities | None
|
||||
`color` | UI color | blue
|
||||
`backend` | echo backend URL | None
|
||||
`backends` | echo backend URL array | None
|
||||
`faults.delay` | random HTTP response delays between 0 and 5 seconds | `false`
|
||||
`faults.error` | 1/3 chances of a random HTTP response error | `false`
|
||||
`faults.unhealthy` | when set, the healthy state is never reached | `false`
|
||||
`faults.unready` | when set, the ready state is never reached | `false`
|
||||
`hpa.enabled` | enables HPA | `false`
|
||||
`hpa.cpu` | target CPU usage per pod | None
|
||||
`hpa.memory` | target memory usage per pod | None
|
||||
`hpa.requests` | target requests per second per pod | None
|
||||
`hpa.maxReplicas` | maximum pod replicas | `10`
|
||||
`ingress.hosts` | ingress accepted hostnames | None
|
||||
`ingress.tls` | ingress TLS configuration | None:
|
||||
`image.pullPolicy` | image pull policy | `IfNotPresent`
|
||||
`image.repository` | image repository | `stefanprodan/podinfo`
|
||||
`image.tag` | image tag | `0.0.1`
|
||||
`image.tag` | image tag | `<VERSION>`
|
||||
`ingress.enabled` | enables ingress | `false`
|
||||
`ingress.annotations` | ingress annotations | None
|
||||
`ingress.hosts` | ingress accepted hostnames | None
|
||||
@@ -57,11 +57,18 @@ Parameter | Description | Default
|
||||
`resources.requests/memory` | pod memory request | `16Mi`
|
||||
`resources.limits/cpu` | pod CPU limit | None
|
||||
`resources.limits/memory` | pod memory limit | None
|
||||
`service.externalPort` | external port for the service | `9898`
|
||||
`service.internalPort` | internal port for the service | `9898`
|
||||
`service.nodePort` | node port for the service | `31198`
|
||||
`service.enabled` | create Kubernetes service (should be disabled when using Flagger) | `true`
|
||||
`service.metricsPort` | Prometheus metrics endpoint port | `9797`
|
||||
`service.externalPort` | ClusterIP HTTP port | `9898`
|
||||
`service.httpPort` | container HTTP port | `9898`
|
||||
`service.nodePort` | NodePort for the HTTP endpoint | `31198`
|
||||
`service.grpcPort` | ClusterIP gPRC port | `9999`
|
||||
`service.grpcService` | gPRC service name | `podinfo`
|
||||
`service.type` | type of service | `ClusterIP`
|
||||
`tolerations` | list of node taints to tolerate | `[]`
|
||||
`serviceAccount.enabled` | specifies whether a service account should be created | `false`
|
||||
`serviceAccount.name` | the name of the service account to use, if not set and create is true, a name is generated using the fullname template | None
|
||||
`linkerd.profile.enabled` | create Linkerd service profile | `false`
|
||||
|
||||
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
|
||||
|
||||
|
||||
@@ -30,3 +30,14 @@ Create chart name and version as used by the chart label.
|
||||
{{- define "podinfo.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "podinfo.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.enabled -}}
|
||||
{{ default (include "podinfo.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else -}}
|
||||
{{ default "default" .Values.serviceAccount.name }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
@@ -8,7 +8,9 @@ metadata:
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
spec:
|
||||
{{- if not .Values.hpa.enabled }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
{{- end }}
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
@@ -23,25 +25,56 @@ spec:
|
||||
app: {{ template "podinfo.name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
annotations:
|
||||
prometheus.io/scrape: 'true'
|
||||
prometheus.io/scrape: "true"
|
||||
prometheus.io/port: "{{ .Values.service.httpPort }}"
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 30
|
||||
{{- if .Values.serviceAccount.enabled }}
|
||||
serviceAccountName: {{ template "podinfo.serviceAccountName" . }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
command:
|
||||
- ./podinfo
|
||||
- --port={{ .Values.service.containerPort }}
|
||||
- --port={{ .Values.service.httpPort | default 9898 }}
|
||||
{{- if .Values.service.metricsPort }}
|
||||
- --port-metrics={{ .Values.service.metricsPort }}
|
||||
{{- end }}
|
||||
{{- if .Values.service.grpcPort }}
|
||||
- --grpc-port={{ .Values.service.grpcPort }}
|
||||
{{- end }}
|
||||
{{- if .Values.service.grpcService }}
|
||||
- --grpc-service-name={{ .Values.service.grpcService }}
|
||||
{{- end }}
|
||||
{{- range .Values.backends }}
|
||||
- --backend-url={{ . }}
|
||||
{{- end }}
|
||||
- --level={{ .Values.logLevel }}
|
||||
- --random-delay={{ .Values.faults.delay }}
|
||||
- --random-error={{ .Values.faults.error }}
|
||||
{{- if .Values.faults.unhealthy }}
|
||||
- --unhealthy
|
||||
{{- end }}
|
||||
{{- if .Values.faults.unready }}
|
||||
- --unready
|
||||
{{- end }}
|
||||
{{- if .Values.h2c.enabled }}
|
||||
- --h2c
|
||||
{{- end }}
|
||||
env:
|
||||
- name: PODINFO_UI_COLOR
|
||||
value: {{ .Values.color }}
|
||||
{{- if .Values.message }}
|
||||
{{- if .Values.ui.message }}
|
||||
- name: PODINFO_UI_MESSAGE
|
||||
value: {{ .Values.message }}
|
||||
value: {{ .Values.ui.message }}
|
||||
{{- end }}
|
||||
{{- if .Values.ui.logo }}
|
||||
- name: PODINFO_UI_LOGO
|
||||
value: {{ .Values.ui.logo }}
|
||||
{{- end }}
|
||||
{{- if .Values.ui.color }}
|
||||
- name: PODINFO_UI_COLOR
|
||||
value: {{ .Values.ui.color }}
|
||||
{{- end }}
|
||||
{{- if .Values.backend }}
|
||||
- name: PODINFO_BACKEND_URL
|
||||
@@ -49,15 +82,25 @@ spec:
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.service.containerPort }}
|
||||
containerPort: {{ .Values.service.httpPort | default 9898 }}
|
||||
protocol: TCP
|
||||
{{- if .Values.service.metricsPort }}
|
||||
- name: http-metrics
|
||||
containerPort: {{ .Values.service.metricsPort }}
|
||||
protocol: TCP
|
||||
{{- end }}
|
||||
{{- if .Values.service.grpcPort }}
|
||||
- name: grpc
|
||||
containerPort: {{ .Values.service.grpcPort }}
|
||||
protocol: TCP
|
||||
{{- end }}
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- podcli
|
||||
- check
|
||||
- http
|
||||
- localhost:{{ .Values.service.containerPort }}/healthz
|
||||
- localhost:{{ .Values.service.httpPort | default 9898 }}/healthz
|
||||
initialDelaySeconds: 1
|
||||
timeoutSeconds: 5
|
||||
readinessProbe:
|
||||
@@ -66,7 +109,7 @@ spec:
|
||||
- podcli
|
||||
- check
|
||||
- http
|
||||
- localhost:{{ .Values.service.containerPort }}/readyz
|
||||
- localhost:{{ .Values.service.httpPort | default 9898 }}/readyz
|
||||
initialDelaySeconds: 1
|
||||
timeoutSeconds: 5
|
||||
volumeMounts:
|
||||
|
||||
@@ -5,7 +5,7 @@ metadata:
|
||||
name: {{ template "podinfo.fullname" . }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1beta2
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ template "podinfo.fullname" . }}
|
||||
minReplicas: {{ .Values.replicaCount }}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{{- if .Values.ingress.enabled -}}
|
||||
{{- $fullName := include "podinfo.fullname" . -}}
|
||||
{{- $servicePort := .Values.service.port -}}
|
||||
{{- $ingressPath := .Values.ingress.path -}}
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
@@ -36,4 +35,12 @@ spec:
|
||||
serviceName: {{ $fullName }}
|
||||
servicePort: http
|
||||
{{- end }}
|
||||
{{- if not .Values.ingress.hosts }}
|
||||
- http:
|
||||
paths:
|
||||
- path: {{ $ingressPath }}
|
||||
backend:
|
||||
serviceName: {{ $fullName }}
|
||||
servicePort: http
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
88
charts/podinfo/templates/linkerd.yaml
Normal file
88
charts/podinfo/templates/linkerd.yaml
Normal file
@@ -0,0 +1,88 @@
|
||||
{{- if .Values.linkerd.profile.enabled -}}
|
||||
apiVersion: linkerd.io/v1alpha2
|
||||
kind: ServiceProfile
|
||||
metadata:
|
||||
name: {{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
|
||||
spec:
|
||||
routes:
|
||||
- condition:
|
||||
method: GET
|
||||
pathRegex: /
|
||||
name: GET /
|
||||
- condition:
|
||||
method: POST
|
||||
pathRegex: /api/echo
|
||||
name: POST /api/echo
|
||||
- condition:
|
||||
method: GET
|
||||
pathRegex: /api/info
|
||||
name: GET /api/info
|
||||
- condition:
|
||||
method: GET
|
||||
pathRegex: /chunked/[^/]*
|
||||
name: GET /chunked/{seconds}
|
||||
- condition:
|
||||
method: GET
|
||||
pathRegex: /delay/[^/]*
|
||||
name: GET /delay/{seconds}
|
||||
- condition:
|
||||
method: GET
|
||||
pathRegex: /env
|
||||
name: GET /env
|
||||
- condition:
|
||||
method: GET
|
||||
pathRegex: /headers
|
||||
name: GET /headers
|
||||
- condition:
|
||||
method: GET
|
||||
pathRegex: /healthz
|
||||
name: GET /healthz
|
||||
- condition:
|
||||
method: GET
|
||||
pathRegex: /metrics
|
||||
name: GET /metrics
|
||||
- condition:
|
||||
method: GET
|
||||
pathRegex: /panic
|
||||
name: GET /panic
|
||||
- condition:
|
||||
method: GET
|
||||
pathRegex: /readyz
|
||||
name: GET /readyz
|
||||
- condition:
|
||||
method: POST
|
||||
pathRegex: /readyz/disable
|
||||
name: POST /readyz/disable
|
||||
- condition:
|
||||
method: POST
|
||||
pathRegex: /readyz/enable
|
||||
name: POST /readyz/enable
|
||||
- condition:
|
||||
method: GET
|
||||
pathRegex: /status/[^/]*
|
||||
name: GET /status/{code}
|
||||
- condition:
|
||||
method: POST
|
||||
pathRegex: /store
|
||||
name: POST /store
|
||||
- condition:
|
||||
method: GET
|
||||
pathRegex: /store/[^/]*
|
||||
name: GET /store/{hash}
|
||||
- condition:
|
||||
method: POST
|
||||
pathRegex: /token
|
||||
name: POST /token
|
||||
- condition:
|
||||
method: POST
|
||||
pathRegex: /token/validate
|
||||
name: POST /token/validate
|
||||
- condition:
|
||||
method: GET
|
||||
pathRegex: /version
|
||||
name: GET /version
|
||||
- condition:
|
||||
method: POST
|
||||
pathRegex: /ws/echo
|
||||
name: POST /ws/echo
|
||||
{{- end }}
|
||||
@@ -1,3 +1,4 @@
|
||||
{{- if .Values.service.enabled -}}
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
@@ -17,6 +18,13 @@ spec:
|
||||
{{- if (and (eq .Values.service.type "NodePort") (not (empty .Values.service.nodePort))) }}
|
||||
nodePort: {{ .Values.service.nodePort }}
|
||||
{{- end }}
|
||||
{{- if .Values.service.grpcPort }}
|
||||
- port: {{ .Values.service.grpcPort }}
|
||||
targetPort: grpc
|
||||
protocol: TCP
|
||||
name: grpc
|
||||
{{- end }}
|
||||
selector:
|
||||
app: {{ template "podinfo.name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
11
charts/podinfo/templates/serviceaccount.yaml
Normal file
11
charts/podinfo/templates/serviceaccount.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
{{- if .Values.serviceAccount.enabled -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ template "podinfo.serviceAccountName" . }}
|
||||
labels:
|
||||
app: {{ template "podinfo.name" . }}
|
||||
chart: {{ template "podinfo.chart" . }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
{{- end -}}
|
||||
21
charts/podinfo/templates/tests/grpc.yaml
Normal file
21
charts/podinfo/templates/tests/grpc.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: {{ template "podinfo.fullname" . }}-grpc-test-{{ randAlphaNum 5 | lower }}
|
||||
labels:
|
||||
heritage: {{ .Release.Service }}
|
||||
release: {{ .Release.Name }}
|
||||
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
app: {{ template "podinfo.name" . }}
|
||||
annotations:
|
||||
"helm.sh/hook": test-success
|
||||
sidecar.istio.io/inject: "false"
|
||||
linkerd.io/inject: disabled
|
||||
appmesh.k8s.aws/sidecarInjectorWebhook: disabled
|
||||
spec:
|
||||
containers:
|
||||
- name: grpc-health-probe
|
||||
image: stefanprodan/grpc_health_probe:v0.3.0
|
||||
command: ['grpc_health_probe']
|
||||
args: ['-addr={{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.grpcPort }}']
|
||||
restartPolicy: Never
|
||||
@@ -9,35 +9,21 @@ metadata:
|
||||
app: {{ template "podinfo.name" . }}
|
||||
annotations:
|
||||
"helm.sh/hook": test-success
|
||||
sidecar.istio.io/inject: "false"
|
||||
linkerd.io/inject: disabled
|
||||
appmesh.k8s.aws/sidecarInjectorWebhook: disabled
|
||||
spec:
|
||||
containers:
|
||||
- name: tools
|
||||
image: giantswarm/tiny-tools
|
||||
command: ["/bin/sh", "/scripts/test.sh"]
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
TOKEN=$(curl -sd 'test' ${PODINFO_SVC}/token | jq -r .token) &&
|
||||
curl -sH "Authorization: Bearer ${TOKEN}" ${PODINFO_SVC}/token/validate | grep test
|
||||
env:
|
||||
- name: PODINFO_SVC
|
||||
value: {{ template "podinfo.fullname" . }}:{{ .Values.service.externalPort }}
|
||||
volumeMounts:
|
||||
- name: scripts
|
||||
mountPath: /scripts
|
||||
value: {{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.externalPort }}
|
||||
restartPolicy: Never
|
||||
volumes:
|
||||
- name: scripts
|
||||
configMap:
|
||||
name: {{ template "podinfo.fullname" . }}-test-cfg
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ template "podinfo.fullname" . }}-test-cfg
|
||||
labels:
|
||||
heritage: {{ .Release.Service }}
|
||||
release: {{ .Release.Name }}
|
||||
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
app: {{ template "podinfo.name" . }}
|
||||
data:
|
||||
test.sh: |
|
||||
#!/bin/sh
|
||||
echo "testing ${PODINFO_SVC}/token"
|
||||
TOKEN=$(curl -sd 'test' ${PODINFO_SVC}/token | jq -r .token) && \
|
||||
curl -H "Authorization: Bearer ${TOKEN}" ${PODINFO_SVC}/token/validate | grep test
|
||||
|
||||
|
||||
@@ -9,10 +9,19 @@ metadata:
|
||||
app: {{ template "podinfo.name" . }}
|
||||
annotations:
|
||||
"helm.sh/hook": test-success
|
||||
sidecar.istio.io/inject: "false"
|
||||
linkerd.io/inject: disabled
|
||||
appmesh.k8s.aws/sidecarInjectorWebhook: disabled
|
||||
spec:
|
||||
containers:
|
||||
- name: curl
|
||||
image: radial/busyboxplus:curl
|
||||
command: ['curl']
|
||||
args: ['{{ template "podinfo.fullname" . }}:{{ .Values.service.externalPort }}']
|
||||
image: giantswarm/tiny-tools
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
curl -s ${PODINFO_SVC}/api/info | grep version
|
||||
env:
|
||||
- name: PODINFO_SVC
|
||||
value: {{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.externalPort }}
|
||||
restartPolicy: Never
|
||||
|
||||
@@ -2,23 +2,36 @@
|
||||
|
||||
replicaCount: 1
|
||||
logLevel: info
|
||||
color: blue
|
||||
backend: #http://backend-podinfo:9898/echo
|
||||
message: #UI greetings
|
||||
backends: []
|
||||
|
||||
ui:
|
||||
color: "#34577c"
|
||||
message: ""
|
||||
logo: ""
|
||||
|
||||
faults:
|
||||
delay: false
|
||||
error: false
|
||||
unhealthy: false
|
||||
unready: false
|
||||
|
||||
h2c:
|
||||
enabled: false
|
||||
|
||||
image:
|
||||
repository: stefanprodan/podinfo
|
||||
tag: 2.1.1
|
||||
tag: 3.2.0
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
service:
|
||||
enabled: true
|
||||
type: ClusterIP
|
||||
metricsPort: 9797
|
||||
httpPort: 9898
|
||||
externalPort: 9898
|
||||
containerPort: 9898
|
||||
grpcPort: 9999
|
||||
grpcService: podinfo
|
||||
nodePort: 31198
|
||||
|
||||
# metrics-server add-on required
|
||||
@@ -32,14 +45,25 @@ hpa:
|
||||
# average http requests per second per pod (k8s-prometheus-adapter)
|
||||
requests:
|
||||
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
enabled: false
|
||||
# The name of the service account to use.
|
||||
# If not set and create is true, a name is generated using the fullname template
|
||||
name:
|
||||
|
||||
linkerd:
|
||||
profile:
|
||||
enabled: false
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
annotations: {}
|
||||
# kubernetes.io/ingress.class: nginx
|
||||
# kubernetes.io/tls-acme: "true"
|
||||
path: /
|
||||
hosts:
|
||||
- podinfo.local
|
||||
path: /*
|
||||
hosts: []
|
||||
# - podinfo.local
|
||||
tls: []
|
||||
# - secretName: chart-example-tls
|
||||
# hosts:
|
||||
|
||||
@@ -14,14 +14,19 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/health/grpc_health_v1"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
var (
|
||||
retryCount int
|
||||
retryDelay time.Duration
|
||||
method string
|
||||
body string
|
||||
timeout time.Duration
|
||||
retryCount int
|
||||
retryDelay time.Duration
|
||||
method string
|
||||
body string
|
||||
timeout time.Duration
|
||||
grpcServiceName string
|
||||
)
|
||||
|
||||
var checkCmd = &cobra.Command{
|
||||
@@ -51,6 +56,13 @@ var checkCertCmd = &cobra.Command{
|
||||
RunE: runCheckCert,
|
||||
}
|
||||
|
||||
var checkgRPCCmd = &cobra.Command{
|
||||
Use: `grpc [address]`,
|
||||
Short: "gRPC health check",
|
||||
Example: ` check grpc localhost:8080 --service=podinfo --retry=1 --delay=2s --timeout=2s`,
|
||||
RunE: runCheckgPRC,
|
||||
}
|
||||
|
||||
func init() {
|
||||
checkUrlCmd.Flags().StringVar(&method, "method", "GET", "HTTP method")
|
||||
checkUrlCmd.Flags().StringVar(&body, "body", "", "HTTP POST/PUT content")
|
||||
@@ -64,6 +76,12 @@ func init() {
|
||||
checkTcpCmd.Flags().DurationVar(&timeout, "timeout", 5*time.Second, "timeout")
|
||||
checkCmd.AddCommand(checkTcpCmd)
|
||||
|
||||
checkgRPCCmd.Flags().IntVar(&retryCount, "retry", 0, "times to retry the TCP check")
|
||||
checkgRPCCmd.Flags().DurationVar(&retryDelay, "delay", 1*time.Second, "wait duration between retries")
|
||||
checkgRPCCmd.Flags().DurationVar(&timeout, "timeout", 5*time.Second, "timeout")
|
||||
checkgRPCCmd.Flags().StringVar(&grpcServiceName, "service", "", "gRPC service name")
|
||||
checkCmd.AddCommand(checkgRPCCmd)
|
||||
|
||||
checkCmd.AddCommand(checkCertCmd)
|
||||
|
||||
rootCmd.AddCommand(checkCmd)
|
||||
@@ -243,3 +261,53 @@ func fmtContentLength(b int64) string {
|
||||
}
|
||||
return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp])
|
||||
}
|
||||
|
||||
func runCheckgPRC(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 grpc localhost:8080")
|
||||
}
|
||||
address := args[0]
|
||||
|
||||
for n := 0; n <= retryCount; n++ {
|
||||
if n != 1 {
|
||||
time.Sleep(retryDelay)
|
||||
}
|
||||
|
||||
conn, err := grpc.Dial(address, grpc.WithInsecure())
|
||||
if err != nil {
|
||||
logger.Info("check failed",
|
||||
zap.String("address", address),
|
||||
zap.Error(err))
|
||||
continue
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
resp, err := grpc_health_v1.NewHealthClient(conn).Check(ctx, &grpc_health_v1.HealthCheckRequest{
|
||||
Service: grpcServiceName,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err != nil {
|
||||
if stat, ok := status.FromError(err); ok && stat.Code() == codes.Unimplemented {
|
||||
logger.Info("gPRC health protocol not implemented")
|
||||
os.Exit(1)
|
||||
} else {
|
||||
logger.Info("check failed",
|
||||
zap.String("address", address),
|
||||
zap.Error(err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
logger.Info("check succeed",
|
||||
zap.String("status", resp.GetStatus().String()))
|
||||
os.Exit(0)
|
||||
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,28 +2,33 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stefanprodan/podinfo/pkg/api"
|
||||
"github.com/stefanprodan/podinfo/pkg/signals"
|
||||
"github.com/stefanprodan/podinfo/pkg/version"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"github.com/stefanprodan/podinfo/pkg/api"
|
||||
"github.com/stefanprodan/podinfo/pkg/grpc"
|
||||
"github.com/stefanprodan/podinfo/pkg/signals"
|
||||
"github.com/stefanprodan/podinfo/pkg/version"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// flags definition
|
||||
fs := pflag.NewFlagSet("default", pflag.ContinueOnError)
|
||||
fs.Int("port", 9898, "port")
|
||||
fs.Int("port", 9898, "HTTP port")
|
||||
fs.Int("port-metrics", 0, "metrics port")
|
||||
fs.Int("grpc-port", 0, "gRPC port")
|
||||
fs.String("grpc-service-name", "podinfo", "gPRC service name")
|
||||
fs.String("level", "info", "log level debug, info, warn, error, flat or panic")
|
||||
fs.String("backend-url", "", "backend service URL")
|
||||
fs.StringSlice("backend-url", []string{}, "backend service URL")
|
||||
fs.Duration("http-client-timeout", 2*time.Minute, "client timeout duration")
|
||||
fs.Duration("http-server-timeout", 30*time.Second, "server read and write timeout duration")
|
||||
fs.Duration("http-server-shutdown-timeout", 5*time.Second, "server graceful shutdown timeout duration")
|
||||
@@ -31,11 +36,15 @@ func main() {
|
||||
fs.String("config-path", "", "config dir path")
|
||||
fs.String("config", "config.yaml", "config file name")
|
||||
fs.String("ui-path", "./ui", "UI local path")
|
||||
fs.String("ui-color", "blue", "UI color")
|
||||
fs.String("ui-logo", "", "UI logo")
|
||||
fs.String("ui-color", "cyan", "UI color")
|
||||
fs.String("ui-message", fmt.Sprintf("greetings from podinfo v%v", version.VERSION), "UI message")
|
||||
fs.Bool("h2c", false, "allow upgrading to H2C")
|
||||
fs.Bool("random-delay", false, "between 0 and 5 seconds random delay")
|
||||
fs.Bool("random-error", false, "1/3 chances of a random response error")
|
||||
fs.Int("stress-cpu", 0, "Number of CPU cores with 100 load")
|
||||
fs.Bool("unhealthy", false, "when set, healthy state is never reached")
|
||||
fs.Bool("unready", false, "when set, ready state is never reached")
|
||||
fs.Int("stress-cpu", 0, "number of CPU cores with 100 load")
|
||||
fs.Int("stress-memory", 0, "MB of data to load into memory")
|
||||
|
||||
versionFlag := fs.BoolP("version", "v", false, "get version number")
|
||||
@@ -59,6 +68,7 @@ func main() {
|
||||
viper.RegisterAlias("backendUrl", "backend-url")
|
||||
hostname, _ := os.Hostname()
|
||||
viper.SetDefault("jwt-secret", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9")
|
||||
viper.SetDefault("ui-logo", "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif")
|
||||
viper.Set("hostname", hostname)
|
||||
viper.Set("version", version.VERSION)
|
||||
viper.Set("revision", version.REVISION)
|
||||
@@ -90,6 +100,18 @@ func main() {
|
||||
viper.Set("port", strconv.Itoa(port))
|
||||
}
|
||||
|
||||
// load gRPC server config
|
||||
var grpcCfg grpc.Config
|
||||
if err := viper.Unmarshal(&grpcCfg); err != nil {
|
||||
logger.Panic("config unmarshal failed", zap.Error(err))
|
||||
}
|
||||
|
||||
// start gRPC server
|
||||
if grpcCfg.Port > 0 {
|
||||
grpcSrv, _ := grpc.NewServer(&grpcCfg, logger)
|
||||
go grpcSrv.ListenAndServe()
|
||||
}
|
||||
|
||||
// load HTTP server config
|
||||
var srvCfg api.Config
|
||||
if err := viper.Unmarshal(&srvCfg); err != nil {
|
||||
|
||||
@@ -7,11 +7,11 @@ The e2e testing infrastructure is powered by CircleCI and [Kubernetes Kind](http
|
||||
* download go modules
|
||||
* run unit tests
|
||||
* build container
|
||||
* install kubectl, helm and Kubernetes Kind CLIs
|
||||
* install kubectl, Helm v3 and Kubernetes Kind CLIs
|
||||
* create local Kubernetes cluster with kind
|
||||
* deploy Tiller on the local cluster
|
||||
* load podinfo image onto the local cluster
|
||||
* deploy podinfo with Helm
|
||||
* set the podinfo image to the locally built one
|
||||
* run Helm tests
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
set -o errexit
|
||||
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
KIND_VERSION=v0.4.0
|
||||
KIND_VERSION=v0.7.0
|
||||
|
||||
if [[ "$1" ]]; then
|
||||
KIND_VERSION=$1
|
||||
@@ -22,13 +22,5 @@ sudo mv kind /usr/local/bin/kind
|
||||
echo ">>> Creating kind cluster"
|
||||
kind create cluster --wait 5m
|
||||
|
||||
export KUBECONFIG="$(kind get kubeconfig-path --name="kind")"
|
||||
kubectl get pods --all-namespaces
|
||||
|
||||
echo ">>> Installing Helm"
|
||||
curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get | bash
|
||||
|
||||
echo '>>> Installing Tiller'
|
||||
kubectl --namespace kube-system create sa tiller
|
||||
kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
|
||||
helm init --service-account tiller --upgrade --wait
|
||||
echo ">>> Installing Helm v3"
|
||||
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
set -o errexit
|
||||
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
export KUBECONFIG="$(kind get kubeconfig-path --name="kind")"
|
||||
|
||||
echo '>>> Loading image in Kind'
|
||||
kind load docker-image test/podinfo:latest
|
||||
|
||||
12
e2e/test.sh
12
e2e/test.sh
@@ -2,11 +2,11 @@
|
||||
|
||||
set -o errexit
|
||||
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
export KUBECONFIG="$(kind get kubeconfig-path --name="kind")"
|
||||
function finish {
|
||||
echo '>>> Test logs'
|
||||
kubectl logs -l app=podinfo
|
||||
}
|
||||
trap finish EXIT
|
||||
|
||||
echo '>>> Testing'
|
||||
echo '>>> Start integration tests'
|
||||
helm test podinfo
|
||||
|
||||
echo '>>> Test logs'
|
||||
kubectl logs -l app=podinfo
|
||||
|
||||
8
go.mod
8
go.mod
@@ -1,9 +1,8 @@
|
||||
module github.com/stefanprodan/podinfo
|
||||
|
||||
go 1.12
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
|
||||
github.com/aws/aws-sdk-go v1.15.63 // indirect
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect
|
||||
@@ -14,7 +13,7 @@ require (
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/fatih/color v1.7.0
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/golang/protobuf v1.2.0 // indirect
|
||||
github.com/go-chi/chi v4.0.2+incompatible // indirect
|
||||
github.com/gorilla/mux v1.7.3
|
||||
github.com/gorilla/websocket v1.4.0
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0 // indirect
|
||||
@@ -48,5 +47,6 @@ require (
|
||||
go.uber.org/atomic v1.3.2 // indirect
|
||||
go.uber.org/multierr v1.1.0 // indirect
|
||||
go.uber.org/zap v1.9.1
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 // indirect
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80
|
||||
google.golang.org/grpc v1.23.0
|
||||
)
|
||||
|
||||
18
go.sum
18
go.sum
@@ -1,3 +1,4 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
@@ -20,6 +21,7 @@ github.com/chzyer/readline v0.0.0-20160726135117-62c6fe619375 h1:JVe1zduaiPlSLOu
|
||||
github.com/chzyer/readline v0.0.0-20160726135117-62c6fe619375/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -31,6 +33,8 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs=
|
||||
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
@@ -40,8 +44,12 @@ github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k
|
||||
github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
@@ -123,19 +131,29 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
|
||||
go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468 h1:fTfk6GjmihJbK0mSUFgPPgYpsdmApQ86Mcd4GuKax9U=
|
||||
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@@ -5,7 +5,7 @@ metadata:
|
||||
labels:
|
||||
app: podinfo
|
||||
spec:
|
||||
minReadySeconds: 5
|
||||
minReadySeconds: 3
|
||||
revisionHistoryLimit: 5
|
||||
progressDeadlineSeconds: 60
|
||||
strategy:
|
||||
@@ -19,26 +19,36 @@ spec:
|
||||
metadata:
|
||||
annotations:
|
||||
prometheus.io/scrape: "true"
|
||||
prometheus.io/port: "9797"
|
||||
labels:
|
||||
app: podinfo
|
||||
spec:
|
||||
containers:
|
||||
- name: podinfod
|
||||
image: stefanprodan/podinfo:2.1.1
|
||||
image: stefanprodan/podinfo:3.2.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 9898
|
||||
name: http
|
||||
- name: http
|
||||
containerPort: 9898
|
||||
protocol: TCP
|
||||
- name: http-metrics
|
||||
containerPort: 9797
|
||||
protocol: TCP
|
||||
- name: grpc
|
||||
containerPort: 9999
|
||||
protocol: TCP
|
||||
command:
|
||||
- ./podinfo
|
||||
- --port=9898
|
||||
- --port-metrics=9797
|
||||
- --grpc-port=9999
|
||||
- --grpc-service-name=podinfo
|
||||
- --level=info
|
||||
- --random-delay=false
|
||||
- --random-error=false
|
||||
env:
|
||||
- name: PODINFO_UI_COLOR
|
||||
value: blue
|
||||
value: "#34577c"
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
|
||||
@@ -13,3 +13,7 @@ spec:
|
||||
port: 9898
|
||||
protocol: TCP
|
||||
targetPort: http
|
||||
- port: 9999
|
||||
targetPort: grpc
|
||||
protocol: TCP
|
||||
name: grpc
|
||||
|
||||
109
pkg/api/echo.go
109
pkg/api/echo.go
@@ -3,8 +3,10 @@ package api
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/stefanprodan/podinfo/pkg/version"
|
||||
"go.uber.org/zap"
|
||||
@@ -26,59 +28,68 @@ func (s *Server) echoHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
if len(s.config.BackendURL) > 0 {
|
||||
backendReq, err := http.NewRequest("POST", s.config.BackendURL, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
s.logger.Error("backend call failed", zap.Error(err), zap.String("url", s.config.BackendURL))
|
||||
s.ErrorResponse(w, r, "backend call failed", http.StatusInternalServerError)
|
||||
return
|
||||
result := make([]string, len(s.config.BackendURL))
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(s.config.BackendURL))
|
||||
for i, b := range s.config.BackendURL {
|
||||
go func(index int, backend string) {
|
||||
defer wg.Done()
|
||||
backendReq, err := http.NewRequest("POST", backend, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
s.logger.Error("backend call failed", zap.Error(err), zap.String("url", backend))
|
||||
return
|
||||
}
|
||||
|
||||
// forward headers
|
||||
copyTracingHeaders(r, backendReq)
|
||||
|
||||
backendReq.Header.Set("X-API-Version", version.VERSION)
|
||||
backendReq.Header.Set("X-API-Revision", version.REVISION)
|
||||
|
||||
ctx, cancel := context.WithTimeout(backendReq.Context(), s.config.HttpClientTimeout)
|
||||
defer cancel()
|
||||
|
||||
// call backend
|
||||
resp, err := http.DefaultClient.Do(backendReq.WithContext(ctx))
|
||||
if err != nil {
|
||||
s.logger.Error("backend call failed", zap.Error(err), zap.String("url", backend))
|
||||
result[index] = fmt.Sprintf("backend %v call failed %v", backend, err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// copy error status from backend and exit
|
||||
if resp.StatusCode >= 400 {
|
||||
s.logger.Error("backend call failed", zap.Int("status", resp.StatusCode), zap.String("url", backend))
|
||||
result[index] = fmt.Sprintf("backend %v response status code %v", backend, resp.StatusCode)
|
||||
return
|
||||
}
|
||||
|
||||
// forward the received body
|
||||
rbody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
s.logger.Error(
|
||||
"reading the backend request body failed",
|
||||
zap.Error(err),
|
||||
zap.String("url", backend))
|
||||
result[index] = fmt.Sprintf("backend %v call failed %v", backend, err)
|
||||
return
|
||||
}
|
||||
|
||||
s.logger.Debug(
|
||||
"payload received from backend",
|
||||
zap.String("response", string(rbody)),
|
||||
zap.String("url", backend))
|
||||
|
||||
result[index] = string(rbody)
|
||||
}(i, b)
|
||||
}
|
||||
|
||||
// forward headers
|
||||
copyTracingHeaders(r, backendReq)
|
||||
|
||||
backendReq.Header.Set("X-API-Version", version.VERSION)
|
||||
backendReq.Header.Set("X-API-Revision", version.REVISION)
|
||||
|
||||
ctx, cancel := context.WithTimeout(backendReq.Context(), s.config.HttpClientTimeout)
|
||||
defer cancel()
|
||||
|
||||
// call backend
|
||||
resp, err := http.DefaultClient.Do(backendReq.WithContext(ctx))
|
||||
if err != nil {
|
||||
s.logger.Error("backend call failed", zap.Error(err), zap.String("url", s.config.BackendURL))
|
||||
s.ErrorResponse(w, r, "backend call failed", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
// copy error status from backend and exit
|
||||
if resp.StatusCode >= 400 {
|
||||
s.ErrorResponse(w, r, "backend error", resp.StatusCode)
|
||||
return
|
||||
}
|
||||
|
||||
// forward the received body
|
||||
rbody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
s.logger.Error(
|
||||
"reading the backend request body failed",
|
||||
zap.Error(err),
|
||||
zap.String("url", s.config.BackendURL))
|
||||
s.ErrorResponse(w, r, "backend call failed", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
s.logger.Debug(
|
||||
"payload received from backend",
|
||||
zap.String("response", string(rbody)),
|
||||
zap.String("url", s.config.BackendURL))
|
||||
wg.Wait()
|
||||
|
||||
w.Header().Set("X-Color", s.config.UIColor)
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
w.Write(rbody)
|
||||
s.JSONResponse(w, r, result)
|
||||
|
||||
} else {
|
||||
w.Header().Set("X-Color", s.config.UIColor)
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
|
||||
@@ -23,8 +23,10 @@ func (s *Server) indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
data := struct {
|
||||
Title string
|
||||
Logo string
|
||||
}{
|
||||
Title: s.config.Hostname,
|
||||
Logo: s.config.UILogo,
|
||||
}
|
||||
|
||||
if err := tmpl.Execute(w, data); err != nil {
|
||||
|
||||
@@ -22,6 +22,7 @@ func (s *Server) infoHandler(w http.ResponseWriter, r *http.Request) {
|
||||
Hostname: s.config.Hostname,
|
||||
Version: version.VERSION,
|
||||
Revision: version.REVISION,
|
||||
Logo: s.config.UILogo,
|
||||
Color: s.config.UIColor,
|
||||
Message: s.config.UIMessage,
|
||||
GOOS: runtime.GOOS,
|
||||
@@ -39,6 +40,7 @@ type RuntimeResponse struct {
|
||||
Version string `json:"version"`
|
||||
Revision string `json:"revision"`
|
||||
Color string `json:"color"`
|
||||
Logo string `json:"logo"`
|
||||
Message string `json:"message"`
|
||||
GOOS string `json:"goos"`
|
||||
GOARCH string `json:"goarch"`
|
||||
|
||||
@@ -12,7 +12,7 @@ func NewMockServer() *Server {
|
||||
Port: "9898",
|
||||
HttpServerShutdownTimeout: 5 * time.Second,
|
||||
HttpServerTimeout: 30 * time.Second,
|
||||
BackendURL: "",
|
||||
BackendURL: []string{},
|
||||
ConfigPath: "/config",
|
||||
DataPath: "/data",
|
||||
HttpClientTimeout: 30 * time.Second,
|
||||
|
||||
@@ -4,6 +4,9 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/swaggo/swag"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/http2/h2c"
|
||||
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
@@ -40,11 +43,17 @@ var (
|
||||
watcher *fscache.Watcher
|
||||
)
|
||||
|
||||
type FluxConfig struct {
|
||||
GitUrl string `mapstructure:"git-url"`
|
||||
GitBranch string `mapstructure:"git-branch"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
HttpClientTimeout time.Duration `mapstructure:"http-client-timeout"`
|
||||
HttpServerTimeout time.Duration `mapstructure:"http-server-timeout"`
|
||||
HttpServerShutdownTimeout time.Duration `mapstructure:"http-server-shutdown-timeout"`
|
||||
BackendURL string `mapstructure:"backend-url"`
|
||||
BackendURL []string `mapstructure:"backend-url"`
|
||||
UILogo string `mapstructure:"ui-logo"`
|
||||
UIMessage string `mapstructure:"ui-message"`
|
||||
UIColor string `mapstructure:"ui-color"`
|
||||
UIPath string `mapstructure:"ui-path"`
|
||||
@@ -53,8 +62,11 @@ type Config struct {
|
||||
Port string `mapstructure:"port"`
|
||||
PortMetrics int `mapstructure:"port-metrics"`
|
||||
Hostname string `mapstructure:"hostname"`
|
||||
H2C bool `mapstructure:"h2c"`
|
||||
RandomDelay bool `mapstructure:"random-delay"`
|
||||
RandomError bool `mapstructure:"random-error"`
|
||||
Unhealthy bool `mapstructure:"unhealthy"`
|
||||
Unready bool `mapstructure:"unready"`
|
||||
JWTSecret string `mapstructure:"jwt-secret"`
|
||||
}
|
||||
|
||||
@@ -135,12 +147,19 @@ func (s *Server) ListenAndServe(stopCh <-chan struct{}) {
|
||||
s.registerHandlers()
|
||||
s.registerMiddlewares()
|
||||
|
||||
var handler http.Handler
|
||||
if s.config.H2C {
|
||||
handler = h2c.NewHandler(s.router, &http2.Server{})
|
||||
} else {
|
||||
handler = s.router
|
||||
}
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: ":" + s.config.Port,
|
||||
WriteTimeout: s.config.HttpServerTimeout,
|
||||
ReadTimeout: s.config.HttpServerTimeout,
|
||||
IdleTimeout: 2 * s.config.HttpServerTimeout,
|
||||
Handler: s.router,
|
||||
Handler: handler,
|
||||
}
|
||||
|
||||
//s.printRoutes()
|
||||
@@ -164,8 +183,12 @@ func (s *Server) ListenAndServe(stopCh <-chan struct{}) {
|
||||
}()
|
||||
|
||||
// signal Kubernetes the server is ready to receive traffic
|
||||
atomic.StoreInt32(&healthy, 1)
|
||||
atomic.StoreInt32(&ready, 1)
|
||||
if !s.config.Unhealthy {
|
||||
atomic.StoreInt32(&healthy, 1)
|
||||
}
|
||||
if !s.config.Unready {
|
||||
atomic.StoreInt32(&ready, 1)
|
||||
}
|
||||
|
||||
// wait for SIGTERM or SIGINT
|
||||
<-stopCh
|
||||
|
||||
46
pkg/grpc/server.go
Normal file
46
pkg/grpc/server.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/health"
|
||||
"google.golang.org/grpc/health/grpc_health_v1"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
logger *zap.Logger
|
||||
config *Config
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Port int `mapstructure:"grpc-port"`
|
||||
ServiceName string `mapstructure:"grpc-service-name"`
|
||||
}
|
||||
|
||||
func NewServer(config *Config, logger *zap.Logger) (*Server, error) {
|
||||
srv := &Server{
|
||||
logger: logger,
|
||||
config: config,
|
||||
}
|
||||
|
||||
return srv, nil
|
||||
}
|
||||
|
||||
func (s *Server) ListenAndServe() {
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf(":%v", s.config.Port))
|
||||
if err != nil {
|
||||
s.logger.Fatal("failed to listen", zap.Int("port", s.config.Port))
|
||||
}
|
||||
|
||||
srv := grpc.NewServer()
|
||||
server := health.NewServer()
|
||||
grpc_health_v1.RegisterHealthServer(srv, server)
|
||||
server.SetServingStatus(s.config.ServiceName, grpc_health_v1.HealthCheckResponse_SERVING)
|
||||
|
||||
if err := srv.Serve(listener); err != nil {
|
||||
s.logger.Fatal("failed to serve", zap.Error(err))
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package version
|
||||
|
||||
var VERSION = "2.1.1"
|
||||
var VERSION = "3.2.0"
|
||||
var REVISION = "unknown"
|
||||
|
||||
23
ui/vue.html
23
ui/vue.html
@@ -10,8 +10,11 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
|
||||
<style>
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
display: none;
|
||||
}
|
||||
.v-application .v-parallax {
|
||||
height: 100vh !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -19,14 +22,14 @@
|
||||
<v-app dark>
|
||||
<v-content>
|
||||
<section>
|
||||
<v-parallax id="parallax-hero" src="#" :class="info.color">
|
||||
<v-parallax id="parallax-hero" :style="cuddleStyle" src="https://upload.wikimedia.org/wikipedia/commons/c/ca/1x1.png">
|
||||
<v-layout
|
||||
column
|
||||
align-center
|
||||
justify-center
|
||||
class="white--text"
|
||||
>
|
||||
<img src="https://d33wubrfki0l68.cloudfront.net/33a12d8be0bc50be4738443101616e968c7afb8f/cba76/images/scalable.png" alt="kubernetes" height="200">
|
||||
<img :src="info.logo" height="350">
|
||||
<h1 class="white--text mb-2 display-1 text-xs-center">${ info.message }</h1>
|
||||
<div class="subheading mb-3 text-xs-center">Served by <b>${ info.hostname }</b></div>
|
||||
<v-btn
|
||||
@@ -86,11 +89,11 @@
|
||||
tlColor1: 'grey',
|
||||
tlName2: '',
|
||||
tlColor2: 'grey',
|
||||
cuddleStyle: {
|
||||
backgroundColor: '#34577c'
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
document.getElementById('parallax-hero').style.height = '100vh'
|
||||
},
|
||||
created: function() {
|
||||
this.getInfo();
|
||||
this.timer = setInterval(this.getInfo, 3000)
|
||||
@@ -108,7 +111,11 @@
|
||||
}
|
||||
}
|
||||
self.info = data
|
||||
self.info.color = 'cyan'
|
||||
self.info.color = data.color
|
||||
self.cuddleStyle = {
|
||||
backgroundColor: data.color
|
||||
}
|
||||
self.info.logo = data.logo
|
||||
document.title = data.hostname
|
||||
let verColor = (parseInt(data.version.split('.').reverse()[0], 10) % 2 === 0)
|
||||
? 'blue'
|
||||
|
||||
Reference in New Issue
Block a user