first commit

master
ermisw 2023-11-13 15:25:46 +01:00
commit 4a59d4bd2d
39 changed files with 1251 additions and 0 deletions

BIN
argo/carts.tgz Normal file

Binary file not shown.

4
argo/carts/Chart.yaml Normal file
View File

@ -0,0 +1,4 @@
apiVersion: v1
description: A Helm chart for service carts
name: carts
version: 0.1.0

View File

@ -0,0 +1,19 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "carts.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 "carts.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "carts.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 "carts.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:80
{{- end }}

View File

@ -0,0 +1,32 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "carts.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.
*/}}
{{- define "carts.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 -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "carts.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

View File

@ -0,0 +1,47 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: carts-db-mongodata
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Mi
status: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: carts-db
labels:
app: carts-db
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: carts-db
template:
metadata:
labels:
app: carts-db
deployment: carts-db
spec:
containers:
- name: carts-db
image: mongo
imagePullPolicy: IfNotPresent
ports:
- containerPort: 27017
resources: {}
volumeMounts:
- mountPath: /data/db
name: carts-db-mongodata
restartPolicy: Always
volumes:
- name: carts-db-mongodata
persistentVolumeClaim:
claimName: carts-db-mongodata

View File

@ -0,0 +1,12 @@
---
apiVersion: v1
kind: Service
metadata:
name: carts-db
spec:
ports:
- name: carts-db
port: 27017
targetPort: 27017
selector:
app: carts-db

View File

@ -0,0 +1,28 @@
apiVersion: batch/v1
kind: Job
metadata:
generateName: app-keptn-notification-
annotations:
argocd.argoproj.io/hook: Sync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
template:
spec:
containers:
- name: keptn-notification
image: agrimmer/alpine-curl-uuid-kubectl:latest
command: ["/bin/sh","-c"]
args: ['while [[ $(kubectl get rollout {{ .Values.keptn.service }}-{{ .Values.keptn.stage }} -n {{ .Values.keptn.project }}-{{ .Values.keptn.stage }} -o "jsonpath={..status.conditions[?(@.type==\"Progressing\")].reason}") == "ReplicaSetUpdated" ]]; do echo "waiting for rollout" && sleep 1; done; UUID=$(uuidgen); now=$(TZ=UTC date "+%FT%T.00Z"); curl -X POST -H "Content-Type: application/cloudevents+json" -H "x-token: ${KEPTN_API_TOKEN}" --insecure -d "{\"contenttype\": \"application/json\", \"data\": { \"project\": \"{{ .Values.keptn.project }}\", \"service\": \"{{ .Values.keptn.service }}\", \"stage\": \"{{ .Values.keptn.stage }}\", \"deploymentURILocal\": \"http://{{ .Values.keptn.service }}-canary.{{ .Values.keptn.project }}-{{ .Values.keptn.stage }}\", \"deploymentstrategy\": \"blue_green_service\", \"teststrategy\": \"performance\"}, \"id\": \"${UUID}\", \"source\": \"argo\", \"specversion\": \"0.2\", \"time\": \"${now}\", \"type\": \"sh.keptn.events.deployment-finished\", \"shkeptncontext\": \"${UUID}\"}" ${KEPTN_API_URL}/v1/event']
env:
- name: KEPTN_API_URL
valueFrom:
secretKeyRef:
name: argo
key: KEPTN_API_URL
- name: KEPTN_API_TOKEN
valueFrom:
secretKeyRef:
name: argo
key: KEPTN_API_TOKEN
restartPolicy: Never
backoffLimit: 2

View File

@ -0,0 +1,75 @@
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: {{ template "carts.fullname" . }}
labels:
app: {{ template "carts.name" . }}
chart: {{ template "carts.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
revisionHistoryLimit: 3
selector:
matchLabels:
app: {{ template "carts.name" . }}
release: {{ .Release.Name }}
strategy:
blueGreen:
autoPromotionEnabled: false
activeService: {{ template "carts.name" . }}-primary
previewService: {{ template "carts.name" . }}-canary
template:
metadata:
labels:
app: {{ template "carts.name" . }}
release: {{ .Release.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 8080
protocol: TCP
env:
- name: DT_CUSTOM_PROP
value: "keptn_project={{ .Values.keptn.project }} keptn_service={{ .Values.keptn.service }} keptn_stage={{ .Values.keptn.stage }}"
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: "metadata.name"
- name: DEPLOYMENT_NAME
valueFrom:
fieldRef:
fieldPath: "metadata.labels['deployment']"
- name: CONTAINER_IMAGE
value: "{{ .Values.image }}"
- name: KEPTN_PROJECT
value: "{{ .Values.keptn.project }}"
- name: KEPTN_STAGE
value: "{{ .Values.keptn.stage }}"
- name: KEPTN_SERVICE
value: "{{ .Values.keptn.service }}"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 15
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 15
resources:
limits:
cpu: 1000m
memory: 2048Mi
requests:
cpu: 500m
memory: 1024Mi

View File

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

28
argo/carts/values.yaml Normal file
View File

@ -0,0 +1,28 @@
replicaCount: 1
image:
repository: docker.io/keptnexamples/carts
tag: 0.13.1
pullPolicy: Always
service:
type: LoadBalancer
port: 80
ingress:
enabled: false
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
path: /
hosts:
- chart-example.local
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
keptn:
project: sockshop
stage: production
service: carts

BIN
carts-db.tgz Normal file

Binary file not shown.

4
carts-db/Chart.yaml Normal file
View File

@ -0,0 +1,4 @@
apiVersion: v1
description: A Helm chart for service carts-db
name: carts-db
version: 0.1.0

View File

@ -0,0 +1,53 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: carts-db-mongodata
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
status: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: carts-db
labels:
app: carts-db
spec:
replicas: {{ .Values.replicaCount }}
strategy:
type: Recreate
selector:
matchLabels:
app: carts-db
template:
metadata:
labels:
app: carts-db
deployment: carts-db
app.kubernetes.io/name: {{ .Values.keptn.service }}
app.kubernetes.io/instance: "{{ .Values.keptn.service }}-{{ .Values.keptn.deployment }}"
app.kubernetes.io/component: database
app.kubernetes.io/part-of: "{{ .Values.keptn.project }}"
app.kubernetes.io/managed-by: Keptn
app.kubernetes.io/version: {{ (split ":" .Values.image)._1 | default "latest" }}
spec:
containers:
- name: carts-db
image: {{ .Values.image }}
imagePullPolicy: IfNotPresent
ports:
- containerPort: 27017
resources: {}
volumeMounts:
- mountPath: /data/db
name: carts-db-mongodata
restartPolicy: Always
volumes:
- name: carts-db-mongodata
persistentVolumeClaim:
claimName: carts-db-mongodata

View File

@ -0,0 +1,12 @@
---
apiVersion: v1
kind: Service
metadata:
name: carts-db
spec:
ports:
- name: {{ .Values.service.name }}
port: {{ .Values.service.externalPort }}
targetPort: {{ .Values.service.internalPort }}
selector:
app: carts-db

6
carts-db/values.yaml Normal file
View File

@ -0,0 +1,6 @@
image: mongo
service:
externalPort: 27017
internalPort: 27017
name: carts-db
replicaCount: 1

BIN
carts.tgz Normal file

Binary file not shown.

4
carts/Chart.yaml Normal file
View File

@ -0,0 +1,4 @@
apiVersion: v1
description: A Helm chart for service carts
name: carts
version: 0.1.0

View File

@ -0,0 +1,77 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: carts
spec:
replicas: {{ .Values.replicaCount }}
strategy:
rollingUpdate:
maxUnavailable: 0
type: RollingUpdate
selector:
matchLabels:
app: carts
template:
metadata:
labels:
app: carts
app.kubernetes.io/name: {{ .Values.keptn.service }}
app.kubernetes.io/instance: "{{ .Values.keptn.service }}-{{ .Values.keptn.deployment }}"
app.kubernetes.io/component: api
app.kubernetes.io/part-of: "{{ .Values.keptn.project }}"
app.kubernetes.io/managed-by: Keptn
app.kubernetes.io/version: {{ (split ":" .Values.image)._1 | default "latest" }}
spec:
containers:
- name: carts
image: "{{ .Values.image }}"
imagePullPolicy: IfNotPresent
ports:
- name: http
protocol: TCP
containerPort: 8080
env:
- name: DT_CUSTOM_PROP
value: "version={{ .Chart.Version }} revision={{ .Release.Revision }} releasename={{ .Release.Name }} keptn_project={{ .Values.keptn.project }} keptn_service={{ .Values.keptn.service }} keptn_stage={{ .Values.keptn.stage }} keptn_deployment={{ .Values.keptn.deployment }}"
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: "metadata.name"
- name: DEPLOYMENT_NAME
valueFrom:
fieldRef:
fieldPath: "metadata.labels['deployment']"
- name: CONTAINER_IMAGE
value: "{{ .Values.image }}"
- name: KEPTN_PROJECT
value: "{{ .Chart.Name }}"
- name: KEPTN_STAGE
valueFrom:
fieldRef:
fieldPath: "metadata.namespace"
- name: KEPTN_SERVICE
value: "carts"
- name: UNLEASH_SERVER_URL
value: "http://unleash.unleash-dev/api"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 15
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 15
resources:
limits:
cpu: 1000m
memory: 2048Mi
requests:
cpu: 500m
memory: 1024Mi

View File

@ -0,0 +1,14 @@
---
apiVersion: v1
kind: Service
metadata:
name: carts
spec:
type: ClusterIP
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
selector:
app: carts

2
carts/values.yaml Normal file
View File

@ -0,0 +1,2 @@
image: docker.io/keptnexamples/carts:0.13.1
replicaCount: 1

View File

@ -0,0 +1,2 @@
spec_version: '0.1.0'
dtCreds: dynatrace

177
jmeter/basiccheck.jmx Normal file
View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.1.1 r1855137">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Testing Sample NodeJs" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="SERVER_URL" elementType="Argument">
<stringProp name="Argument.name">SERVER_URL</stringProp>
<stringProp name="Argument.value">carts.sockshop-staging.public.demo.keptn.sh</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="CHECK_PATH" elementType="Argument">
<stringProp name="Argument.name">CHECK_PATH</stringProp>
<stringProp name="Argument.value">/</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="DT_LTN" elementType="Argument">
<stringProp name="Argument.name">DT_LTN</stringProp>
<stringProp name="Argument.value">Default</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="DefaultThinkTime" elementType="Argument">
<stringProp name="Argument.name">DefaultThinkTime</stringProp>
<stringProp name="Argument.value">250</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="SERVER_PORT" elementType="Argument">
<stringProp name="Argument.name">SERVER_PORT</stringProp>
<stringProp name="Argument.value">80</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="PROTOCOL" elementType="Argument">
<stringProp name="Argument.name">PROTOCOL</stringProp>
<stringProp name="Argument.value">http</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="VUCount" elementType="Argument">
<stringProp name="Argument.name">VUCount</stringProp>
<stringProp name="Argument.value">1</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="LoopCount" elementType="Argument">
<stringProp name="Argument.name">LoopCount</stringProp>
<stringProp name="Argument.value">1</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="BasicThreadGroup" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">${__P(LoopCount,${VUCount})}</stringProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">${__P(VUCount,${VUCount})}</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<longProp name="ThreadGroup.start_time">1444323045000</longProp>
<longProp name="ThreadGroup.end_time">1444323045000</longProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
</ThreadGroup>
<hashTree>
<CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true">
<collectionProp name="CookieManager.cookies"/>
<boolProp name="CookieManager.clearEachIteration">false</boolProp>
</CookieManager>
<hashTree/>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
<collectionProp name="HeaderManager.headers"/>
</HeaderManager>
<hashTree/>
<BeanShellPreProcessor guiclass="TestBeanGUI" testclass="BeanShellPreProcessor" testname="Set Dynatrace Headers" enabled="true">
<boolProp name="resetInterpreter">true</boolProp>
<stringProp name="parameters"></stringProp>
<stringProp name="filename"></stringProp>
<stringProp name="script">import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.protocol.http.control.HeaderManager;
import java.io;
import java.util;
// -------------------------------------------------------------------------------------
// Generate the x-dynatrace-test header based on this best practic
// -&gt; https://www.dynatrace.com/support/help/integrations/test-automation-frameworks/how-do-i-integrate-dynatrace-into-my-load-testing-process/
// -------------------------------------------------------------------------------------
String LTN=JMeterUtils.getProperty(&quot;DT_LTN&quot;);
if((LTN == null) || (LTN.length() == 0)) {
if(vars != null) {
LTN = vars.get(&quot;DT_LTN&quot;);
}
}
if(LTN == null) LTN = &quot;NoTestName&quot;;
String LSN = (bsh.args.length &gt; 0) ? bsh.args[0] : &quot;Test Scenario&quot;;
String TSN = sampler.getName();
String VU = ctx.getThreadGroup().getName() + ctx.getThreadNum();
String headerValue = &quot;LSN=&quot;+ LSN + &quot;;TSN=&quot; + TSN + &quot;;LTN=&quot; + LTN + &quot;;VU=&quot; + VU + &quot;;&quot;;
// -------------------------------------------
// Set header
// -------------------------------------------
HeaderManager hm = sampler.getHeaderManager();
hm.removeHeaderNamed(&quot;x-dynatrace-test&quot;);
hm.add(new org.apache.jmeter.protocol.http.control.Header(&quot;x-dynatrace-test&quot;, headerValue));</stringProp>
</BeanShellPreProcessor>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Basic Check" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">${__P(SERVER_URL,${SERVER_URL})}</stringProp>
<stringProp name="HTTPSampler.port">${__P(SERVER_PORT,${SERVER_PORT})}</stringProp>
<stringProp name="HTTPSampler.protocol">${__P(PROTOCOL,${PROTOCOL})}</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">${__P(CHECK_PATH,${CHECK_PATH})}</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
<collectionProp name="HeaderManager.headers"/>
</HeaderManager>
<hashTree/>
</hashTree>
<ConstantTimer guiclass="ConstantTimerGui" testclass="ConstantTimer" testname="Default Think Time" enabled="true">
<stringProp name="ConstantTimer.delay">{__P(ThinkTime,${DefaultThinkTime})}</stringProp>
</ConstantTimer>
<hashTree/>
</hashTree>
<ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>false</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<threadCounts>true</threadCounts>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
</hashTree>
</jmeterTestPlan>

191
jmeter/load.jmx Normal file
View File

@ -0,0 +1,191 @@
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.1.1 r1855137">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="SERVER_URL" elementType="Argument">
<stringProp name="Argument.name">SERVER_URL</stringProp>
<stringProp name="Argument.value">carts.sockshop-staging.public.demo.keptn.sh</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="DefaultThinkTime" elementType="Argument">
<stringProp name="Argument.name">DefaultThinkTime</stringProp>
<stringProp name="Argument.value">250</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="DT_LTN" elementType="Argument">
<stringProp name="Argument.name">DT_LTN</stringProp>
<stringProp name="Argument.value">Default</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="SERVER_PORT" elementType="Argument">
<stringProp name="Argument.name">SERVER_PORT</stringProp>
<stringProp name="Argument.value">80</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="PROTOCOL" elementType="Argument">
<stringProp name="Argument.name">PROTOCOL</stringProp>
<stringProp name="Argument.value">http</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="VUCount" elementType="Argument">
<stringProp name="Argument.name">VUCount</stringProp>
<stringProp name="Argument.value">1</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="LoopCount" elementType="Argument">
<stringProp name="Argument.name">LoopCount</stringProp>
<stringProp name="Argument.value">1</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">${__P(LoopCount,${LoopCount})}</stringProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">${__P(VUCount,${VUCount})}</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<longProp name="ThreadGroup.start_time">1536064517000</longProp>
<longProp name="ThreadGroup.end_time">1536064517000</longProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
</ThreadGroup>
<hashTree>
<CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true">
<collectionProp name="CookieManager.cookies"/>
<boolProp name="CookieManager.clearEachIteration">false</boolProp>
</CookieManager>
<hashTree/>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Cache-Control</stringProp>
<stringProp name="Header.value">no-cache</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">json</stringProp>
<stringProp name="Header.value">true</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
<BeanShellPreProcessor guiclass="TestBeanGUI" testclass="BeanShellPreProcessor" testname="Set Dynatrace Headers" enabled="true">
<stringProp name="filename"></stringProp>
<stringProp name="parameters"></stringProp>
<boolProp name="resetInterpreter">false</boolProp>
<stringProp name="script">import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.protocol.http.control.HeaderManager;
import java.io;
import java.util;
// -------------------------------------------------------------------------------------
// Generate the x-dynatrace-test header based on this best practic
// -&gt; https://www.dynatrace.com/support/help/integrations/test-automation-frameworks/how-do-i-integrate-dynatrace-into-my-load-testing-process/
// -------------------------------------------------------------------------------------
String LTN=JMeterUtils.getProperty(&quot;DT_LTN&quot;);
if((LTN == null) || (LTN.length() == 0)) {
if(vars != null) {
LTN = vars.get(&quot;DT_LTN&quot;);
}
}
if(LTN == null) LTN = &quot;NoTestName&quot;;
String LSN = (bsh.args.length &gt; 0) ? bsh.args[0] : &quot;Test Scenario&quot;;
String TSN = sampler.getName();
String VU = ctx.getThreadGroup().getName() + ctx.getThreadNum();
String headerValue = &quot;LSN=&quot;+ LSN + &quot;;TSN=&quot; + TSN + &quot;;LTN=&quot; + LTN + &quot;;VU=&quot; + VU + &quot;;&quot;;
// -------------------------------------------
// Set header
// -------------------------------------------
HeaderManager hm = sampler.getHeaderManager();
hm.removeHeaderNamed(&quot;x-dynatrace-test&quot;);
hm.add(new org.apache.jmeter.protocol.http.control.Header(&quot;x-dynatrace-test&quot;, headerValue));</stringProp>
</BeanShellPreProcessor>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Add Item to Cart" enabled="true">
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value"> {&#xd;
&quot;itemId&quot;:&quot;03fef6ac-1896-4ce8-bd69-b798f85c6e0b&quot;,&#xd;
&quot;unitPrice&quot;:&quot;99.99&quot;&#xd;
}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain">${__P(SERVER_URL,${SERVER_URL})}</stringProp>
<stringProp name="HTTPSampler.port">${__P(SERVER_PORT,${SERVER_PORT})}</stringProp>
<stringProp name="HTTPSampler.protocol">${__P(PROTOCOL,${PROTOCOL})}</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/carts/1/items</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.BROWSER_COMPATIBLE_MULTIPART">true</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<ConstantTimer guiclass="ConstantTimerGui" testclass="ConstantTimer" testname="Default Think Time" enabled="true">
<stringProp name="ConstantTimer.delay">{__P(ThinkTime,${DefaultThinkTime})}</stringProp>
</ConstantTimer>
<hashTree/>
</hashTree>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>false</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<threadCounts>true</threadCounts>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
</hashTree>
</jmeterTestPlan>

View File

@ -0,0 +1,7 @@
apiVersion: v1
data:
sli-provider: dynatrace
kind: ConfigMap
metadata:
name: lighthouse-config-sockshop
namespace: keptn

View File

@ -0,0 +1,7 @@
apiVersion: v1
data:
sli-provider: prometheus
kind: ConfigMap
metadata:
name: lighthouse-config-sockshop
namespace: keptn

View File

@ -0,0 +1,58 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: carts-db-mongodata
namespace: sockshop-hardening
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Mi
status: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: carts-db
namespace: sockshop-hardening
spec:
replicas: 1
selector:
matchLabels:
app: carts-db
strategy:
type: Recreate
template:
metadata:
labels:
app: carts-db
spec:
containers:
- image: mongo
name: carts-db
ports:
- containerPort: 27017
resources: {}
volumeMounts:
- mountPath: /data/db
name: carts-db-mongodata
restartPolicy: Always
volumes:
- name: carts-db-mongodata
persistentVolumeClaim:
claimName: carts-db-mongodata
---
apiVersion: v1
kind: Service
metadata:
name: carts-db
namespace: sockshop-hardening
spec:
ports:
- name: "27017"
port: 27017
targetPort: 27017
selector:
app: carts-db

View File

@ -0,0 +1,88 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: carts
namespace: sockshop-hardening
spec:
replicas: 1
selector:
matchLabels:
app: carts
template:
metadata:
labels:
app: carts
version: v1
deployment: carts
spec:
containers:
- name: carts
image: docker.io/keptnexamples/carts:0.13.1
env:
- name: JAVA_OPTS
value: -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=128m -XX:+UseG1GC -Djava.security.egd=file:/dev/urandom
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: "metadata.name"
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
fieldPath: "metadata.namespace"
- name: DEPLOYMENT_NAME
valueFrom:
fieldRef:
fieldPath: "metadata.labels['deployment']"
- name: CONTAINER_IMAGE
value: docker.io/keptnexamples/carts:0.13.1
- name: DT_CUSTOM_PROP
value: "keptn_stage=hardening keptn_project=sockshop keptn_service=carts"
resources:
limits:
cpu: 500m
memory: 1024Mi
requests:
cpu: 400m
memory: 768Mi
ports:
- containerPort: 8080
volumeMounts:
- mountPath: /tmp
name: tmp-volume
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 15
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 15
volumes:
- name: tmp-volume
emptyDir:
medium: Memory
nodeSelector:
beta.kubernetes.io/os: linux
---
apiVersion: v1
kind: Service
metadata:
name: carts
labels:
app: carts
namespace: sockshop-hardening
spec:
ports:
- name: http
port: 80
targetPort: 8080
selector:
app: carts
type: LoadBalancer

18
remediation.yaml Normal file
View File

@ -0,0 +1,18 @@
apiVersion: spec.keptn.sh/0.1.4
kind: Remediation
metadata:
name: carts-remediation
spec:
remediations:
- problemType: Response time degradation
actionsOnOpen:
- action: scaling
name: scaling
description: Scale up
value: "1"
- problemType: response_time_p90
actionsOnOpen:
- action: scaling
name: scaling
description: Scale up
value: "1"

View File

@ -0,0 +1,20 @@
apiVersion: spec.keptn.sh/0.1.4
kind: Remediation
metadata:
name: carts-remediation
spec:
remediations:
- problemType: Response time degradation
actionsOnOpen:
- action: toggle-feature
name: Toogle feature flag
description: Toogle feature flag EnableItemCache to ON
value:
EnableItemCache: "on"
- problemType: Failure rate increase
actionsOnOpen:
- action: toggle-feature
name: Toogle feature flag
description: Toogle feature flag EnablePromotion to OFF
value:
EnablePromotion: "off"

18
shipyard-argo.yaml Normal file
View File

@ -0,0 +1,18 @@
apiVersion: "spec.keptn.sh/0.2.2"
kind: "Shipyard"
metadata:
name: "shipyard-sockshop"
spec:
stages:
- name: "production"
sequences:
- name: "artifact-delivery"
tasks:
- name: "deployment"
properties:
deploymentstrategy: "blue_green_service"
- name: "test"
properties:
teststrategy: "performance"
- name: "evaluation"
- name: "release"

View File

@ -0,0 +1,7 @@
apiVersion: "spec.keptn.sh/0.2.2"
kind: "Shipyard"
metadata:
name: "shipyard-quality-gates"
spec:
stages:
- name: "hardening"

96
shipyard.yaml Normal file
View File

@ -0,0 +1,96 @@
apiVersion: "spec.keptn.sh/0.2.2"
kind: "Shipyard"
metadata:
name: "shipyard-sockshop"
spec:
stages:
- name: "dev"
sequences:
- name: "delivery"
tasks:
- name: "deployment"
properties:
deploymentstrategy: "direct"
- name: "test"
properties:
teststrategy: "functional"
- name: "evaluation"
- name: "release"
- name: "delivery-direct"
tasks:
- name: "deployment"
properties:
deploymentstrategy: "direct"
- name: "release"
- name: "staging"
sequences:
- name: "delivery"
triggeredOn:
- event: "dev.delivery.finished"
tasks:
- name: "deployment"
properties:
deploymentstrategy: "blue_green_service"
- name: "test"
properties:
teststrategy: "performance"
- name: "evaluation"
- name: "release"
- name: "rollback"
triggeredOn:
- event: "staging.delivery.finished"
selector:
match:
result: "fail"
tasks:
- name: "rollback"
- name: "delivery-direct"
triggeredOn:
- event: "dev.delivery-direct.finished"
tasks:
- name: "deployment"
properties:
deploymentstrategy: "direct"
- name: "release"
- name: "production"
sequences:
- name: "delivery"
triggeredOn:
- event: "staging.delivery.finished"
tasks:
- name: "deployment"
properties:
deploymentstrategy: "blue_green_service"
- name: "release"
- name: "rollback"
triggeredOn:
- event: "production.delivery.finished"
selector:
match:
result: "fail"
tasks:
- name: "rollback"
- name: "delivery-direct"
triggeredOn:
- event: "staging.delivery-direct.finished"
tasks:
- name: "deployment"
properties:
deploymentstrategy: "direct"
- name: "release"
- name: "remediation"
triggeredOn:
- event: "production.remediation.finished"
selector:
match:
evaluation.result: "fail"
tasks:
- name: "get-action"
- name: "action"
- name: "evaluation"
triggeredAfter: "15m"
properties:
timeframe: "15m"

View File

@ -0,0 +1,6 @@
---
spec_version: '1.0'
indicators:
response_time_p50: histogram_quantile(0.5, sum by(le) (rate(http_response_time_milliseconds_bucket{handler="ItemsController.addToCart",job="$SERVICE-$PROJECT-$STAGE-canary"}[$DURATION_SECONDS])))
response_time_p90: histogram_quantile(0.9, sum by(le) (rate(http_response_time_milliseconds_bucket{handler="ItemsController.addToCart",job="$SERVICE-$PROJECT-$STAGE-canary"}[$DURATION_SECONDS])))
response_time_p95: histogram_quantile(0.95, sum by(le) (rate(http_response_time_milliseconds_bucket{handler="ItemsController.addToCart",job="$SERVICE-$PROJECT-$STAGE-canary"}[$DURATION_SECONDS])))

View File

@ -0,0 +1,8 @@
---
spec_version: '1.0'
indicators:
throughput: "metricSelector=builtin:service.requestCount.total:merge(\"dt.entity.service\"):sum&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE)"
error_rate: "metricSelector=builtin:service.errors.total.count:merge(\"dt.entity.service\"):avg&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE)"
response_time_p50: "metricSelector=builtin:service.response.time:merge(\"dt.entity.service\"):percentile(50)&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE)"
response_time_p90: "metricSelector=builtin:service.response.time:merge(\"dt.entity.service\"):percentile(90)&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE)"
response_time_p95: "metricSelector=builtin:service.response.time:merge(\"dt.entity.service\"):percentile(95)&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE)"

View File

@ -0,0 +1,8 @@
---
spec_version: '1.0'
indicators:
throughput: "metricSelector=builtin:service.requestCount.total:merge(\"dt.entity.service\"):sum&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT)"
error_rate: "metricSelector=builtin:service.errors.total.count:merge(\"dt.entity.service\"):avg&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT)"
response_time_p50: "metricSelector=builtin:service.response.time:merge(\"dt.entity.service\"):percentile(50)&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT)"
response_time_p90: "metricSelector=builtin:service.response.time:merge(\"dt.entity.service\"):percentile(90)&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT)"
response_time_p95: "metricSelector=builtin:service.response.time:merge(\"dt.entity.service\"):percentile(95)&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT)"

View File

@ -0,0 +1,6 @@
---
spec_version: '1.0'
indicators:
response_time_p50: histogram_quantile(0.5, sum by(le) (rate(http_response_time_milliseconds_bucket{handler="ItemsController.addToCart",job="$SERVICE-$PROJECT-$STAGE-$DEPLOYMENT"}[$DURATION_SECONDS])))
response_time_p90: histogram_quantile(0.9, sum by(le) (rate(http_response_time_milliseconds_bucket{handler="ItemsController.addToCart",job="$SERVICE-$PROJECT-$STAGE-$DEPLOYMENT"}[$DURATION_SECONDS])))
response_time_p95: histogram_quantile(0.95, sum by(le) (rate(http_response_time_milliseconds_bucket{handler="ItemsController.addToCart",job="$SERVICE-$PROJECT-$STAGE-$DEPLOYMENT"}[$DURATION_SECONDS])))

23
slo-quality-gates.yaml Normal file
View File

@ -0,0 +1,23 @@
---
spec_version: "1.0"
comparison:
aggregate_function: "avg"
compare_with: "single_result"
include_result_with_score: "pass"
number_of_comparison_results: 1
filter:
objectives:
- sli: "response_time_p95"
displayName: "Response time P95"
key_sli: false
pass: # pass if (relative change <= 10% AND absolute value is < 600ms)
- criteria:
- "<=+10%" # relative values require a prefixed sign (plus or minus)
- "<600" # absolute values only require a logical operator
warning: # if the response time is below 800ms, the result should be a warning
- criteria:
- "<=800"
weight: 1
total_score:
pass: "90%"
warning: "75%"

View File

@ -0,0 +1,30 @@
---
spec_version: "1.0"
comparison:
aggregate_function: "avg"
compare_with: "single_result"
include_result_with_score: "pass"
number_of_comparison_results: 1
filter:
objectives:
- sli: "response_time_p90"
displayName: "Response time P90"
key_sli: false
pass: # pass if (relative change <= 10% AND absolute value is < 1000)
- criteria:
- "<=+10%" # relative values require a prefixed sign (plus or minus)
- "<1000" # absolute values only require a logical operator
warning: # if the response time is below 1200ms, the result should be a warning
- criteria:
- "<=1200"
weight: 1
- sli: "problem_open"
displayName: "Problem open"
key_sli: true
pass:
- criteria:
- "=0"
weight: 1
total_score:
pass: "90%"
warning: "40%"

View File

@ -0,0 +1,23 @@
---
spec_version: "1.0"
comparison:
aggregate_function: "avg"
compare_with: "single_result"
include_result_with_score: "pass"
number_of_comparison_results: 1
filter:
objectives:
- sli: "response_time_p90"
displayName: "Response time P90"
key_sli: false
pass: # pass if (relative change <= 10% AND absolute value is < 1000)
- criteria:
- "<=+10%" # relative values require a prefixed sign (plus or minus)
- "<1000" # absolute values only require a logical operator
warning: # if the response time is below 1200ms, the result should be a warning
- criteria:
- "<=1200"
weight: 1
total_score:
pass: "90%"
warning: "40%"