├── .gitignore
├── appBomp
├── Dockerfile
├── build.sh
├── main
├── go.mod
├── go.sum
├── README.md
└── goProject
│ └── main.go
├── smf-runner
├── babel.config.js
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── assets
│ │ ├── logo.png
│ │ └── service-mesh.png
│ ├── main.js
│ ├── compositions
│ │ ├── clusters.js
│ │ ├── deployment.js
│ │ └── bomb.js
│ ├── components
│ │ ├── Grafanas.vue
│ │ ├── BombStatus.vue
│ │ ├── LoadSpinner.vue
│ │ └── ApplyForm.vue
│ └── App.vue
├── .gitignore
├── README.md
├── server
│ ├── index.js
│ ├── api.js
│ └── kube.js
└── package.json
├── clusters.json
└── bookinfo.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 |
--------------------------------------------------------------------------------
/appBomp/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine
2 | COPY main /.
3 | CMD ["/main"]
--------------------------------------------------------------------------------
/appBomp/build.sh:
--------------------------------------------------------------------------------
1 | env GOOS=linux GOARCH=amd64 go build goProject/main.go
2 |
--------------------------------------------------------------------------------
/appBomp/main:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tikal-fuseday/service-mesh-fight/HEAD/appBomp/main
--------------------------------------------------------------------------------
/appBomp/go.mod:
--------------------------------------------------------------------------------
1 | module goProject
2 |
3 | go 1.13
4 |
5 | require github.com/gorilla/mux v1.7.4
6 |
--------------------------------------------------------------------------------
/smf-runner/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | };
6 |
--------------------------------------------------------------------------------
/smf-runner/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tikal-fuseday/service-mesh-fight/HEAD/smf-runner/public/favicon.ico
--------------------------------------------------------------------------------
/smf-runner/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tikal-fuseday/service-mesh-fight/HEAD/smf-runner/src/assets/logo.png
--------------------------------------------------------------------------------
/smf-runner/src/assets/service-mesh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tikal-fuseday/service-mesh-fight/HEAD/smf-runner/src/assets/service-mesh.png
--------------------------------------------------------------------------------
/appBomp/go.sum:
--------------------------------------------------------------------------------
1 | github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
2 | github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
3 |
--------------------------------------------------------------------------------
/appBomp/README.md:
--------------------------------------------------------------------------------
1 | http://bomb.ddns.net:30001/api/v1/bomb/1/status
2 |
3 | dockerhub royptikal/tikalkservicemeshfuze:init
4 |
5 | to build go application: cd appBomp folder, and then ./build.sh
6 |
7 | then docker build -t royptikal/tikalkservicemeshfuze:init (edited)
8 |
--------------------------------------------------------------------------------
/smf-runner/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import VueCompositionApi from "@vue/composition-api";
3 | Vue.use(VueCompositionApi);
4 |
5 | import App from "./App.vue";
6 |
7 | Vue.config.productionTip = false;
8 |
9 | new Vue({
10 | render: h => h(App)
11 | }).$mount("#app");
12 |
--------------------------------------------------------------------------------
/clusters.json:
--------------------------------------------------------------------------------
1 | {
2 | "ms-at-scale.tikal.io": {
3 | "bombServiceUrl": "http://bomb.ddns.net:30001"
4 | },
5 | "scaling-io.tikal.io": {
6 | "bombServiceUrl": "http://bomb.ddns.net:30001",
7 | "istio-grafana": "http://istio.ddns.net:3000",
8 | "linkerd-grafana": "http://linkerd.ddns.net:8084/grafana"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/smf-runner/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw?
22 |
--------------------------------------------------------------------------------
/smf-runner/README.md:
--------------------------------------------------------------------------------
1 | # smf-runner
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Lints and fixes files
19 | ```
20 | npm run lint
21 | ```
22 |
23 | ### Customize configuration
24 | See [Configuration Reference](https://cli.vuejs.org/config/).
25 |
--------------------------------------------------------------------------------
/smf-runner/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/smf-runner/src/compositions/clusters.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import {computed} from '@vue/composition-api';
3 | import axios from 'axios'
4 |
5 | const clustersState = Vue.observable({
6 | clusters: [],
7 | currentCluster: null
8 | });
9 |
10 | function fetchClusters() {
11 | return axios.get('/api/clusters').then(res => {
12 | clustersState.clusters = res.data;
13 | });
14 | }
15 |
16 |
17 | function fetchCluster(clusterName) {
18 | return axios.get('/api/clusters/' + clusterName).then(res => {
19 | clustersState.currentCluster = res.data;
20 | });
21 | }
22 |
23 | export function useClusters() {
24 | return {
25 | fetchClusters,
26 | fetchCluster,
27 | clusters: computed(() => clustersState.clusters),
28 | currentCluster: computed(() => clustersState.currentCluster),
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/smf-runner/server/index.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const express = require("express");
3 | const cors = require("cors");
4 | const bodyParser = require("body-parser");
5 | const morgan = require("morgan");
6 |
7 | const app = express();
8 | app.use(morgan("combined"));
9 | app.use(cors());
10 |
11 | // tell the app to parse HTTP body messages
12 | app.use(bodyParser.json());
13 |
14 | require('./api')(app);
15 |
16 | app.use(express.static(path.join(__dirname, '../dist')));
17 | app.use('*', express.static(path.join(__dirname, '../dist/index.html')));
18 |
19 | app.set("port", process.env.PORT || 3000);
20 | app.set("ip", process.env.IP || "127.0.0.1");
21 |
22 | // start the server
23 | app.listen(app.get("port"), app.get("ip"), () => {
24 | console.log(
25 | `Service-Mesh-Fight is running.
26 | \nPlease open in browser: http://${app.get("ip")}:${app.get("port")}`
27 | );
28 | });
29 |
--------------------------------------------------------------------------------
/smf-runner/src/components/Grafanas.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
26 |
37 |
--------------------------------------------------------------------------------
/smf-runner/src/compositions/deployment.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import {reactive} from '@vue/composition-api';
3 | import axios from 'axios'
4 | import {createBomb} from './bomb';
5 |
6 | const STORAGE_KEY = 'smf_deployment';
7 |
8 | const plugins = ["istio", "linkerd"];
9 | const deployment = Vue.observable(localStorage.getItem(STORAGE_KEY) ? JSON.parse(localStorage.getItem(STORAGE_KEY)) : {
10 | clusterName: "",
11 | namespace: 'istio',
12 | testUrl: '',
13 | deploymentFilePath: "",
14 | plugins: [...plugins],
15 | loading: false
16 | });
17 |
18 | function apply() {
19 | localStorage.setItem(STORAGE_KEY, JSON.stringify(deployment));
20 | deployment.loading = true;
21 | return axios.post('/api/apply', deployment)
22 | .then(() => {
23 | return createBomb(deployment.clusterName, deployment.testUrl)
24 | })
25 | .finally(() => {
26 | deployment.loading = false;
27 | })
28 | }
29 |
30 | export function useDeployment() {
31 | return {
32 | plugins,
33 | deployment: reactive(deployment),
34 | apply
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/smf-runner/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smf-runner",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint",
9 | "postinstall": "npm run build",
10 | "start": "node server"
11 | },
12 | "dependencies": {
13 | "@vue/composition-api": "^0.3.4",
14 | "axios": "^0.19.2",
15 | "body-parser": "^1.19.0",
16 | "core-js": "^3.6.4",
17 | "cors": "^2.8.5",
18 | "express": "^4.17.1",
19 | "morgan": "^1.9.1",
20 | "vue": "^2.6.11"
21 | },
22 | "devDependencies": {
23 | "@vue/cli-plugin-babel": "^4.2.0",
24 | "@vue/cli-plugin-eslint": "^4.2.0",
25 | "@vue/cli-service": "^4.2.0",
26 | "babel-eslint": "^10.0.3",
27 | "eslint": "^6.7.2",
28 | "eslint-plugin-vue": "^6.1.2",
29 | "vue-template-compiler": "^2.6.11"
30 | },
31 | "eslintConfig": {
32 | "root": true,
33 | "env": {
34 | "node": true
35 | },
36 | "extends": [
37 | "plugin:vue/essential",
38 | "eslint:recommended"
39 | ],
40 | "parserOptions": {
41 | "parser": "babel-eslint"
42 | },
43 | "rules": {}
44 | },
45 | "browserslist": [
46 | "> 1%",
47 | "last 2 versions"
48 | ]
49 | }
50 |
--------------------------------------------------------------------------------
/smf-runner/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
33 |
34 |
57 |
--------------------------------------------------------------------------------
/smf-runner/src/components/BombStatus.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Cluster: {{bomb.clusterName}}
5 |
6 |
7 | Total Requests: {{bomb.requests}}
8 |
9 |
10 |
11 |
{{completed}}
12 |
13 |
14 |
15 |
27 |
59 |
--------------------------------------------------------------------------------
/smf-runner/src/compositions/bomb.js:
--------------------------------------------------------------------------------
1 | import {computed, reactive} from '@vue/composition-api';
2 | import axios from 'axios'
3 | import Vue from 'vue';
4 |
5 | const STORAGE_KEY = 'smf_bomb';
6 |
7 | export const bomb = Vue.observable(localStorage.getItem(STORAGE_KEY) ? JSON.parse(localStorage.getItem(STORAGE_KEY)) : {
8 | clusterName: '',
9 | parallels: 100,
10 | bombId: '',
11 | status: '',
12 | completed: 0,
13 | requests: 0,
14 | });
15 |
16 | export function createBomb(clusterName, testUrl, parallels = 100) {
17 | bomb.clusterName = clusterName;
18 | bomb.parallels = parallels;
19 | localStorage.setItem(STORAGE_KEY, JSON.stringify(bomb));
20 | return axios.post(`/api/bomb/${clusterName}`, {url: testUrl}).then(res => res.data)
21 | .then(data => {
22 | if (data.bombId) {
23 | bomb.bombId = data.bombId;
24 | localStorage.setItem(STORAGE_KEY, JSON.stringify(bomb));
25 | checkBomb();
26 | }
27 | });
28 | }
29 |
30 | export function checkBomb() {
31 | if (!bomb.bombId || !bomb.clusterName) {
32 | return;
33 | }
34 | return axios.get(`/api/bomb/${bomb.clusterName}/${bomb.bombId}/status`)
35 | .then(res => res.data)
36 | .then(({status, completed, requests}) => {
37 | bomb.status = status;
38 | bomb.completed = completed;
39 | bomb.requests = requests;
40 | localStorage.setItem(STORAGE_KEY, JSON.stringify(bomb));
41 | setTimeout(checkBomb, 2000);
42 | });
43 | }
44 |
45 | export function useBomb() {
46 | return {
47 | bomb: reactive(bomb),
48 | completed: computed(() => (bomb.completed).toFixed(2) + '%')
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/smf-runner/src/components/LoadSpinner.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
113 |
--------------------------------------------------------------------------------
/smf-runner/server/api.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios');
2 | const {getClusters, applyDeployment} = require("./kube");
3 | const clusters = require('../../clusters');
4 |
5 | module.exports = function (app) {
6 |
7 | app.get("/api/clusters", async function (req, res) {
8 | const clusters = await getClusters();
9 | res.status(200).json(clusters);
10 | });
11 |
12 | app.get("/api/clusters/:clusterName", async function (req, res) {
13 | const cluster = clusters[req.params.clusterName];
14 | if (!cluster) {
15 | res.status(401).json({message: 'cluster does not exist'});
16 | return;
17 | }
18 | res.status(200).json(cluster);
19 | });
20 |
21 | app.post("/api/apply", async function (req, res) {
22 | const body = req.body || {};
23 | if (!(body.clusterName && body.namespace && body.deploymentFilePath)) {
24 | res.status(401).json({message: 'missing arguments'});
25 | return;
26 | }
27 | await applyDeployment(body.clusterName, body.namespace, body.deploymentFilePath);
28 | res.status(200).json({status: 'success'});
29 | });
30 |
31 | app.post("/api/bomb/:clusterName", async function (req, res) {
32 | const cluster = clusters[req.params.clusterName];
33 | const url = req.body.url;
34 | if (!cluster) {
35 | res.status(401).json({message: 'cluster does not exist'});
36 | return;
37 | }
38 | if (!url) {
39 | res.status(401).json({message: 'please provide a url to test'});
40 | return;
41 | }
42 |
43 | const timeInSeconds = 60 * 60;
44 | const concurrentThreads = req.body.parallels || 100;
45 |
46 | axios.post(
47 | `${cluster.bombServiceUrl}/api/v1/bomb/${timeInSeconds}/${concurrentThreads}`,
48 | null,
49 | {params: {url}}
50 | )
51 | .then(({data}) => {
52 | // should be {bombId: number}
53 | res.status(200).json(data);
54 | })
55 | .catch(err => {
56 | console.log(err);
57 | res.status(500).json({message: err.message});
58 | })
59 | });
60 |
61 | app.get("/api/bomb/:clusterName/:bombId/status", async function (req, res) {
62 | const cluster = clusters[req.params.clusterName];
63 | if (!cluster) {
64 | res.status(401).json({message: 'cluster does not exist'});
65 | return;
66 | }
67 |
68 | axios.get(`${cluster.bombServiceUrl}/api/v1/bomb/${req.params.bombId}/status`)
69 | .then(({data}) => {
70 | // should be:
71 | // {status: 'running' | 'done', completed: Number(between 0 to 1)}
72 | res.status(200).json(data);
73 | })
74 | .catch(err => {
75 | console.log(err);
76 | res.status(500).json({message: err.message});
77 | })
78 | });
79 |
80 | };
81 |
--------------------------------------------------------------------------------
/smf-runner/src/components/ApplyForm.vue:
--------------------------------------------------------------------------------
1 |
2 |
33 |
34 |
54 |
106 |
--------------------------------------------------------------------------------
/smf-runner/server/kube.js:
--------------------------------------------------------------------------------
1 | const {exec} = require("child_process");
2 | const clusters = require('../../clusters');
3 |
4 |
5 | function getClusters() {
6 | return Promise.resolve(Object.keys(clusters));
7 | /*return new Promise((resolve, reject) => {
8 | exec("kubectl config get-contexts -o=name", function (err, data) {
9 | if (err) {
10 | reject(err);
11 | return;
12 | }
13 | resolve(
14 | data
15 | .toString()
16 | .split("\n")
17 | .filter(Boolean)
18 | );
19 | });
20 | });*/
21 | }
22 |
23 | function useKubeCluster(clusterName) {
24 | return new Promise((resolve, reject) => {
25 | exec(`kubectl config use-context ${clusterName}`, function (err, data) {
26 | if (err) {
27 | reject(err);
28 | return;
29 | }
30 | const result = data.toString();
31 | if (result.includes('error')) {
32 | reject(result);
33 | return;
34 | }
35 | resolve(result);
36 | });
37 | });
38 | }
39 |
40 | async function applyDeployment(clusterName, namespace, deploymentFilePath) {
41 | await useKubeCluster(clusterName);
42 | await createNamespace(namespace);
43 |
44 | return new Promise((resolve, reject) => {
45 | exec(`kubectl apply -f ${deploymentFilePath} -n ${namespace}`, function (err, data) {
46 | if (err) {
47 | reject(err);
48 | return;
49 | }
50 | const result = data.toString();
51 | if (result.includes('error')) {
52 | reject(result);
53 | return;
54 | }
55 | resolve(result);
56 | });
57 | });
58 | }
59 |
60 | function getNamespaces() {
61 | return new Promise((resolve, reject) => {
62 | exec(`kubectl get ns -o=name`, function (err, data) {
63 | if (err) {
64 | reject(err);
65 | return;
66 | }
67 | resolve(
68 | data
69 | .toString()
70 | .split("\n")
71 | .filter(Boolean)
72 | .map(name => name.substr('namespace/'.length))
73 | );
74 | });
75 | });
76 | }
77 |
78 | async function createNamespace(namespace) {
79 | const namespaces = await getNamespaces();
80 |
81 | if (namespaces.includes(namespace)) {
82 | return Promise.resolve();
83 | }
84 |
85 | return new Promise((resolve, reject) => {
86 | exec(`kubectl create ns ${namespace}`, function (err, data) {
87 | if (err) {
88 | reject(err);
89 | return;
90 | }
91 | const result = data.toString();
92 | if (result.includes('error')) {
93 | reject(result);
94 | return;
95 | }
96 | resolve(result);
97 | });
98 | });
99 | }
100 |
101 | module.exports = {getClusters, applyDeployment};
102 |
--------------------------------------------------------------------------------
/appBomp/goProject/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/gorilla/mux"
6 | "log"
7 | "net/http"
8 | "strconv"
9 | "time"
10 | )
11 |
12 | const PRODUCT_PAGE_URL = "http://www.google.com"
13 |
14 | func params(w http.ResponseWriter, r *http.Request) {
15 | w.Header().Set("Content-Type", "application/json")
16 | userID := -1
17 | var err error
18 |
19 | pathParams := mux.Vars(r)
20 | if val, ok := pathParams["userID"]; ok {
21 | userID, err = strconv.Atoi(val)
22 | if err != nil {
23 | w.WriteHeader(http.StatusInternalServerError)
24 | w.Write([]byte(`{"message": "need a number for userID"}`))
25 | return
26 | }
27 | }
28 |
29 | commentID := -1
30 | if val, ok := pathParams["commentID"]; ok {
31 | commentID, err = strconv.Atoi(val)
32 | if err != nil {
33 | w.WriteHeader(http.StatusInternalServerError)
34 | w.Write([]byte(`{"message": "need a number for commentID"}`))
35 | return
36 | }
37 | }
38 |
39 | query := r.URL.Query()
40 | location := query.Get("location")
41 |
42 | w.Write([]byte(fmt.Sprintf(`{"userID": %d, "commentID": %d, "location": "%s" }`, userID, commentID, location)))
43 | }
44 |
45 | /**
46 | expected response:
47 | {"bombId": 123 }
48 | */
49 | func startSending1(w http.ResponseWriter, r *http.Request) {
50 | println("============= startSending1 ==========================")
51 | w.Header().Set("Content-Type", "application/json")
52 | timeInSeconds := -1
53 | var err error
54 |
55 | pathParams := mux.Vars(r)
56 | if val, ok := pathParams["timeInSeconds"]; ok {
57 | timeInSeconds, err = strconv.Atoi(val)
58 | if err != nil {
59 | w.WriteHeader(http.StatusInternalServerError)
60 | w.Write([]byte(`{"message": "timeInSeconds must be a number"}`))
61 | return
62 | }
63 | }
64 |
65 | concurrentThreads := -1
66 | if val, ok := pathParams["concurrentThreads"]; ok {
67 | concurrentThreads, err = strconv.Atoi(val)
68 | if err != nil {
69 | w.WriteHeader(http.StatusInternalServerError)
70 | w.Write([]byte(`{"message": "need a number for commentID"}`))
71 | return
72 | }
73 | }
74 |
75 | query := r.URL.Query()
76 | location := query.Get("url")
77 |
78 | //timeInSeconds = timeInSeconds + 1
79 | //concurrentThreads = concurrentThreads + 1
80 |
81 | go startSendingRequests(timeInSeconds, concurrentThreads, location)
82 |
83 | bombId := 3
84 |
85 | w.Write([]byte(fmt.Sprintf(`{"bombId": %d }`, bombId)))
86 | }
87 |
88 | /**
89 | expected response:
90 | {status: 'running' | 'done', completed: Number(between 0 to 1), grafanaUrl: string}
91 | */
92 | func findStatus(w http.ResponseWriter, r *http.Request) {
93 | println("============= findStatus ==========================")
94 |
95 | w.Header().Set("Content-Type", "application/json")
96 | bombId := -1
97 | var err error
98 |
99 | pathParams := mux.Vars(r)
100 | if val, ok := pathParams["bombId"]; ok {
101 | println(val)
102 | bombId, err = strconv.Atoi(val)
103 | if err != nil {
104 | w.WriteHeader(http.StatusInternalServerError)
105 | w.Write([]byte(`{"message": "bombId must be a number"}`))
106 | return
107 | }
108 | }
109 |
110 | bombId = bombId + 1
111 |
112 | // TODO here: retrieve data of this bombId
113 |
114 | status := "running"
115 |
116 | percentNow := percent
117 | if percentNow >= 1.0 {
118 | status = "done"
119 | percentNow = 1.0
120 | }
121 |
122 | completedPercent := percentNow
123 | grafanaUrl := "http://www.tikalk.com"
124 |
125 | responseBody := fmt.Sprintf(`{"status": "%s", "completed": %f, "grafanaUrl": "%s", "requests": %d}`, status, completedPercent, grafanaUrl, countOK)
126 |
127 | w.Write([]byte(responseBody))
128 | }
129 |
130 | func startSendingRequests(timeInSeconds int, concurrentThreads int, urlEncoded string) {
131 | println("Starting ", timeInSeconds, " seconds of requests on ", concurrentThreads, " concurrent threads ...")
132 |
133 | if (concurrentThreads > 200) || (concurrentThreads < 1) {
134 | println("change concurrentThreads from ", concurrentThreads, " to 1")
135 | concurrentThreads = 1
136 | }
137 | countOK = 0
138 | countErr = 0
139 | responses := make(chan job, 100000)
140 | go aggregateStatus(responses, concurrentThreads)
141 | for i := 0; i < concurrentThreads; i++ {
142 | go sendManyRequests(i, timeInSeconds, urlEncoded, responses)
143 | }
144 |
145 | println("completed!")
146 | }
147 |
148 | var countOK int64
149 | var countErr int64
150 | var percent float32
151 |
152 | func aggregateStatus(statusCodes chan job, concurrentThreads int) {
153 | for j := range statusCodes {
154 | //percent = j.percent // * float32(concurrentThreads)
155 | percent = j.percent
156 | if j.statusCode == 200 {
157 | countOK = countOK + 1
158 | } else {
159 | countErr++
160 | }
161 | }
162 | }
163 |
164 | type Pair struct {
165 | a, b interface{}
166 | }
167 | type job struct {
168 | statusCode int
169 | percent float32
170 | }
171 |
172 | func sendManyRequests(threadNumber int, timeInSeconds int, urlEncoded string, responses chan job) {
173 | startTime := time.Now().Unix()
174 | fmt.Printf("currentTime = %v\n", startTime)
175 |
176 | runUntil := startTime + int64(timeInSeconds)
177 | fmt.Printf("runUntil = %v\n", runUntil)
178 | for {
179 | statusCode := work(threadNumber, urlEncoded)
180 | secondsPassed := time.Now().Unix() - startTime
181 | secondsRequested := timeInSeconds
182 | current := float32(secondsPassed) / float32(secondsRequested)
183 | //fmt.Println("seconds Passed:", secondsPassed, ", seconds Requested:", secondsRequested, ", percent=", current)
184 | responses <- job{statusCode, current}
185 | //responses<-Pair{statusCode, current}
186 | if time.Now().Unix() > runUntil {
187 | break
188 | }
189 | }
190 | }
191 |
192 | func work(threadNumber int, urlEncoded string) int {
193 | //println("[thread ", threadNumber, "] send ", urlEncoded, " ")
194 | resp, err := http.Get(urlEncoded)
195 | if err != nil {
196 | panic(err)
197 | }
198 | defer resp.Body.Close()
199 |
200 | //fmt.Println("Response status:", resp.Status)
201 |
202 | return resp.StatusCode
203 | }
204 |
205 | /*******************************
206 | Clear is Better than Clever
207 | Rob Pike
208 | ******************************** */
209 | func main() {
210 | r := mux.NewRouter()
211 |
212 | api := r.PathPrefix("/api/v1").Subrouter()
213 |
214 | api.HandleFunc("/bomb/{timeInSeconds}/{concurrentThreads}", startSending1).Methods(http.MethodPost) // + ?url=http://www.google.com
215 | api.HandleFunc("/bomb/{bombId}/status", findStatus).Methods(http.MethodGet) // ?timeInSeconds=3&concurrentThreads=4
216 |
217 | log.Fatal(http.ListenAndServe(":30001", r))
218 | }
219 |
--------------------------------------------------------------------------------
/bookinfo.yaml:
--------------------------------------------------------------------------------
1 | # Copyright 2017 Istio Authors
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | ##################################################################################################
16 | # This file defines the services, service accounts, and deployments for the Bookinfo sample.
17 | #
18 | # To apply all 4 Bookinfo services, their corresponding service accounts, and deployments:
19 | #
20 | # kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
21 | #
22 | # Alternatively, you can deploy any resource separately:
23 | #
24 | # kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l service=reviews # reviews Service
25 | # kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l account=reviews # reviews ServiceAccount
26 | # kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l app=reviews,version=v3 # reviews-v3 Deployment
27 | ##################################################################################################
28 |
29 | ##################################################################################################
30 | # Details service
31 | ##################################################################################################
32 | apiVersion: v1
33 | kind: Service
34 | metadata:
35 | name: details
36 | labels:
37 | app: details
38 | service: details
39 | spec:
40 | ports:
41 | - port: 9080
42 | name: http
43 | selector:
44 | app: details
45 | ---
46 | apiVersion: v1
47 | kind: ServiceAccount
48 | metadata:
49 | name: bookinfo-details
50 | labels:
51 | account: details
52 | ---
53 | apiVersion: apps/v1
54 | kind: Deployment
55 | metadata:
56 | name: details-v1
57 | labels:
58 | app: details
59 | version: v1
60 | spec:
61 | replicas: 1
62 | selector:
63 | matchLabels:
64 | app: details
65 | version: v1
66 | template:
67 | metadata:
68 | labels:
69 | app: details
70 | version: v1
71 | spec:
72 | serviceAccountName: bookinfo-details
73 | containers:
74 | - name: details
75 | image: docker.io/istio/examples-bookinfo-details-v1:1.15.0
76 | imagePullPolicy: IfNotPresent
77 | ports:
78 | - containerPort: 9080
79 | ---
80 | ##################################################################################################
81 | # Ratings service
82 | ##################################################################################################
83 | apiVersion: v1
84 | kind: Service
85 | metadata:
86 | name: ratings
87 | labels:
88 | app: ratings
89 | service: ratings
90 | spec:
91 | ports:
92 | - port: 9080
93 | name: http
94 | selector:
95 | app: ratings
96 | ---
97 | apiVersion: v1
98 | kind: ServiceAccount
99 | metadata:
100 | name: bookinfo-ratings
101 | labels:
102 | account: ratings
103 | ---
104 | apiVersion: apps/v1
105 | kind: Deployment
106 | metadata:
107 | name: ratings-v1
108 | labels:
109 | app: ratings
110 | version: v1
111 | spec:
112 | replicas: 1
113 | selector:
114 | matchLabels:
115 | app: ratings
116 | version: v1
117 | template:
118 | metadata:
119 | labels:
120 | app: ratings
121 | version: v1
122 | spec:
123 | serviceAccountName: bookinfo-ratings
124 | containers:
125 | - name: ratings
126 | image: docker.io/istio/examples-bookinfo-ratings-v1:1.15.0
127 | imagePullPolicy: IfNotPresent
128 | ports:
129 | - containerPort: 9080
130 | ---
131 | ##################################################################################################
132 | # Reviews service
133 | ##################################################################################################
134 | apiVersion: v1
135 | kind: Service
136 | metadata:
137 | name: reviews
138 | labels:
139 | app: reviews
140 | service: reviews
141 | spec:
142 | ports:
143 | - port: 9080
144 | name: http
145 | selector:
146 | app: reviews
147 | ---
148 | apiVersion: v1
149 | kind: ServiceAccount
150 | metadata:
151 | name: bookinfo-reviews
152 | labels:
153 | account: reviews
154 | ---
155 | apiVersion: apps/v1
156 | kind: Deployment
157 | metadata:
158 | name: reviews-v1
159 | labels:
160 | app: reviews
161 | version: v1
162 | spec:
163 | replicas: 1
164 | selector:
165 | matchLabels:
166 | app: reviews
167 | version: v1
168 | template:
169 | metadata:
170 | labels:
171 | app: reviews
172 | version: v1
173 | spec:
174 | serviceAccountName: bookinfo-reviews
175 | containers:
176 | - name: reviews
177 | image: docker.io/istio/examples-bookinfo-reviews-v1:1.15.0
178 | imagePullPolicy: IfNotPresent
179 | ports:
180 | - containerPort: 9080
181 | ---
182 | apiVersion: apps/v1
183 | kind: Deployment
184 | metadata:
185 | name: reviews-v2
186 | labels:
187 | app: reviews
188 | version: v2
189 | spec:
190 | replicas: 1
191 | selector:
192 | matchLabels:
193 | app: reviews
194 | version: v2
195 | template:
196 | metadata:
197 | labels:
198 | app: reviews
199 | version: v2
200 | spec:
201 | serviceAccountName: bookinfo-reviews
202 | containers:
203 | - name: reviews
204 | image: docker.io/istio/examples-bookinfo-reviews-v2:1.15.0
205 | imagePullPolicy: IfNotPresent
206 | ports:
207 | - containerPort: 9080
208 | ---
209 | apiVersion: apps/v1
210 | kind: Deployment
211 | metadata:
212 | name: reviews-v3
213 | labels:
214 | app: reviews
215 | version: v3
216 | spec:
217 | replicas: 1
218 | selector:
219 | matchLabels:
220 | app: reviews
221 | version: v3
222 | template:
223 | metadata:
224 | labels:
225 | app: reviews
226 | version: v3
227 | spec:
228 | serviceAccountName: bookinfo-reviews
229 | containers:
230 | - name: reviews
231 | image: docker.io/istio/examples-bookinfo-reviews-v3:1.15.0
232 | imagePullPolicy: IfNotPresent
233 | ports:
234 | - containerPort: 9080
235 | ---
236 | ##################################################################################################
237 | # Productpage services
238 | ##################################################################################################
239 | apiVersion: v1
240 | kind: Service
241 | metadata:
242 | name: productpage
243 | labels:
244 | app: productpage
245 | service: productpage
246 | spec:
247 | ports:
248 | - port: 9080
249 | name: http
250 | type: LoadBalancer
251 | selector:
252 | app: productpage
253 | ---
254 | apiVersion: v1
255 | kind: ServiceAccount
256 | metadata:
257 | name: bookinfo-productpage
258 | labels:
259 | account: productpage
260 | ---
261 | apiVersion: apps/v1
262 | kind: Deployment
263 | metadata:
264 | name: productpage-v1
265 | labels:
266 | app: productpage
267 | version: v1
268 | spec:
269 | replicas: 1
270 | selector:
271 | matchLabels:
272 | app: productpage
273 | version: v1
274 | template:
275 | metadata:
276 | labels:
277 | app: productpage
278 | version: v1
279 | spec:
280 | serviceAccountName: bookinfo-productpage
281 | containers:
282 | - name: productpage
283 | image: docker.io/istio/examples-bookinfo-productpage-v1:1.15.0
284 | imagePullPolicy: IfNotPresent
285 | ports:
286 | - containerPort: 9080
287 | ---
288 |
--------------------------------------------------------------------------------