Compare commits

...

30 Commits
v0.5.0 ... v0.x

Author SHA1 Message Date
Stefan Prodan
21922197b5 Add resource usage to blue/green dashboard 2018-08-18 14:22:35 +03:00
Stefan Prodan
7ea943525f Add Helm chart for load testing 2018-08-17 18:45:52 +03:00
Stefan Prodan
57ff4465cd Add Istio Blue/Green Grafana dashboard 2018-08-17 17:25:04 +03:00
Stefan Prodan
a86ef1fdb6 Add frontend, backend and store chart values
- add Istio virtual service weight for blue/green
2018-08-17 15:41:23 +03:00
Stefan Prodan
ddf1b80e1b Log backend errors 2018-08-17 15:38:35 +03:00
Stefan Prodan
896aceb240 Add Helm chart for Istio canary deployments and A/B testing 2018-08-16 15:24:04 +03:00
Stefan Prodan
7996f76e71 Release v0.6.1
- update page title when hostname changes
2018-08-16 15:21:26 +03:00
Stefan Prodan
8b04a8f502 Remove old charts 2018-08-16 15:20:21 +03:00
Stefan Prodan
8a6a4e8901 Release v0.6
- Helm chart: use quay image, add color env var, rename backend env var, adjust deployment strategy and set liveness probe to 2s
2018-08-16 00:09:02 +03:00
Stefan Prodan
cf8531c224 Move ping to api/echo 2018-08-16 00:05:32 +03:00
Stefan Prodan
d1574a6601 Decrease Istio HTTP 503 errors with preStop 2018-08-15 19:42:08 +03:00
Stefan Prodan
75d93e0c54 Inject delay and failures for the orange backend 2018-08-15 13:37:40 +03:00
Stefan Prodan
7622dfb74f Add store service 2018-08-15 12:28:03 +03:00
Stefan Prodan
85a26ed71e Add X-Api-Version header
- inject version header for backend calls
- route frontend calls to backend based on API version
2018-08-15 11:16:20 +03:00
Stefan Prodan
81b22f08f8 Add instrumentation list 2018-08-15 11:14:59 +03:00
Stefan Prodan
7d9e3afde7 Beta release v0.6.0-beta.10 2018-08-14 16:41:58 +03:00
Stefan Prodan
3d2028a124 Display hostname as title 2018-08-14 16:41:14 +03:00
Stefan Prodan
1b56648f5b Enable HTTPS redirect in Istio gateway 2018-08-14 16:04:44 +03:00
Stefan Prodan
3a704215a4 Move the public gateway to istio-system ns
- expose Jaeger and Grafana
2018-08-14 15:57:07 +03:00
Stefan Prodan
25aaeff13c Ignore DS_Store 2018-08-14 13:33:36 +03:00
Stefan Prodan
3b93a3445e Make message and color configurable via env vars 2018-08-14 13:21:35 +03:00
Stefan Prodan
a6cc3d2ef9 Reload page when version changes and use fetch API for backend calls 2018-08-14 13:20:05 +03:00
Stefan Prodan
718d8ba4e0 Get external IP from httpbin.org 2018-08-14 11:24:22 +03:00
Stefan Prodan
24ceb25930 Beta release v0.6.0-beta.2 2018-08-13 14:56:13 +03:00
Stefan Prodan
fc8dfc7678 Add Istio Gateway manifests 2018-08-13 14:55:27 +03:00
Stefan Prodan
8e656fdfd0 Add UI/API response and forward OpenTracing headers to backend 2018-08-13 14:54:46 +03:00
Stefan Prodan
a945842e9b Add VueJS UI 2018-08-13 14:52:49 +03:00
Stefan Prodan
09a743f5c2 Add CPU and Memory stress test flags 2018-08-10 11:48:12 +03:00
Stefan Prodan
c44a58602e Release v0.5.1 2018-08-08 12:17:05 +03:00
Stefan Prodan
2ee11bf6b2 Remove deleted files from cache instead of clearing the whole cache 2018-08-08 12:14:26 +03:00
89 changed files with 3544 additions and 780 deletions

3
.gitignore vendored
View File

@@ -10,8 +10,11 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
.DS_Store
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
.idea/
release/
build/
gcloud/

View File

@@ -6,7 +6,7 @@ RUN addgroup -S app \
curl openssl netcat-openbsd
WORKDIR /home/app
COPY ./ui ./ui
ADD podinfo .
RUN chown -R app:app ./

View File

@@ -1,5 +1,6 @@
FROM alpine:3.7
COPY ./ui ./ui
ADD podinfo /podinfo
CMD ["./podinfo"]

View File

@@ -24,7 +24,7 @@ RUN addgroup -S app \
WORKDIR /home/app
COPY --from=builder /go/src/github.com/stefanprodan/k8s-podinfo/podinfo .
COPY ./ui ./ui
RUN chown -R app:app ./
USER app

View File

@@ -21,6 +21,7 @@ build:
@echo Building: linux/$(LINUX_ARCH) $(VERSION) ;\
for arch in $(LINUX_ARCH); do \
mkdir -p build/linux/$$arch && CGO_ENABLED=0 GOOS=linux GOARCH=$$arch go build -ldflags="-s -w -X $(GITREPO)/pkg/version.GITCOMMIT=$(GITCOMMIT)" -o build/linux/$$arch/$(NAME) ./cmd/$(NAME) ;\
cp -r ui/ build/linux/$$arch/ui;\
done
.PHONY: tar
@@ -45,6 +46,7 @@ docker-build: tar
@for arch in $(LINUX_ARCH); do \
mkdir -p build/docker/linux/$$arch ;\
tar -xzf release/$(NAME)_$(VERSION)_linux_$$arch.tgz -C build/docker/linux/$$arch ;\
cp -r ui/ build/docker/linux/$$arch/ui;\
if [ $$arch == amd64 ]; then \
cp Dockerfile build/docker/linux/$$arch ;\
cp Dockerfile build/docker/linux/$$arch/Dockerfile.in ;\
@@ -103,9 +105,10 @@ dep:
.PHONY: charts
charts:
cd charts/ && helm package podinfo/
cd charts/ && helm package podinfo-istio/
cd charts/ && helm package loadtest/
cd charts/ && helm package ambassador/
cd charts/ && helm package grafana/
cd charts/ && helm package ngrok/
cd charts/ && helm package weave-flux/
mv charts/*.tgz docs/
helm repo index docs --url https://stefanprodan.github.io/k8s-podinfo --merge ./docs/index.yaml

View File

@@ -3,7 +3,7 @@ kind: Deployment
metadata:
name: {{ template "grafana.fullname" . }}
labels:
app: {{ template "grafana.name" . }}
app: {{ template "grafana.fullname" . }}
chart: {{ template "grafana.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
@@ -11,12 +11,12 @@ spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ template "grafana.name" . }}
app: {{ template "grafana.fullname" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "grafana.name" . }}
app: {{ template "grafana.fullname" . }}
release: {{ .Release.Name }}
annotations:
prometheus.io/scrape: 'false'

View File

@@ -15,5 +15,5 @@ spec:
protocol: TCP
name: http
selector:
app: {{ template "grafana.name" . }}
app: {{ template "grafana.fullname" . }}
release: {{ .Release.Name }}

View File

@@ -0,0 +1,5 @@
apiVersion: v1
appVersion: "1.0"
description: Hey load test Helm chart for Kubernetes
name: loadtest
version: 0.1.0

View File

@@ -0,0 +1 @@
{{ template "loadtest.fullname" . }} has been deployed successfully!

View File

@@ -2,7 +2,7 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "weave-cloud.name" -}}
{{- define "loadtest.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
@@ -11,7 +11,7 @@ Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "weave-cloud.fullname" -}}
{{- define "loadtest.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
@@ -27,6 +27,6 @@ If release name contains chart name it will be used as a full name.
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "weave-cloud.chart" -}}
{{- define "loadtest.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

View File

@@ -0,0 +1,31 @@
{{- $fullname := include "loadtest.fullname" . -}}
{{- $name := include "loadtest.name" . -}}
{{- $chart := include "loadtest.chart" . -}}
{{- $image := .Values.image -}}
{{- range $test := .Values.tests }}
---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: {{ $fullname }}-{{ .name }}
labels:
app: {{ $name }}
chart: {{ $chart }}
spec:
schedule: "*/1 * * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
spec:
containers:
- name: loadtest
image: {{ $image }}
args:
- /bin/sh
- -c
- "hey -z 58s {{ $test.cmd }} {{ $test.url }}"
restartPolicy: OnFailure
{{- end -}}

View File

@@ -0,0 +1,11 @@
# Default values for loadtest.
image: stefanprodan/loadtest:latest
tests:
- name: "blue"
url: "https://canary.istio.weavedx.com/api/echo"
cmd: "-h2 -m POST -d '{test: 1}' -H 'X-API-Version: 0.6.0' -c 50 -q 5"
- name: "green"
url: "https://canary.istio.weavedx.com/api/echo"
cmd: "-h2 -m POST -d '{test: 2}' -H 'X-API-Version: 0.6.1' -c 10 -q 5"

View File

@@ -0,0 +1,12 @@
apiVersion: v1
appVersion: "0.6.0"
description: Podinfo Helm chart for Istio
name: podinfo-istio
version: 0.1.0
home: https://github.com/stefanprodan/k8s-podinfo
sources:
- https://github.com/stefanprodan/k8s-podinfo
maintainers:
- name: stefanprodan
email: stefanprodan@users.noreply.github.com
engine: gotpl

View File

@@ -0,0 +1,80 @@
# Podinfo Istio
Podinfo is a tiny web application made with Go
that showcases best practices of running microservices in Kubernetes.
## Installing the Chart
Create an Istio enabled namespace:
```console
kubectl create namespace demo
kubectl label namespace demo istio-injection=enabled
```
Create an Istio Gateway in the `istio-system` namespace named `public-gateway`:
```yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: public-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
tls:
httpsRedirect: true
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- "*"
tls:
mode: SIMPLE
privateKey: /etc/istio/ingressgateway-certs/tls.key
serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
```
Create the `frontend` release by specifying the external domain name:
```console
helm upgrade frontend --install ./charts/podinfo-istio \
--namespace=demo \
--set host=podinfo.example.com \
--set gateway.name=public-gateway \
--set gateway.create=false \
-f ./charts/podinfo-istio/frontend.yaml
```
Create the `backend` release:
```console
helm upgrade backend --install ./charts/podinfo-istio \
--namespace=demo \
-f ./charts/podinfo-istio/backend.yaml
```
Create the `store` release:
```console
helm upgrade store --install ./charts/podinfo-istio \
--namespace=demo \
-f ./charts/podinfo-istio/store.yaml
```
Start load test:
```console
helm upgrade --install loadtest ./charts/loadtest \
--namespace=loadtesting
```

34
charts/podinfo-istio/apply.sh Executable file
View File

@@ -0,0 +1,34 @@
#!/usr/bin/env bash
#Usage: fswatch -o ./podinfo-istio/ | xargs -n1 ./podinfo-istio/apply.sh
set -e
MARK='\033[0;32m'
NC='\033[0m'
log (){
echo -e "$(date +%Y-%m-%dT%H:%M:%S%z) ${MARK}${1}${NC}"
}
log "installing frontend"
helm upgrade frontend --install ./podinfo-istio \
--namespace=demo \
--set host=canary.istio.weavedx.com \
--set gateway.name=public-gateway \
--set gateway.create=false \
-f ./podinfo-istio/frontend.yaml
log "installing backend"
helm upgrade backend --install ./podinfo-istio \
--namespace=demo \
-f ./podinfo-istio/backend.yaml
log "installing store"
helm upgrade store --install ./podinfo-istio \
--namespace=demo \
-f ./podinfo-istio/store.yaml
log "finished installing frontend, backend and store"

View File

@@ -0,0 +1,21 @@
# Default values for backend demo.
# expose the blue/green deployments inside the cluster
host: backend
# stable release
blue:
replicas: 2
tag: "0.6.0"
backend: http://store:9898/api/echo
# canary release
green:
replicas: 2
tag: "0.6.1"
routing:
# target green callers
- match:
- sourceLabels:
color: green
backend: http://store:9898/api/echo

View File

@@ -0,0 +1,39 @@
# Default values for frontend demo.
# external domain
host:
exposeHost: true
# no more than one Gateway can be created on a cluster
# if TLS is enabled the istio-ingressgateway-certs secret must exist in istio-system ns
# if you have a Gateway running you can set the name to your own gateway and turn off create
gateway:
name: public-gateway
create: true
tls: true
httpsRedirect: true
# stable release
blue:
replicas: 2
tag: "0.6.0"
message: "Greetings from the blue frontend"
backend: http://backend:9898/api/echo
# canary release
green:
replicas: 2
tag: "0.6.1"
routing:
# target Safari
- match:
- headers:
user-agent:
regex: "^(?!.*Chrome).*Safari.*"
# target API clients by version
- match:
- headers:
x-api-version:
regex: "^(v{0,1})0\\.6\\.([1-9]).*"
message: "Greetings from the green frontend"
backend: http://backend:9898/api/echo

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
# Default values for backend demo.
# expose the store deployment inside the cluster
host: store
# load balance 80/20 between blue and green
blue:
replicas: 2
tag: "0.6.0"
backend: https://httpbin.org/anything
weight: 80
green:
replicas: 2
tag: "0.6.1"
backend: https://httpbin.org/anything
externalServices:
- httpbin.org

View File

@@ -0,0 +1 @@
{{ template "podinfo-istio.fullname" . }} has been deployed successfully!

View File

@@ -2,42 +2,35 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "weave-flux.name" -}}
{{- define "podinfo-istio.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
The release name is used as a full name.
*/}}
{{- define "weave-flux.fullname" -}}
{{- define "podinfo-istio.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- define "podinfo-istio.blue" -}}
{{- printf "%s-%s" .Release.Name "blue" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "podinfo-istio.green" -}}
{{- printf "%s-%s" .Release.Name "green" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "weave-flux.chart" -}}
{{- define "podinfo-istio.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create the name of the service account to use
*/}}
{{- define "weave-flux.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{ default (include "weave-flux.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}

View File

@@ -0,0 +1,76 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "podinfo-istio.blue" . }}
labels:
app: {{ template "podinfo-istio.fullname" . }}
chart: {{ template "podinfo-istio.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
color: blue
version: {{ .Values.blue.tag }}
spec:
replicas: {{ .Values.blue.replicas }}
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
app: {{ template "podinfo-istio.fullname" . }}
color: blue
template:
metadata:
labels:
app: {{ template "podinfo-istio.fullname" . }}
color: blue
version: {{ .Values.blue.tag }}
annotations:
prometheus.io/scrape: 'true'
spec:
terminationGracePeriodSeconds: 30
containers:
- name: podinfod
image: "{{ .Values.blue.repository }}:{{ .Values.blue.tag }}"
imagePullPolicy: {{ .Values.imagePullPolicy }}
command:
- ./podinfo
- -port={{ .Values.containerPort }}
- -logLevel={{ .Values.logLevel }}
env:
- name: color
value: blue
{{- if .Values.blue.backend }}
- name: backendURL
value: {{ .Values.blue.backend }}
{{- end }}
{{- if .Values.blue.message }}
- name: message
value: {{ .Values.blue.message }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.containerPort }}
protocol: TCP
readinessProbe:
httpGet:
path: /readyz
port: 9898
initialDelaySeconds: 1
periodSeconds: 2
failureThreshold: 1
livenessProbe:
httpGet:
path: /healthz
port: 9898
initialDelaySeconds: 1
periodSeconds: 10
failureThreshold: 2
volumeMounts:
- name: data
mountPath: /data
resources:
{{ toYaml .Values.resources | indent 12 }}
volumes:
- name: data
emptyDir: {}

View File

@@ -0,0 +1,20 @@
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: {{ template "podinfo-istio.fullname" . }}
labels:
app: {{ template "podinfo-istio.fullname" . }}
chart: {{ template "podinfo-istio.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
host: {{ template "podinfo-istio.fullname" . }}
subsets:
- name: blue
labels:
color: blue
{{- if gt .Values.green.replicas 0.0 }}
- name: green
labels:
color: green
{{- end }}

View File

@@ -0,0 +1,22 @@
{{- if .Values.externalServices -}}
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: {{ template "podinfo-istio.fullname" . }}-external-svcs
labels:
app: {{ template "podinfo-istio.fullname" . }}
chart: {{ template "podinfo-istio.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
hosts:
{{- range .Values.externalServices }}
- {{ . }}
{{- end }}
location: MESH_EXTERNAL
ports:
- number: 443
name: https
protocol: HTTPS
resolution: DNS
{{- end }}

View File

@@ -0,0 +1,31 @@
{{- if .Values.gateway.create -}}
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: {{ .Values.gateway.name }}
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
tls:
httpsRedirect: {{ .Values.gateway.httpsRedirect }}
{{- if .Values.gateway.tls }}
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- "*"
tls:
mode: SIMPLE
privateKey: /etc/istio/ingressgateway-certs/tls.key
serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
{{- end }}
{{- end }}

View File

@@ -0,0 +1,78 @@
{{- if gt .Values.green.replicas 0.0 -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "podinfo-istio.green" . }}
labels:
app: {{ template "podinfo-istio.fullname" . }}
chart: {{ template "podinfo-istio.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
color: green
version: {{ .Values.green.tag }}
spec:
replicas: {{ .Values.green.replicas }}
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
app: {{ template "podinfo-istio.fullname" . }}
color: green
template:
metadata:
labels:
app: {{ template "podinfo-istio.fullname" . }}
color: green
version: {{ .Values.green.tag }}
annotations:
prometheus.io/scrape: 'true'
spec:
terminationGracePeriodSeconds: 30
containers:
- name: podinfod
image: "{{ .Values.green.repository }}:{{ .Values.green.tag }}"
imagePullPolicy: {{ .Values.imagePullPolicy }}
command:
- ./podinfo
- -port={{ .Values.containerPort }}
- -logLevel={{ .Values.logLevel }}
env:
- name: color
value: green
{{- if .Values.green.backend }}
- name: backendURL
value: {{ .Values.green.backend }}
{{- end }}
{{- if .Values.green.message }}
- name: message
value: {{ .Values.green.message }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.containerPort }}
protocol: TCP
readinessProbe:
httpGet:
path: /readyz
port: 9898
initialDelaySeconds: 1
periodSeconds: 2
failureThreshold: 1
livenessProbe:
httpGet:
path: /healthz
port: 9898
initialDelaySeconds: 1
periodSeconds: 10
failureThreshold: 2
volumeMounts:
- name: data
mountPath: /data
resources:
{{ toYaml .Values.resources | indent 12 }}
volumes:
- name: data
emptyDir: {}
{{- end }}

View File

@@ -0,0 +1,18 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "podinfo-istio.fullname" . }}
labels:
app: {{ template "podinfo-istio.fullname" . }}
chart: {{ template "podinfo-istio.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
type: ClusterIP
ports:
- port: {{ .Values.containerPort }}
targetPort: http
protocol: TCP
name: http
selector:
app: {{ template "podinfo-istio.fullname" . }}

View File

@@ -0,0 +1,43 @@
{{- $host := .Release.Name -}}
{{- $timeout := .Values.timeout -}}
{{- $greenWeight := (sub 100 (.Values.blue.weight|int)) | int -}}
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: {{ template "podinfo-istio.fullname" . }}
labels:
app: {{ template "podinfo-istio.fullname" . }}
chart: {{ template "podinfo-istio.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
hosts:
- {{ .Values.host }}
{{- if .Values.exposeHost }}
gateways:
- {{ .Values.gateway.name }}.istio-system.svc.cluster.local
{{- end }}
http:
{{- if gt .Values.green.replicas 0.0 }}
{{- range .Values.green.routing }}
- match:
{{ toYaml .match | indent 6 }}
route:
- destination:
host: {{ $host }}
subset: green
timeout: {{ $timeout }}
{{- end }}
{{- end }}
- route:
- destination:
host: {{ template "podinfo-istio.fullname" . }}
subset: blue
weight: {{ .Values.blue.weight }}
{{- if gt .Values.green.replicas 0.0 }}
- destination:
host: {{ template "podinfo-istio.fullname" . }}
subset: green
weight: {{ $greenWeight }}
{{- end }}
timeout: {{ $timeout }}

View File

@@ -0,0 +1,55 @@
# Default values for podinfo-istio.
# host can be an extarnal domain or a local one
host: podinfo
# if the host is an external domain must be exposed via the Gateway
exposeHost: false
timeout: 30s
# creates public-gateway.istio-system.svc.cluster.local
# no more than one Gateway can be created on a cluster
# if TLS is enabled the istio-ingressgateway-certs secret must exist in istio-system ns
# if you have a Gateway running you can set the name to your own gateway and turn off create
gateway:
name: public-gateway
create: false
tls: false
httpsRedirect: false
# authorise external https services
#externalServices:
# - api.github.com
# - apis.google.com
# - googleapis.com
# stable release
# by default all traffic goes to blue
blue:
replicas: 2
repository: quay.io/stefanprodan/podinfo
tag: "0.6.0"
# green must have at at least one replica to set weight under 100
weight: 100
message:
backend:
# canary release
# disabled with 0 replicas
green:
replicas: 0
repository: quay.io/stefanprodan/podinfo
tag: "0.6.1"
message:
backend:
routing:
# blue/green common settings
logLevel: info
containerPort: 9898
imagePullPolicy: IfNotPresent
resources:
limits:
requests:
cpu: 1m
memory: 16Mi

View File

@@ -1,8 +1,8 @@
apiVersion: v1
appVersion: "0.5.0"
appVersion: "0.6.0"
description: Podinfo Helm chart for Kubernetes
name: podinfo
version: 0.2.0
version: 0.2.2
home: https://github.com/stefanprodan/k8s-podinfo
sources:
- https://github.com/stefanprodan/k8s-podinfo

View File

@@ -7,20 +7,31 @@ metadata:
chart: {{ template "podinfo.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
color: {{ .Values.color }}
version: {{ .Values.image.tag }}
spec:
replicas: {{ .Values.replicaCount }}
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
app: {{ template "podinfo.name" . }}
color: {{ .Values.color }}
version: {{ .Values.image.tag }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "podinfo.name" . }}
color: {{ .Values.color }}
version: {{ .Values.image.tag }}
release: {{ .Release.Name }}
annotations:
prometheus.io/scrape: 'true'
spec:
terminationGracePeriodSeconds: 30
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
@@ -30,20 +41,30 @@ spec:
- -port={{ .Values.service.containerPort }}
- -logLevel={{ .Values.logLevel }}
env:
- name: backend_url
- name: color
value: {{ .Values.color }}
{{- if .Values.backend }}
- name: backendURL
value: {{ .Values.backend }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.service.containerPort }}
protocol: TCP
livenessProbe:
httpGet:
path: /healthz
port: http
readinessProbe:
httpGet:
path: /readyz
port: http
port: 9898
initialDelaySeconds: 1
periodSeconds: 2
failureThreshold: 1
livenessProbe:
httpGet:
path: /healthz
port: 9898
initialDelaySeconds: 1
periodSeconds: 10
failureThreshold: 2
volumeMounts:
- name: data
mountPath: /data

View File

@@ -1,11 +1,12 @@
# Default values for podinfo.
replicaCount: 1
backend: http://backend-podinfo:9898/echo
replicaCount: 2
color: blue
backend: #http://backend-podinfo:9898/echo
image:
repository: stefanprodan/podinfo
tag: 0.4.0
repository: quay.io/stefanprodan/podinfo
tag: 0.6.0
pullPolicy: IfNotPresent
service:
@@ -14,7 +15,7 @@ service:
containerPort: 9898
nodePort: 31198
# Heapster or metrics-server add-on required
# metrics-server add-on required
hpa:
enabled: false
maxReplicas: 10
@@ -50,4 +51,4 @@ tolerations: []
affinity: {}
logLevel: debug
logLevel: info

View File

@@ -1,13 +0,0 @@
apiVersion: v1
appVersion: "1.0"
description: Weave Cloud is a add-on to Kubernetes which provides Continuous Delivery, along with hosted Prometheus Monitoring and a visual dashboard for exploring & debugging microservices
name: weave-cloud
version: 0.2.0
home: https://weave.works
maintainers:
- name: Ilya Dmitrichenko
email: ilya@weave.works
- name: Stefan Prodan
email: stefan@weave.works
engine: gotpl
icon: https://www.weave.works/assets/images/bltd108e8f850ae9e7c/weave-logo-512.png

View File

@@ -1,53 +0,0 @@
# Weave Cloud Agents
> ***NOTE: This chart is for Kubernetes version 1.6 and later.***
Weave Cloud is a add-on to Kubernetes which provides Continuous Delivery, along with hosted Prometheus Monitoring and a visual dashboard for exploring & debugging microservices.
This package contains the agents which connect your cluster to Weave Cloud.
_To learn more and sign up please visit [Weaveworks website](https://weave.works)._
You will need a service token which you can get from [cloud.weave.works](https://cloud.weave.works/).
## Installing the Chart
To install the chart:
```console
$ helm install --name weave-cloud --namespace weave --set token=<YOUR_WEAVE_CLOUD_SERVICE_TOKEN> stable/weave-cloud
```
To view the pods installed:
```console
$ kubectl get pods -n weave -l weave-cloud-component
```
To upgrade the chart:
```console
$ helm upgrade --reuse-values weave-cloud stable/weave-cloud
```
## Uninstalling the Chart
To uninstall/delete the `weave-cloud` chart:
```console
$ helm delete --purge weave-cloud
```
Delete the `weave` namespace:
```console
$ kubectl delete namespace weave
```
The command removes all the Kubernetes components associated with the chart and deletes the release.
## Configuration
The following tables lists the configurable parameters of the Weave Cloud Agents chart and their default values.
| Parameter | Description | Default |
| --------- | ----------- | ------- |
| `token` | Weave Cloud service token | _none_ _(**must be set**)_ |

View File

@@ -1,28 +0,0 @@
{{- if .Values.token -}}
Weave Cloud agents had been installed!
First, verify all Pods are running:
kubectl get pods -n {{ .Release.Namespace }}
Next, login to Weave Cloud (https://cloud.weave.works) and verify the agents are connect to your instance.
If you need help or have any question, join our Slack to chat to us https://slack.weave.works.
Happy hacking!
{{- else -}}
#######################################################
#### ERROR: Weave Cloud service token is missing ####
#######################################################
The installation of Weave Cloud agents is incomplete until you set the service token.
To retrieve your Weave Cloud service token, log in to your instance first: https://cloud.weave.works/instances
Then run:
helm upgrade {{ .Release.Name }} --set token=<YOUR_WEAVE_CLOUD_SERVICE_TOKEN> stable/weave-cloud
{{- end }}

View File

@@ -1,40 +0,0 @@
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: {{ .Values.agent.name }}
labels:
app: {{ .Values.agent.name }}
chart: {{ template "weave-cloud.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
minReadySeconds: 30
replicas: 1
revisionHistoryLimit: 2
selector:
matchLabels:
app: {{ .Values.agent.name }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ .Values.agent.name }}
release: {{ .Release.Name }}
spec:
serviceAccount: {{ .Values.agent.name }}
serviceAccountName: {{ .Values.agent.name }}
terminationGracePeriodSeconds: 30
containers:
- name: {{ .Values.agent.name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
args:
- -agent.poll-url=https://get.weave.works/k8s/agent.yaml?instanceID={{"{{.InstanceID}}"}}
- -wc.hostname=cloud.weave.works
- -wc.token=$(WEAVE_CLOUD_TOKEN)
env:
- name: WEAVE_CLOUD_TOKEN
valueFrom:
secretKeyRef:
key: token
name: weave-cloud

View File

@@ -1,39 +0,0 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Values.agent.name }}
namespace: {{ .Release.Namespace }}
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: {{ .Values.agent.name }}
labels:
name: {{ .Values.agent.name }}
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
- nonResourceURLs:
- '*'
verbs:
- '*'
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: {{ .Values.agent.name }}
labels:
name: {{ .Values.agent.name }}
roleRef:
kind: ClusterRole
name: {{ .Values.agent.name }}
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: {{ .Values.agent.name }}
namespace: {{ .Release.Namespace }}

View File

@@ -1,7 +0,0 @@
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: weave-cloud
data:
token: {{ .Values.token | b64enc }}

View File

@@ -1,12 +0,0 @@
# Default values for weave-cloud.
# token: ""
agent:
name: weave-agent
image:
repository: quay.io/weaveworks/launcher-agent
tag: master-b60524c
pullPolicy: IfNotPresent

View File

@@ -1,13 +0,0 @@
apiVersion: v1
appVersion: "1.3.0"
description: Flux is a tool that automatically ensures that the state of a cluster matches what is specified in version control
name: weave-flux
version: 0.2.0
home: https://weave.works
sources:
- https://github.com/weaveworks/flux
maintainers:
- name: stefanprodan
email: stefan@weave.works
engine: gotpl
icon: https://landscape.cncf.io/logos/weave-flux.svg

View File

@@ -1,115 +0,0 @@
# Weave Flux OSS
Flux is a tool that automatically ensures that the state of a cluster matches what is specified in version control.
It is most useful when used as a deployment tool at the end of a Continuous Delivery pipeline. Flux will make sure that your new container images and config changes are propagated to the cluster.
## Introduction
This chart bootstraps a [Weave Flux](https://github.com/weaveworks/flux) deployment on
a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
## Prerequisites
- Kubernetes 1.7+
## Installing the Chart
To install the chart with the release name `cd`:
```console
$ helm install --name cd \
--set git.url=git@github.com:weaveworks/flux-example \
--namespace flux \
./charts/weave-flux
```
To install Flux with the Helm operator:
```console
$ helm install --name cd \
--set git.url=git@github.com:stefanprodan/weave-flux-helm-demo \
--set git.user="Stefan Prodan" \
--set git.email="stefan.prodan@gmail.com" \
--set helmOperator.create=true \
--namespace flux \
./charts/weave-flux
```
Be aware that the Helm operator is alpha quality, DO NOT use it on a production cluster.
The [configuration](#configuration) section lists the parameters that can be configured during installation.
### Setup Git deploy
At startup Flux generates a SSH key and logs the public key.
Find the SSH public key with:
```bash
export FLUX_POD=$(kubectl get pods --namespace flux -l "app=weave-flux,release=cd" -o jsonpath="{.items[0].metadata.name}")
kubectl -n flux logs $FLUX_POD | grep identity.pub | cut -d '"' -f2 | sed 's/.\{2\}$//'
```
In order to sync your cluster state with GitHub you need to copy the public key and
create a deploy key with write access on your GitHub repository.
## Uninstalling the Chart
To uninstall/delete the `cd` deployment:
```console
$ helm delete --purge cd
```
The command removes all the Kubernetes components associated with the chart and deletes the release.
You should also remove the deploy key from your GitHub repository.
## Configuration
The following tables lists the configurable parameters of the Weave Flux chart and their default values.
| Parameter | Description | Default |
| ------------------------------- | ------------------------------------------ | ---------------------------------------------------------- |
| `image.repository` | Image repository | `quay.io/weaveworks/flux`
| `image.tag` | Image tag | `1.2.5`
| `image.pullPoliwell cy` | Image pull policy | `IfNotPresent`
| `resources` | CPU/memory resource requests/limits | None
| `rbac.create` | If `true`, create and use RBAC resources | `true`
| `serviceAccount.create` | If `true`, create a new service account | `true`
| `serviceAccount.name` | Service account to be used | `weave-flux`
| `service.type` | Service type to be used | `ClusterIP`
| `service.port` | Service port to be used | `3030`
| `git.url` | URL of git repo with Kubernetes manifests | None
| `git.branch` | Branch of git repo to use for Kubernetes manifests | `master`
| `git.path` | Path within git repo to locate Kubernetes manifests (relative path) | None
| `git.user` | Username to use as git committer | `Weave Flux`
| `git.email` | Email to use as git committer | `support@weave.works`
| `git.chartsPath` | Path within git repo to locate Helm charts (relative path) | `charts`
| `git.pollInterval` | Period at which to poll git repo for new commits | `30s`
| `helmOperator.create` | If `true`, install the Helm operator | `false`
| `helmOperator.repository` | Helm operator image repository | `quay.io/weaveworks/helm-operator`
| `helmOperator.tag` | Helm operator image tag | `master-6f427cb`
| `helmOperator.pullPolicy` | Helm operator image pull policy | `IfNotPresent`
| `token` | Weave Cloud service token | None
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example:
```console
$ helm upgrade --install --wait cd \
--set git.url=git@github.com:stefanprodan/podinfo \
--set git.path=deploy/auto-scaling \
--namespace flux \
./charts/weave-flux
```
## Upgrade
Update Weave Flux version with:
```console
helm upgrade --reuse-values cd \
--set image.tag=1.2.6 \
./charts/weave-flux
```

View File

@@ -1,19 +0,0 @@
1. Get the application URL by running these commands:
{{- if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "weave-flux.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w {{ template "weave-flux.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "weave-flux.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "weave-flux.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
kubectl -n {{ .Release.Namespace }} port-forward $POD_NAME 8080:3030
{{- end }}
2. Get the Git deploy key by running these commands:
export FLUX_POD=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "weave-flux.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
kubectl -n {{ .Release.Namespace }} logs $FLUX_POD | grep identity.pub | cut -d '"' -f2 | sed 's/.\{2\}$//'

View File

@@ -1,75 +0,0 @@
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: {{ template "weave-flux.fullname" . }}
labels:
app: {{ template "weave-flux.name" . }}
chart: {{ template "weave-flux.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ template "weave-flux.name" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "weave-flux.name" . }}
release: {{ .Release.Name }}
spec:
{{- if .Values.serviceAccount.create }}
serviceAccountName: {{ template "weave-flux.serviceAccountName" . }}
{{- end }}
volumes:
- name: git-key
secret:
secretName: {{ template "weave-flux.fullname" . }}-git-deploy
defaultMode: 0400
- name: git-keygen
emptyDir:
medium: Memory
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 3030
protocol: TCP
volumeMounts:
- name: git-key
mountPath: /etc/fluxd/ssh
readOnly: true
- name: git-keygen
mountPath: /var/fluxd/keygen
args:
- --ssh-keygen-dir=/var/fluxd/keygen
- --k8s-secret-name={{ template "weave-flux.fullname" . }}-git-deploy
- --memcached-hostname={{ template "weave-flux.fullname" . }}-memcached
- --git-url={{ .Values.git.url }}
- --git-branch={{ .Values.git.branch }}
- --git-path={{ .Values.git.path }}
- --git-user={{ .Values.git.user }}
- --git-email={{ .Values.git.email }}
- --git-poll-interval={{ .Values.git.pollInterval }}
- --sync-interval={{ .Values.git.pollInterval }}
{{- if .Values.token }}
- --connect=wss://cloud.weave.works/api/flux
- --token={{ .Values.token }}
{{- end }}
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}

View File

@@ -1,19 +0,0 @@
{{- if .Values.helmOperator.create -}}
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: fluxhelmreleases.helm.integrations.flux.weave.works
labels:
app: {{ template "weave-flux.name" . }}
chart: {{ template "weave-flux.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
group: helm.integrations.flux.weave.works
names:
kind: FluxHelmRelease
listKind: FluxHelmReleaseList
plural: fluxhelmreleases
scope: Namespaced
version: v1alpha2
{{- end -}}

View File

@@ -1,43 +0,0 @@
{{- if .Values.helmOperator.create -}}
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: {{ template "weave-flux.fullname" . }}-helm-operator
labels:
app: {{ template "weave-flux.name" . }}-helm-operator
chart: {{ template "weave-flux.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ template "weave-flux.name" . }}-helm-operator
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "weave-flux.name" . }}-helm-operator
release: {{ .Release.Name }}
spec:
{{- if .Values.serviceAccount.create }}
serviceAccountName: {{ template "weave-flux.serviceAccountName" . }}
{{- end }}
volumes:
- name: git-key
secret:
secretName: {{ template "weave-flux.fullname" . }}-git-deploy
defaultMode: 0400
containers:
- name: flux-helm-operator
image: "{{ .Values.helmOperator.repository }}:{{ .Values.helmOperator.tag }}"
imagePullPolicy: {{ .Values.helmOperator.pullPolicy }}
volumeMounts:
- name: git-key
mountPath: /etc/fluxd/ssh
readOnly: true
args:
- --git-url={{ .Values.git.url }}
- --git-branch={{ .Values.git.branch }}
- --git-charts-path={{ .Values.git.chartsPath }}
{{- end -}}

View File

@@ -1,54 +0,0 @@
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: {{ template "weave-flux.fullname" . }}-memcached
labels:
app: {{ template "weave-flux.name" . }}-memcached
chart: {{ template "weave-flux.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: {{ template "weave-flux.name" . }}-memcached
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "weave-flux.name" . }}-memcached
release: {{ .Release.Name }}
spec:
containers:
- name: memcached
image: memcached:1.4.25
imagePullPolicy: IfNotPresent
args:
- -m 64 # Maximum memory to use, in megabytes. 64MB is default.
- -p 11211 # Default port, but being explicit is nice.
- -vv # This gets us to the level of request logs.
ports:
- name: memcached
containerPort: 11211
---
apiVersion: v1
kind: Service
metadata:
name: {{ template "weave-flux.fullname" . }}-memcached
labels:
app: {{ template "weave-flux.name" . }}-memcached
chart: {{ template "weave-flux.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
clusterIP: None
ports:
- port: 11211
targetPort: memcached
protocol: TCP
name: memcached
selector:
app: {{ template "weave-flux.name" . }}-memcached
release: {{ .Release.Name }}

View File

@@ -1,40 +0,0 @@
{{- if .Values.rbac.create -}}
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: {{ template "weave-flux.fullname" . }}
labels:
app: {{ template "weave-flux.name" . }}
chart: {{ template "weave-flux.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
- nonResourceURLs:
- '*'
verbs:
- '*'
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: {{ template "weave-flux.fullname" . }}
labels:
app: {{ template "weave-flux.name" . }}
chart: {{ template "weave-flux.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ template "weave-flux.fullname" . }}
subjects:
- name: {{ template "weave-flux.serviceAccountName" . }}
namespace: {{ .Release.Namespace | quote }}
kind: ServiceAccount
{{- end -}}

View File

@@ -1,5 +0,0 @@
apiVersion: v1
kind: Secret
metadata:
name: {{ template "weave-flux.fullname" . }}-git-deploy
type: Opaque

View File

@@ -1,19 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "weave-flux.fullname" . }}
labels:
app: {{ template "weave-flux.name" . }}
chart: {{ template "weave-flux.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
app: {{ template "weave-flux.name" . }}
release: {{ .Release.Name }}

View File

@@ -1,11 +0,0 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ template "weave-flux.serviceAccountName" . }}
labels:
app: {{ template "weave-flux.name" . }}
chart: {{ template "weave-flux.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- end -}}

View File

@@ -1,64 +0,0 @@
# Default values for weave-flux.
# Weave Cloud service token
token: ""
replicaCount: 1
image:
repository: quay.io/weaveworks/flux
tag: 1.3.0
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 3030
helmOperator:
create: false
repository: quay.io/weaveworks/helm-operator
tag: 0.1.0-alpha
pullPolicy: IfNotPresent
rbac:
# Specifies whether RBAC resources should be created
create: true
serviceAccount:
# Specifies whether a service account should be created
create: true
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name:
resources: {}
# If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
nodeSelector: {}
tolerations: []
affinity: {}
git:
# URL of git repo with Kubernetes manifests; e.g. git@github.com:weaveworks/flux-example
url: ""
# Branch of git repo to use for Kubernetes manifests
branch: "master"
# Path within git repo to locate Kubernetes manifests (relative path)
path: ""
# Username to use as git committer
user: "Weave Flux"
# Email to use as git committer
email: "support@weave.works"
# Path within git repo to locate Helm charts (relative path)
chartsPath: "charts"
# Period at which to poll git repo for new commits
pollInterval: "30s"

View File

@@ -2,7 +2,9 @@ package main
import (
"flag"
"io/ioutil"
stdlog "log"
"os"
"time"
"github.com/rs/zerolog"
@@ -13,15 +15,20 @@ import (
)
var (
port string
debug bool
logLevel string
port string
debug bool
logLevel string
stressCPU int
stressMemory int
stressMemoryPayload []byte
)
func init() {
flag.StringVar(&port, "port", "9898", "Port to listen on.")
flag.BoolVar(&debug, "debug", false, "sets log level to debug")
flag.StringVar(&logLevel, "logLevel", "debug", "sets log level as debug, info, warn, error, flat or panic ")
flag.IntVar(&stressCPU, "stressCPU", 0, "Number of CPU cores with 100% load")
flag.IntVar(&stressMemory, "stressMemory", 0, "MB of data to load into memory")
}
func main() {
@@ -32,6 +39,7 @@ func main() {
log.Debug().Msgf("Starting HTTP server on port %v", port)
stopCh := signals.SetupSignalHandler()
beginStressTest(stressCPU, stressMemory)
server.ListenAndServe(port, 5*time.Second, stopCh)
}
@@ -64,3 +72,43 @@ func setLogging() {
stdlog.SetFlags(0)
stdlog.SetOutput(log.Logger)
}
func beginStressTest(cpus int, mem int) {
done := make(chan int)
if cpus > 0 {
log.Info().Msgf("Starting CPU stress, %v core(s)", cpus)
for i := 0; i < cpus; i++ {
go func() {
for {
select {
case <-done:
return
default:
}
}
}()
}
}
if mem > 0 {
path := "/tmp/podinfo.data"
f, err := os.Create(path)
if err != nil {
log.Error().Err(err).Msgf("Memory stress failed")
}
if err := f.Truncate(1000000 * int64(mem)); err != nil {
log.Error().Err(err).Msgf("Memory stress failed")
}
stressMemoryPayload, err = ioutil.ReadFile(path)
f.Close()
os.Remove(path)
if err != nil {
log.Error().Err(err).Msgf("Memory stress failed")
}
log.Info().Msgf("Starting memory stress, size %v", len(stressMemoryPayload))
}
}

View File

@@ -0,0 +1,28 @@
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: public-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
tls:
httpsRedirect: true
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- "*"
tls:
mode: SIMPLE
privateKey: /etc/istio/ingressgateway-certs/tls.key
serverCertificate: /etc/istio/ingressgateway-certs/tls.crt

View File

@@ -0,0 +1,17 @@
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: grafana
namespace: istio-system
spec:
hosts:
- "grafana.istio.weavedx.com"
gateways:
- public-gateway.istio-system.svc.cluster.local
http:
- route:
- destination:
host: grafana
timeout: 30s

View File

@@ -0,0 +1,17 @@
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: grafanax
namespace: istio-system
spec:
hosts:
- "grafanax.istio.weavedx.com"
gateways:
- public-gateway.istio-system.svc.cluster.local
http:
- route:
- destination:
host: grafanax
timeout: 30s

View File

@@ -0,0 +1,17 @@
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: jaeger
namespace: istio-system
spec:
hosts:
- "jaeger.istio.weavedx.com"
gateways:
- public-gateway.istio-system.svc.cluster.local
http:
- route:
- destination:
host: jaeger-query
timeout: 30s

View File

@@ -0,0 +1,14 @@
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: podinfo-backend
spec:
host: podinfo-backend
subsets:
- name: grey
labels:
color: grey
- name: orange
labels:
color: orange

View File

@@ -0,0 +1,64 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: podinfo-backend-grey
labels:
app: podinfo-backend
color: grey
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
app: podinfo-backend
color: grey
template:
metadata:
labels:
app: podinfo-backend
color: grey
annotations:
prometheus.io/scrape: 'true'
spec:
terminationGracePeriodSeconds: 30
containers:
- name: podinfod
image: quay.io/stefanprodan/podinfo:0.6.0
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- sleep 3
command:
- ./podinfo
- -port=9898
- -logLevel=debug
ports:
- name: http
containerPort: 9898
protocol: TCP
readinessProbe:
httpGet:
path: /readyz
port: 9898
livenessProbe:
httpGet:
path: /healthz
port: 9898
resources:
requests:
memory: "32Mi"
cpu: "10m"
env:
- name: color
value: "grey"
- name: message
value: "Greetings from backend grey"
- name: backendURL
value: "http://podinfo-store:9898/echo" #"https://httpbin.org/anything"

View File

@@ -0,0 +1,64 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: podinfo-backend-orange
labels:
app: podinfo-backend
color: orange
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
app: podinfo-backend
color: orange
template:
metadata:
labels:
app: podinfo-backend
color: orange
annotations:
prometheus.io/scrape: 'true'
spec:
terminationGracePeriodSeconds: 30
containers:
- name: podinfod
image: quay.io/stefanprodan/podinfo:0.6.0
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- sleep 3
command:
- ./podinfo
- -port=9898
- -logLevel=debug
ports:
- name: http
containerPort: 9898
protocol: TCP
readinessProbe:
httpGet:
path: /readyz
port: 9898
livenessProbe:
httpGet:
path: /healthz
port: 9898
resources:
requests:
memory: "32Mi"
cpu: "10m"
env:
- name: color
value: "orange"
- name: message
value: "Greetings from backend orange"
- name: backendURL
value: "http://podinfo-store:9898/echo" #"https://httpbin.org/anything"

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: podinfo-backend
labels:
app: podinfo-backend
spec:
type: ClusterIP
ports:
- port: 9898
targetPort: http
protocol: TCP
name: http
selector:
app: podinfo-backend

View File

@@ -0,0 +1,29 @@
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: podinfo-backend
spec:
hosts:
- podinfo-backend
http:
# new version
# forward 100% of the traffic to orange
- match:
# - headers:
# x-api-version:
# regex: "^(v{0,1})0\\.6\\.([0-9]{1,3}).*"
- sourceLabels:
color: blue
route:
- destination:
host: podinfo-backend
subset: orange
timeout: 20s
# default route
# forward 100% of the traffic to grey
- route:
- destination:
host: podinfo-backend
subset: grey
timeout: 20s

View File

@@ -0,0 +1,65 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: podinfo-blue
labels:
app: podinfo
color: blue
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
app: podinfo
color: blue
template:
metadata:
labels:
app: podinfo
color: blue
annotations:
prometheus.io/scrape: 'true'
spec:
terminationGracePeriodSeconds: 30
containers:
- name: podinfod
image: quay.io/stefanprodan/podinfo:0.6.0
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- sleep 3
command:
- ./podinfo
- -port=9898
- -logLevel=debug
ports:
- name: http
containerPort: 9898
protocol: TCP
readinessProbe:
httpGet:
path: /readyz
port: 9898
initialDelaySeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: 9898
resources:
requests:
memory: "32Mi"
cpu: "10m"
env:
- name: color
value: "blue"
- name: message
value: "Greetings from podinfo blue"
- name: backendURL
value: "http://podinfo-backend:9898/backend"

View File

@@ -0,0 +1,14 @@
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: podinfo
spec:
host: podinfo
subsets:
- name: blue
labels:
color: blue
- name: green
labels:
color: green

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: podinfo
labels:
app: podinfo
spec:
type: ClusterIP
ports:
- port: 9898
targetPort: http
protocol: TCP
name: http
selector:
app: podinfo

View File

@@ -0,0 +1,82 @@
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: podinfo
spec:
hosts:
- "podinfo.istio.weavedx.com"
gateways:
- public-gateway.istio-system.svc.cluster.local
http:
# Opera: forward 100% of the traffic to green
- match:
- headers:
user-agent:
regex: ".*OPR.*"
route:
- destination:
host: podinfo
subset: green
timeout: 30s
# Chrome: 50/50 load balancing between blue and green
- match:
- headers:
user-agent:
regex: ".*Chrome.*"
route:
- destination:
host: podinfo
subset: blue
weight: 50
- destination:
host: podinfo
subset: green
weight: 50
timeout: 30s
# Safari: 70/30 load balancing between blue and green
- match:
- headers:
user-agent:
regex: "^(?!.*Chrome).*Safari.*"
route:
- destination:
host: podinfo
subset: blue
weight: 100
- destination:
host: podinfo
subset: green
weight: 0
timeout: 30s
# Route based on color header
- match:
- headers:
x-color:
exact: "blue"
route:
- destination:
host: podinfo
subset: blue
timeout: 30s
retries:
attempts: 3
perTryTimeout: 3s
- match:
- headers:
x-color:
exact: "green"
route:
- destination:
host: podinfo
subset: green
timeout: 30s
retries:
attempts: 3
perTryTimeout: 3s
# Any other browser: forward 100% of the traffic to blue
- route:
- destination:
host: podinfo
subset: blue
timeout: 35s

View File

@@ -0,0 +1,68 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: podinfo-green
labels:
app: podinfo
color: green
spec:
replicas: 3
minReadySeconds: 15
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
app: podinfo
color: green
template:
metadata:
labels:
app: podinfo
color: green
annotations:
prometheus.io/scrape: 'true'
spec:
terminationGracePeriodSeconds: 30
containers:
- name: podinfod
image: quay.io/stefanprodan/podinfo:0.6.0
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- sleep 4
command:
- ./podinfo
- -port=9898
- -logLevel=debug
ports:
- name: http
containerPort: 9898
protocol: TCP
readinessProbe:
httpGet:
path: /readyz
port: 9898
initialDelaySeconds: 1
periodSeconds: 2
failureThreshold: 1
livenessProbe:
httpGet:
path: /healthz
port: 9898
resources:
requests:
memory: "32Mi"
cpu: "10m"
env:
- name: color
value: "green"
- name: message
value: "Greetings from podinfo green"
- name: backendURL
value: "http://podinfo-backend:9898/backend"

View File

@@ -0,0 +1,15 @@
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: httpbin
spec:
hosts:
- httpbin.org
ports:
- number: 80
name: http
protocol: HTTP
- number: 443
name: https
protocol: HTTPS
resolution: DNS

View File

@@ -0,0 +1,62 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: podinfo-store
labels:
app: podinfo-store
version: "0.6"
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
app: podinfo-store
version: "0.6"
template:
metadata:
labels:
app: podinfo-store
version: "0.6"
annotations:
prometheus.io/scrape: 'true'
spec:
terminationGracePeriodSeconds: 30
containers:
- name: podinfod
image: quay.io/stefanprodan/podinfo:0.6.0
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- sleep 3
command:
- ./podinfo
- -port=9898
- -logLevel=debug
ports:
- name: http
containerPort: 9898
protocol: TCP
readinessProbe:
httpGet:
path: /readyz
port: 9898
livenessProbe:
httpGet:
path: /healthz
port: 9898
resources:
requests:
memory: "32Mi"
cpu: "10m"
env:
- name: color
value: "yellow"
- name: message
value: "Greetings from store yellow"

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: podinfo-store
labels:
app: podinfo-store
spec:
type: ClusterIP
ports:
- port: 9898
targetPort: http
protocol: TCP
name: http
selector:
app: podinfo-store

View File

@@ -0,0 +1,27 @@
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: podinfo-store
spec:
hosts:
- podinfo-store
http:
- match:
- sourceLabels:
color: orange
route:
- destination:
host: podinfo-store
timeout: 15s
fault:
delay:
percent: 50
fixedDelay: 500ms
abort:
percent: 50
httpStatus: 500
- route:
- destination:
host: podinfo-store
timeout: 15s

View File

@@ -0,0 +1,8 @@
apiVersion: v1
data:
basic_auth_password: ODM4NzIwYTUxMjgxNDlkMzJmMTIxYTViMWQ4N2FjMzUwNzAxZThmZQ==
basic_auth_test: YWRtaW4=
kind: Secret
metadata:
name: basic-auth
type: Opaque

View File

@@ -6,7 +6,13 @@ metadata:
labels:
app: podinfo
spec:
replicas: 1
replicas: 3
minReadySeconds: 15
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
selector:
matchLabels:
app: podinfo
@@ -17,13 +23,21 @@ spec:
annotations:
prometheus.io/scrape: 'true'
spec:
terminationGracePeriodSeconds: 30
containers:
- name: podinfod
image: quay.io/stefanprodan/podinfo:0.5.0-alpha6
image: quay.io/stefanprodan/podinfo:0.6.0
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- sleep 3
command:
- ./podinfo
- -port=9898
- -debug=true
- -logLevel=debug
ports:
- name: http
containerPort: 9898
@@ -33,7 +47,7 @@ spec:
path: /readyz
port: 9898
initialDelaySeconds: 1
periodSeconds: 5
periodSeconds: 2
failureThreshold: 1
livenessProbe:
httpGet:
@@ -47,6 +61,12 @@ spec:
memory: "32Mi"
cpu: "10m"
env:
- name: color
value: "blue"
- name: message
value: "Greetings from podinfo blue"
- name: backendURL
value: "http://podinfo-backend:9898/echo"
- name: configPath
value: "/var/secrets"
volumeMounts:

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: podinfo
labels:
app: podinfo
spec:
type: LoadBalancer
ports:
- port: 9898
targetPort: http
protocol: TCP
name: http
selector:
app: podinfo

View File

@@ -3,7 +3,7 @@ entries:
ambassador:
- apiVersion: v1
appVersion: 0.29.0
created: 2018-08-04T02:00:00.9435927+03:00
created: 2018-08-17T18:44:47.405027259+03:00
description: A Helm chart for Datawire Ambassador
digest: a30c8cb38e696b09fda8269ad8465ce6fec6100cfc108ca85ecbc85913ca5c7f
engine: gotpl
@@ -19,17 +19,27 @@ entries:
grafana:
- apiVersion: v1
appVersion: "1.0"
created: 2018-08-04T02:00:00.944250297+03:00
created: 2018-08-17T18:44:47.405800273+03:00
description: A Helm chart for Kubernetes
digest: abdcadc5cddcb7c015aa5bb64e59bfa246774ad9243b3eb3c2a814abb38f2776
name: grafana
urls:
- https://stefanprodan.github.io/k8s-podinfo/grafana-0.1.0.tgz
version: 0.1.0
loadtest:
- apiVersion: v1
appVersion: "1.0"
created: 2018-08-17T18:44:47.406037468+03:00
description: Hey load test Helm chart for Kubernetes
digest: fd3c3cd1eafa9d496250356aa52de1430f3467535fc2972eba5c5c392714eb1f
name: loadtest
urls:
- https://stefanprodan.github.io/k8s-podinfo/loadtest-0.1.0.tgz
version: 0.1.0
ngrok:
- apiVersion: v1
appVersion: "1.0"
created: 2018-08-04T02:00:00.944555634+03:00
created: 2018-08-17T18:44:47.406355266+03:00
description: A Ngrok Helm chart for Kubernetes
digest: 7bf5ed2ef63ccd5efb76bcd9a086b04816a162c51d6ab592bccf58c283acd2ea
name: ngrok
@@ -37,9 +47,41 @@ entries:
- https://stefanprodan.github.io/k8s-podinfo/ngrok-0.1.0.tgz
version: 0.1.0
podinfo:
- apiVersion: v1
appVersion: 0.6.0
created: 2018-08-17T18:44:47.410049875+03:00
description: Podinfo Helm chart for Kubernetes
digest: bd25a710eddb3985d3bd921a11022b5c68a04d37cf93a1a4aab17eeda35aa2f8
engine: gotpl
home: https://github.com/stefanprodan/k8s-podinfo
maintainers:
- email: stefanprodan@users.noreply.github.com
name: stefanprodan
name: podinfo
sources:
- https://github.com/stefanprodan/k8s-podinfo
urls:
- https://stefanprodan.github.io/k8s-podinfo/podinfo-0.2.2.tgz
version: 0.2.2
- apiVersion: v1
appVersion: 0.5.1
created: 2018-08-17T18:44:47.409648714+03:00
description: Podinfo Helm chart for Kubernetes
digest: 631ca3e2db5553541a50b625f538e6a1f2a103c13aa8148fdd38baf2519e5235
engine: gotpl
home: https://github.com/stefanprodan/k8s-podinfo
maintainers:
- email: stefanprodan@users.noreply.github.com
name: stefanprodan
name: podinfo
sources:
- https://github.com/stefanprodan/k8s-podinfo
urls:
- https://stefanprodan.github.io/k8s-podinfo/podinfo-0.2.1.tgz
version: 0.2.1
- apiVersion: v1
appVersion: 0.5.0
created: 2018-08-04T02:00:00.946091506+03:00
created: 2018-08-17T18:44:47.408857715+03:00
description: Podinfo Helm chart for Kubernetes
digest: dfe7cf44aef0d170549918b00966422a07e7611f9d0081fb34f5b5beb0641c00
engine: gotpl
@@ -55,7 +97,7 @@ entries:
version: 0.2.0
- apiVersion: v1
appVersion: 0.3.0
created: 2018-08-04T02:00:00.945649351+03:00
created: 2018-08-17T18:44:47.407706617+03:00
description: Podinfo Helm chart for Kubernetes
digest: 4865a2d8b269cf453935cda9661c2efb82c16411471f8c11221a6d03d9bb58b1
engine: gotpl
@@ -69,41 +111,21 @@ entries:
urls:
- https://stefanprodan.github.io/k8s-podinfo/podinfo-0.1.0.tgz
version: 0.1.0
weave-flux:
podinfo-istio:
- apiVersion: v1
appVersion: 1.3.0
created: 2018-08-04T02:00:00.947870112+03:00
description: Flux is a tool that automatically ensures that the state of a cluster
matches what is specified in version control
digest: 1f52e427bb1d728641405f5ad9c514e8861905c110c14db95516629d24443b7d
appVersion: 0.6.0
created: 2018-08-17T18:44:47.410653895+03:00
description: Podinfo Helm chart for Istio
digest: 79d9cbe4ba8b83ced977d895e56c1223d1fcd88a15f2df1981365c39cf6f4de7
engine: gotpl
home: https://weave.works
icon: https://landscape.cncf.io/logos/weave-flux.svg
home: https://github.com/stefanprodan/k8s-podinfo
maintainers:
- email: stefan@weave.works
- email: stefanprodan@users.noreply.github.com
name: stefanprodan
name: weave-flux
name: podinfo-istio
sources:
- https://github.com/weaveworks/flux
- https://github.com/stefanprodan/k8s-podinfo
urls:
- https://stefanprodan.github.io/k8s-podinfo/weave-flux-0.2.0.tgz
version: 0.2.0
- apiVersion: v1
appVersion: 1.2.5
created: 2018-08-04T02:00:00.947340919+03:00
description: Flux is a tool that automatically ensures that the state of a cluster
matches what is specified in version control
digest: 9e18fb8d175f4fac3b054905c7110d18b6d18f884011df9e9d010c66337da7ec
engine: gotpl
home: https://weave.works
icon: https://www.weave.works/assets/images/bltd108e8f850ae9e7c/weave-logo-512.png
maintainers:
- email: stefan@weave.works
name: Stefan Prodan
name: weave-flux
sources:
- https://github.com/weaveworks/flux
urls:
- https://stefanprodan.github.io/k8s-podinfo/weave-flux-0.1.0.tgz
- https://stefanprodan.github.io/k8s-podinfo/podinfo-istio-0.1.0.tgz
version: 0.1.0
generated: 2018-08-04T02:00:00.942959149+03:00
generated: 2018-08-17T18:44:47.404359545+03:00

BIN
docs/loadtest-0.1.0.tgz Normal file

Binary file not shown.

BIN
docs/podinfo-0.2.1.tgz Normal file

Binary file not shown.

BIN
docs/podinfo-0.2.2.tgz Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -94,13 +94,16 @@ func (w *Watcher) updateCache() error {
}
}
// clear cache
// remove deleted files from cache
w.Cache.Range(func(key interface{}, value interface{}) bool {
w.Cache.Delete(key)
_, ok := fileMap[key.(string)]
if !ok {
w.Cache.Delete(key)
}
return true
})
// load cache
// sync cache
for k, v := range fileMap {
w.Cache.Store(k, v)
}

178
pkg/server/api.go Normal file
View File

@@ -0,0 +1,178 @@
package server
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"time"
"github.com/rs/zerolog/log"
"github.com/stefanprodan/k8s-podinfo/pkg/version"
)
func (s *Server) apiInfo(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/info" && r.Method != http.MethodGet {
w.WriteHeader(http.StatusNotFound)
return
}
host, _ := os.Hostname()
color := os.Getenv("color")
if len(color) < 1 {
color = "blue"
}
msg := os.Getenv("message")
if len(msg) < 1 {
msg = fmt.Sprintf("Greetings from podinfo v%v", version.VERSION)
}
data := struct {
Message string `json:"message"`
Version string `json:"version"`
Revision string `json:"revision"`
Hostname string `json:"hostname"`
Color string `json:"color"`
}{
Message: msg,
Version: version.VERSION,
Revision: version.GITCOMMIT,
Hostname: host,
Color: color,
}
d, err := json.Marshal(data)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(http.StatusOK)
w.Write(d)
}
func (s *Server) apiEcho(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/echo" && r.Method != http.MethodPost {
w.WriteHeader(http.StatusNotFound)
return
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Error().Msgf("Reading the request body failed: %v", err)
jsonError(w, "invalid request body", http.StatusBadRequest)
return
}
backendURL := os.Getenv("backendURL")
if len(backendURL) > 0 {
backendReq, err := http.NewRequest("POST", backendURL, bytes.NewReader(body))
if err != nil {
log.Error().Err(err).Msgf("%v backend call failed", r.URL.Path)
jsonError(w, "backend call failed", http.StatusInternalServerError)
return
}
// forward headers
copyTracingHeaders(r, backendReq)
setVersionHeaders(backendReq)
// TODO: make the timeout configurable
ctx, cancel := context.WithTimeout(backendReq.Context(), 2*time.Minute)
defer cancel()
// call backend
resp, err := http.DefaultClient.Do(backendReq.WithContext(ctx))
if err != nil {
log.Error().Err(err).Msgf("backend call to %s failed", backendURL)
jsonError(w, "backend call failed", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
// copy error status from backend and exit
if resp.StatusCode >= 400 {
w.WriteHeader(resp.StatusCode)
return
}
// forward the received body
rbody, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error().Err(err).Msgf("%v reading the backend request body failed", r.URL.Path)
jsonError(w, "backend call failed", http.StatusInternalServerError)
return
}
// set logLevel=info when load testing
log.Debug().Msgf("Payload received %v from backend: %s", r.URL.Path, string(rbody))
setResponseHeaders(w)
w.Write(rbody)
} else {
setResponseHeaders(w)
w.Write(body)
}
}
func copyTracingHeaders(from *http.Request, to *http.Request) {
headers := []string{
"x-request-id",
"x-b3-traceid",
"x-b3-spanid",
"x-b3-parentspanid",
"x-b3-sampled",
"x-b3-flags",
"x-ot-span-context",
}
for i := range headers {
headerValue := from.Header.Get(headers[i])
if len(headerValue) > 0 {
to.Header.Set(headers[i], headerValue)
}
}
}
func setVersionHeaders(r *http.Request) {
r.Header.Set("X-API-Version", version.VERSION)
r.Header.Set("X-API-Revision", version.GITCOMMIT)
}
func setResponseHeaders(w http.ResponseWriter) {
color := os.Getenv("color")
if len(color) < 1 {
color = "blue"
}
w.Header().Set("X-Color", color)
w.WriteHeader(http.StatusAccepted)
}
func jsonError(w http.ResponseWriter, error string, code int) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(code)
data := struct {
Code int `json:"code"`
Message string `json:"message"`
}{
Code: code,
Message: error,
}
body, err := json.Marshal(data)
if err != nil {
log.Debug().Err(err).Msg("jsonError marshal failed")
} else {
w.Write(body)
}
}

View File

@@ -5,10 +5,12 @@ import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"html/template"
"io/ioutil"
"net/http"
"os"
"path"
"strings"
"sync/atomic"
"time"
@@ -23,23 +25,50 @@ func (s *Server) index(w http.ResponseWriter, r *http.Request) {
return
}
resp, err := makeResponse()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
log.Debug().Msgf("Request %s received from %s on %s", r.Header.Get("x-request-id"), r.RemoteAddr, r.RequestURI)
if strings.Contains(r.UserAgent(), "Mozilla") {
uiPath := os.Getenv("uiPath")
if len(uiPath) < 1 {
uiPath = "ui"
}
tmpl, err := template.New("vue.html").ParseFiles(path.Join(uiPath, "vue.html"))
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(path.Join(uiPath, "vue.html") + err.Error()))
return
}
host, _ := os.Hostname()
data := struct {
Title string
}{
Title: host,
}
if err := tmpl.Execute(w, data); err != nil {
http.Error(w, path.Join(uiPath, "vue.html")+err.Error(), http.StatusInternalServerError)
}
} else {
resp, err := makeResponse()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
d, err := yaml.Marshal(resp)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(http.StatusOK)
w.Write(d)
}
d, err := yaml.Marshal(resp)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(http.StatusOK)
w.Write(d)
}
func (s *Server) echo(w http.ResponseWriter, r *http.Request) {
@@ -86,9 +115,21 @@ func (s *Server) backend(w http.ResponseWriter, r *http.Request) {
return
}
backendURL := os.Getenv("backend_url")
backendURL := os.Getenv("backendURL")
if len(backendURL) > 0 {
resp, err := http.Post(backendURL, r.Header.Get("Content-type"), bytes.NewReader(body))
backendReq, err := http.NewRequest("POST", backendURL, bytes.NewReader(body))
if err != nil {
log.Error().Msgf("Backend call failed: %v", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
// forward tracing headers
copyTracingHeaders(r, backendReq)
setVersionHeaders(backendReq)
resp, err := http.DefaultClient.Do(backendReq)
if err != nil {
log.Error().Msgf("Backend call failed: %v", err)
w.WriteHeader(http.StatusInternalServerError)
@@ -96,6 +137,10 @@ func (s *Server) backend(w http.ResponseWriter, r *http.Request) {
return
}
defer resp.Body.Close()
if resp.StatusCode >= 500 {
w.WriteHeader(resp.StatusCode)
return
}
rbody, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error().Msgf("Reading the backend request body failed: %v", err)
@@ -104,11 +149,12 @@ func (s *Server) backend(w http.ResponseWriter, r *http.Request) {
return
}
log.Debug().Msgf("Payload received from backend: %s", string(rbody))
w.WriteHeader(http.StatusAccepted)
setResponseHeaders(w)
w.Write(rbody)
} else {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Backend not specified, set backend_url env var"))
w.Write([]byte("Backend not specified, set backendURL env var"))
}
default:
w.WriteHeader(http.StatusNotAcceptable)

View File

@@ -3,6 +3,7 @@ package server
import (
"bufio"
"crypto/tls"
"encoding/json"
"io/ioutil"
"net/http"
"os"
@@ -99,7 +100,7 @@ func runtimeToMap() map[string]string {
"max_procs": strconv.FormatInt(int64(runtime.GOMAXPROCS(0)), 10),
"num_goroutine": strconv.FormatInt(int64(runtime.NumGoroutine()), 10),
"num_cpu": strconv.FormatInt(int64(runtime.NumCPU()), 10),
"external_ip": findIp("http://api.ipify.org"),
"external_ip": findIp("http://httpbin.org/ip"),
}
return info
}
@@ -117,7 +118,7 @@ func findIp(url string) string {
req, _ := http.NewRequest("GET", url, nil)
res, err := client.Do(req)
if err != nil {
log.Error().Err(errors.Wrapf(err, "cannot connect to %s", url)).Msg("ipify timeout")
log.Error().Err(errors.Wrapf(err, "cannot connect to %s", url)).Msg("timeout")
return ip
}
@@ -129,7 +130,12 @@ func findIp(url string) string {
if err != nil {
return ip
}
return string(contents)
jsonMap := make(map[string]string)
err = json.Unmarshal([]byte(contents), &jsonMap)
if err != nil {
return ip
}
return jsonMap["origin"]
}
}

View File

@@ -57,6 +57,10 @@ func NewServer(options ...func(*Server)) *Server {
s.mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
s.mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
// API
s.mux.HandleFunc("/api/info", s.apiInfo)
s.mux.HandleFunc("/api/echo", s.apiEcho)
return s
}
@@ -115,6 +119,12 @@ func ListenAndServe(port string, timeout time.Duration, stopCh <-chan struct{})
log.Info().Msgf("Shutting down HTTP server with timeout: %v", timeout)
// wait for Kubernetes readiness probe
// to remove this instance from the load balancer
// the readiness check interval must lower than the timeout
time.Sleep(timeout)
// attempt graceful shutdown
if err := srv.Shutdown(ctx); err != nil {
log.Error().Err(err).Msg("HTTP server graceful shutdown failed")
} else {

View File

@@ -1,4 +1,4 @@
package version
var VERSION = "0.5.0"
var VERSION = "0.6.1"
var GITCOMMIT = "unknown"

194
ui/vue.html Normal file
View File

@@ -0,0 +1,194 @@
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}}</title>
<meta charset="utf-8">
<!-- <meta http-equiv="refresh" content="5"> -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="shortcut icon" type="image/png" href="https://kubernetes.io/images/favicon.png">
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet">
<link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet">
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<v-app dark>
<v-content>
<section>
<v-parallax src="#" height="600" :class="info.color">
<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">
<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
class="red darken-2 mt-5"
dark
large
@click="postBackend()"
>
<v-badge left color="red">
<span slot="badge">${ pings }</span>
<v-icon left dark>touch_app</v-icon>
</v-badge>
Ping
</v-btn>
</v-layout>
</v-parallax>
</section>
<section>
<v-layout
column
wrap
class="my-5"
align-center
>
<v-flex xs12 sm4 class="my-3">
<div class="text-xs-center">
<h2 class="headline">The best way to start developing</h2>
<span class="subheading">
stateless microservices with Go for Kubernetes
</span>
</div>
</v-flex>
<v-flex xs12>
<v-container grid-list-xl>
<v-layout row wrap align-center>
<v-flex xs12 md4>
<v-card class="elevation-0 transparent">
<v-card-text class="text-xs-center">
<v-icon x-large class="blue--text text--lighten-2">cloud</v-icon>
</v-card-text>
<v-card-title primary-title class="layout justify-center">
<div class="headline text-xs-center">Cloud Native</div>
</v-card-title>
<v-card-text>
Distributed as a Helm chart. Builtin Kubernetes health checks (readiness and liveness).
Graceful shutdown on interrupt signals.
</v-card-text>
</v-card>
</v-flex>
<v-flex xs12 md4>
<v-card class="elevation-0 transparent">
<v-card-text class="text-xs-center">
<v-icon x-large class="blue--text text--lighten-2">graphic_eq</v-icon>
</v-card-text>
<v-card-title primary-title class="layout justify-center">
<div class="headline">Observability</div>
</v-card-title>
<v-card-text>
Instrumentation with Prometheus (RED method).
Structured logging with zerolog and Fluentd.
Tracing with Jaeger and Istio.
</v-card-text>
</v-card>
</v-flex>
<v-flex xs12 md4>
<v-card class="elevation-0 transparent">
<v-card-text class="text-xs-center">
<v-icon x-large class="blue--text text--lighten-2">flash_on</v-icon>
</v-card-text>
<v-card-title primary-title class="layout justify-center">
<div class="headline text-xs-center">Release automation</div>
</v-card-title>
<v-card-text>
Multi-platform Docker image AMD64 and ARMv7.
CI/CD powered by: TravisCI CircleCI Quay.io Google Cloud Container Builder Skaffold Weave Flux.
</v-card-text>
</v-card>
</v-flex>
</v-layout>
</v-container>
</v-flex>
</v-layout>
</section>
<v-footer class="blue darken-2">
<v-layout row wrap align-center>
<v-flex xs12>
<div class="white--text ml-3">
Powered
by <a class="white--text" href="https://github.com/stefanprodan/k8s-podinfo" target="_blank">podinfo</a>
version ${ info.version } revision ${ info.revision }
</div>
</v-flex>
</v-layout>
</v-footer>
</v-content>
</v-app>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/vuetify/dist/vuetify.min.js"></script>
<script>
new Vue({
delimiters: ['${', '}'],
el: '#app',
data: function() {
return {
info: {},
timer: '',
color: '',
pings: 0
}
},
created: function() {
this.getInfo();
this.timer = setInterval(this.getInfo, 3000)
},
methods: {
getInfo: function() {
var xhr = new XMLHttpRequest()
var self = this
xhr.open('GET', "/api/info")
xhr.onload = function () {
data = JSON.parse(xhr.responseText)
// reload page when the version changes
if (self.info.version) {
if (self.info.version != data.version) {
console.log("New version", data.version)
window.location.reload()
}
}
self.info = data
document.title = data.hostname
}
xhr.onerror = function() {
console.log(xhr.responseText || 'Network request failed')
}
xhr.send()
},
postBackend: function() {
var self = this
fetch("/api/echo", {
method: 'post',
headers: {
"Content-type": "application/json; charset=UTF-8",
"X-APP": Math.random(),
},
body: JSON.stringify({random: Math.random()})
})
.then(function(response) {
self.pings++
self.color = response.headers.get('X-Color')
return response.json()
})
.then(function(json) {
console.log('Request successful', json);
})
.catch(function (error) {
console.log('Request failed', error);
});
}
}
})
</script>
</body>
</html>