├── .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 | 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 | 10 | 11 | 33 | 34 | 57 | -------------------------------------------------------------------------------- /smf-runner/src/components/BombStatus.vue: -------------------------------------------------------------------------------- 1 | 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 | 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 | 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 | --------------------------------------------------------------------------------