mirror of
https://github.com/lucky-sideburn/kubeinvaders.git
synced 2026-08-29 00:47:16 +00:00
added pure javascript version
This commit is contained in:
+7
-3
@@ -1,7 +1,6 @@
|
||||
FROM nginx
|
||||
|
||||
# Install kubectl
|
||||
|
||||
RUN curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl"
|
||||
RUN chmod +x ./kubectl
|
||||
RUN mv ./kubectl /usr/local/bin/kubectl
|
||||
@@ -12,8 +11,11 @@ RUN apt-get -y install --no-install-recommends wget gnupg ca-certificates jq ope
|
||||
RUN wget -O - https://openresty.org/package/pubkey.gpg | apt-key add -
|
||||
RUN codename=`grep -Po 'VERSION="[0-9]+ \(\K[^)]+' /etc/os-release` && echo "deb http://openresty.org/package/debian $codename openresty" | tee /etc/apt/sources.list.d/openresty.list
|
||||
RUN apt-get update
|
||||
RUN apt-get -y install openresty
|
||||
RUN apt-get -y install openresty luarocks
|
||||
RUN chmod 777 /usr/local/openresty/nginx
|
||||
RUN apt-get -y install openresty luarocks libssl-dev git vim lua-json lua-socket
|
||||
RUN luarocks install luasec
|
||||
RUN luarocks install lunajson
|
||||
|
||||
# Install kube-linter
|
||||
RUN curl -L -O https://github.com/stackrox/kube-linter/releases/download/0.1.5/kube-linter-linux.tar.gz
|
||||
@@ -41,13 +43,15 @@ RUN sed -i.bak 's/listen\(.*\)80;/listen 8081;/' /etc/nginx/conf.d/default.conf
|
||||
|
||||
COPY nginx/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY nginx/metrics.lua-script /tmp/metrics.lua
|
||||
COPY nginx/pod.lua /tmp/pod.lua
|
||||
COPY nginx/node.lua /tmp/node.lua
|
||||
COPY chaos-node/chaos-node.lua /tmp/chaos-node.lua
|
||||
|
||||
COPY nginx/KubeInvaders.templ /etc/nginx/conf.d/KubeInvaders.templ
|
||||
COPY nginx/KubeInvaders_dev.templ /etc/nginx/conf.d/KubeInvaders_dev.templ
|
||||
|
||||
RUN chmod g+rwx /var/cache/nginx /var/run /var/log/nginx /var/www/html /etc/nginx/conf.d/KubeInvaders.templ /etc/nginx/conf.d
|
||||
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENV PATH=/usr/local/openresty/nginx/sbin:$PATH
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
FROM nginx
|
||||
|
||||
# Install kubectl
|
||||
|
||||
RUN curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl"
|
||||
RUN chmod +x ./kubectl
|
||||
RUN mv ./kubectl /usr/local/bin/kubectl
|
||||
|
||||
# Install Openresty
|
||||
RUN apt-get update
|
||||
RUN apt-get -y install --no-install-recommends wget gnupg ca-certificates jq openssl task-spooler at
|
||||
RUN wget -O - https://openresty.org/package/pubkey.gpg | apt-key add -
|
||||
RUN codename=`grep -Po 'VERSION="[0-9]+ \(\K[^)]+' /etc/os-release` && echo "deb http://openresty.org/package/debian $codename openresty" | tee /etc/apt/sources.list.d/openresty.list
|
||||
RUN apt-get update
|
||||
RUN apt-get -y install openresty
|
||||
RUN chmod 777 /usr/local/openresty/nginx
|
||||
|
||||
# Install kube-linter
|
||||
RUN curl -L -O https://github.com/stackrox/kube-linter/releases/download/0.1.5/kube-linter-linux.tar.gz
|
||||
RUN tar -xvf kube-linter-linux.tar.gz
|
||||
RUN rm -f kube-linter-linux.tar.gz
|
||||
RUN cp kube-linter /usr/local/bin/
|
||||
RUN chmod 775 /usr/local/bin/kube-linter
|
||||
COPY kube-linter/kube-linter-parser.sh /opt/kube-linter-parser.sh
|
||||
RUN chmod +x /opt/kube-linter-parser.sh
|
||||
# Install Redis
|
||||
RUN apt-get install redis -y
|
||||
COPY redis/redis.conf /etc/redis/redis.conf
|
||||
|
||||
# Install KubeInvaders
|
||||
COPY ./js-web/KubeInvaders /var/www/html
|
||||
|
||||
# Configure Nginx
|
||||
#RUN sed -i.bak 's/^user/#user/' /etc/nginx/nginx.conf
|
||||
RUN sed -i.bak 's/listen\(.*\)80;/listen 8081;/' /etc/nginx/conf.d/default.conf
|
||||
|
||||
COPY nginx/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY nginx/metrics.lua-script /tmp/metrics.lua
|
||||
COPY chaos-node/chaos-node.lua-script /tmp/chaos-node.lua
|
||||
|
||||
COPY nginx/KubeInvaders.templ /etc/nginx/conf.d/KubeInvaders.templ
|
||||
COPY nginx/KubeInvaders_dev.templ /etc/nginx/conf.d/KubeInvaders_dev.templ
|
||||
|
||||
RUN chmod g+rwx /var/cache/nginx /var/run /var/log/nginx /var/www/html /etc/nginx/conf.d/KubeInvaders.templ /etc/nginx/conf.d
|
||||
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENV PATH=/usr/local/openresty/nginx/sbin:$PATH
|
||||
COPY ./temporary_hack.sh /
|
||||
RUN chmod a+rwx temporary_hack.sh
|
||||
ENTRYPOINT ["/temporary_hack.sh"]
|
||||
@@ -1,39 +1,44 @@
|
||||

|
||||
|
||||
*Gamified chaos engineering and analysis tool for Kubernetes. It is like Space Invaders but the aliens are PODs.*
|
||||
*Gamified chaos engineering and analysis tool for Kubernetes. It is like Space Invaders but the aliens are pods or worker nodes.*
|
||||
|
||||

|
||||
|
||||
# Table of Contents
|
||||
|
||||
1. [Description](#Description)
|
||||
2. [Special Input Keys and features](#Special-Input-Keys-and-features)
|
||||
3. [Prometheus metrics and Grafana](#Metrics)
|
||||
4. [Installation](#Installation)
|
||||
5. [Notes for large clusters](#Notes-for-large-clusters)
|
||||
6. [Configuration](#Configuration)
|
||||
2. [New Version (game part in pure JS)](#new-version)
|
||||
3. [Special Input Keys and features](#Special-Input-Keys-and-features)
|
||||
4. [Prometheus metrics and Grafana](#Metrics)
|
||||
5. [Installation](#Installation)
|
||||
6. [Notes for large clusters](#Notes-for-large-clusters)
|
||||
7. [Configuration](#Configuration)
|
||||
|
||||
## Description
|
||||
|
||||
KubeInvaders has been developed using [Defold](https://www.defold.com/).
|
||||
|
||||
Through KubeInvaders you can stress a Kubernetes cluster in a fun way and check how it is resilient.
|
||||
## New Version
|
||||
|
||||
I added also new experimental features like a linter for the pods. The current latest image of the game include [kube-linter](https://github.com/stackrox/kube-linter) developed by [stackrox](https://github.com/stackrox).
|
||||
KubeInvaders is going to be full open-source. Meanwhile it possibile choose 2 different version.
|
||||
|
||||
Legacy version: Made with Defold as game engine
|
||||
New version: It has been written in pure Javascript and it is totaly open-source
|
||||
|
||||
The new version of KubeInvaders has fewer features than legacy but is optimized for chaos engineering because pods and nodes are rendered together and there is a shuffle for better random experiments.
|
||||
|
||||
## Special Input Keys and features
|
||||
|
||||
| Input | Action |
|
||||
|-----------------|--------------------------------------------------------------------------------------------|
|
||||
| n | Change namespace (you should define namespaces list. Ex: TARGET_NAMESPACE=foo1,foo2,foo3). |
|
||||
| a | Switch to automatic mode. |
|
||||
| m | Switch to manual mode. |
|
||||
| h | Show special keys. |
|
||||
| q | Hide help for special keys. |
|
||||
| i | Show pod's name. Move the ship towards an alien. |
|
||||
| r | Refresh log of a pod when spaceship is over the alien. |
|
||||
| k | *(NEW)* Perform [kube-linter](https://github.com/stackrox/kube-linter) analysis for a pod. |
|
||||
| w | *(NEW)* Chaos engineering against Kubernetes nodes. |
|
||||
| Input | Action | Version (New or Legacy)|
|
||||
|-----------------|--------------------------------------------------------------------------------------------|------------------------|
|
||||
| n | Change namespace (you should define namespaces list. Ex: TARGET_NAMESPACE=foo1,foo2,foo3). | New, Legacy |
|
||||
| a | Switch to automatic mode. | Legacy |
|
||||
| m | Switch to manual mode. | Legacy |
|
||||
| h | Show special keys. | New, Legacy |
|
||||
| q | Hide help for special keys. | New, Legacy |
|
||||
| i | Show pod's name. Move the ship towards an alien. | Legacy |
|
||||
| r | Refresh log of a pod when spaceship is over the alien. | Legacy |
|
||||
| k | *(NEW)* Perform [kube-linter](https://github.com/stackrox/kube-linter) analysis for a pod. | Legacy |
|
||||
| w | *(NEW)* Chaos engineering against Kubernetes nodes. | New, Legacy |
|
||||
|
||||
### Known problems
|
||||
|
||||
@@ -82,44 +87,15 @@ git clone https://github.com/lucky-sideburn/KubeInvaders.git
|
||||
|
||||
kubectl create namespace kubeinvaders
|
||||
|
||||
# Install new and full open-source version
|
||||
helm install kubeinvaders --set-string target_namespace="namespace1\,namespace2" \
|
||||
--namespace kubeinvaders ./helm-charts/kubeinvaders \
|
||||
--set ingress.hostName=kubeinvaders.io
|
||||
```
|
||||
### Install client on your workstation
|
||||
--set ingress.hostName=kubeinvaders.io --set image.tag=latest
|
||||
|
||||
The easiest way to install KubeInvaders is on your workstation but if you choose this method you cannot use kube-linter feature directly from the game. Follow this guide:
|
||||
|
||||
1. Start KubeInvaders docker container locally
|
||||
|
||||
```bash
|
||||
docker rm kubeinvaders -f && docker run --env DEVELOPMENT=true --env ENDPOINT=https://<k8s_url> --env NAMESPACE=namespace1,namespace2 --env TOKEN=<Service Account token> -p 8080:8080 --name kubeinvaders docker.io/luckysideburn/kubeinvaders
|
||||
```
|
||||
|
||||
2. Create $HOME/.KubeInv.json like this - The endpoint is localhost:8080 because it is using KubeInvaders container as a proxy
|
||||
to Kubernetes:
|
||||
|
||||
```json
|
||||
{
|
||||
"token": "<Service Account Token>",
|
||||
"endpoint": "http://localhost:8080",
|
||||
"namespace": "namespace1,namespace2"
|
||||
}
|
||||
```
|
||||
|
||||
Download the game from these locations:
|
||||
|
||||
* [MacOS](https://github.com/lucky-sideburn/KubeInvaders/releases/download/1.0-metrics/x86_64-darwin.zip)
|
||||
* [Linux](https://github.com/lucky-sideburn/KubeInvaders/releases/download/1.0-metrics/x86_64-linux.zip)
|
||||
### Run directly from Docker
|
||||
|
||||
This method can be used for developing KubeInvaders and testing the HTML5 bundle.
|
||||
Using this method you can have problem of CORS:
|
||||
|
||||
```bash
|
||||
docker build . -t kubeinvaders_dev
|
||||
|
||||
docker rm kubeinvaders -f && docker run --env DEVELOPMENT=true --env ENDPOINT=https://youk8scluster:8443 --env NAMESPACE=kubeinvadersdemo --env TOKEN=xxxx -p 8080:8080 --name kubeinvaders kubeinvaders_dev
|
||||
# Install legacy version
|
||||
helm install kubeinvaders --set-string target_namespace="namespace1\,namespace2" \
|
||||
--namespace kubeinvaders ./helm-charts/kubeinvaders \
|
||||
--set ingress.hostName=kubeinvaders.io --set image.tag=legacy
|
||||
```
|
||||
### Install KubeInvaders on OpenShift
|
||||
|
||||
@@ -147,11 +123,6 @@ KUBEINVADERS_SECRET=$(oc get secret -n kubeinvaders --field-selector=type==kuber
|
||||
|
||||
oc process -f openshift/KubeInvaders.yaml -p ROUTE_HOST=$ROUTE_HOST -p TARGET_NAMESPACE=$TARGET_NAMESPACE -p KUBEINVADERS_SECRET=$KUBEINVADERS_SECRET | oc create -f -
|
||||
```
|
||||
|
||||
#### How the configuration of KubeInvaders DeploymentConfig should be (remember to use your TARGET_NAMESPACE and ROUTE_HOST)
|
||||
|
||||

|
||||
|
||||
## Notes for large clusters
|
||||
|
||||
For clusters with many workers-nodes, KubeInvaders selects a subset of random items.
|
||||
@@ -160,7 +131,7 @@ For clusters with many workers-nodes, KubeInvaders selects a subset of random it
|
||||
|-----------|--------------|
|
||||
| Nodes | 15 |
|
||||
## Configuration
|
||||
### Environment Variables - Make the game more difficult to win!
|
||||
### (Legacy Version) Environment Variables - Make the game more difficult to win!
|
||||
|
||||
Set the following variables in Kubernetes Deployment or OpenShift DeploymentConfig:
|
||||
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
loadfile("/tmp/metrics.lua")
|
||||
|
||||
local https = require "ssl.https"
|
||||
local ltn12 = require "ltn12"
|
||||
local json = require 'lunajson'
|
||||
|
||||
function read_all(file)
|
||||
local f = assert(io.open(file, "rb"))
|
||||
local content = f:read("*all")
|
||||
f:close()
|
||||
return content
|
||||
end
|
||||
|
||||
local http = require("socket.http")
|
||||
math.randomseed(os.clock()*100000000000)
|
||||
local rand = math.random(999, 9999)
|
||||
local arg = ngx.req.get_uri_args()
|
||||
|
||||
local k8s_url = os.getenv("ENDPOINT")
|
||||
local token = os.getenv("TOKEN")
|
||||
local namespace = arg['namespace']
|
||||
local node_name = arg['node_name']
|
||||
local url = k8s_url .. "/apis/batch/v1/namespaces/" .. namespace .. "/jobs"
|
||||
local resp = {}
|
||||
|
||||
ngx.header['Access-Control-Allow-Origin'] = '*'
|
||||
ngx.header['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
|
||||
ngx.header['Access-Control-Allow-Headers'] = 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'
|
||||
ngx.header['Access-Control-Expose-Headers'] = 'Content-Length,Content-Range';
|
||||
|
||||
headers = {
|
||||
["Accept"] = "application/json",
|
||||
["Content-Type"] = "application/json",
|
||||
["Authorization"] = "Bearer " .. token,
|
||||
}
|
||||
|
||||
body = [[
|
||||
{
|
||||
"apiVersion": "batch/v1",
|
||||
"kind": "Job",
|
||||
"metadata": {
|
||||
"name": "kubeinvaders-chaos-]] .. rand .. [[",
|
||||
"labels": {
|
||||
"app": "kubeinvaders",
|
||||
"approle": "chaosnode"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"template": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"app": "kubeinvaders",
|
||||
"approle": "chaosnode"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "kubeinvaders-chaos-node",
|
||||
"image": "docker.io/luckysideburn/kubeinvaders-stress-ng:latest",
|
||||
"command": [
|
||||
"stress-ng",
|
||||
"--cpu",
|
||||
"4",
|
||||
"--io",
|
||||
"2",
|
||||
"--vm",
|
||||
"1",
|
||||
"--vm-bytes",
|
||||
"1G",
|
||||
"--timeout",
|
||||
"10s",
|
||||
"--metrics-brief"
|
||||
]
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Never",
|
||||
"nodeSelector": {
|
||||
"kubernetes.io/hostname": "wrk5-oc"
|
||||
}
|
||||
}
|
||||
},
|
||||
"backoffLimit": null
|
||||
}
|
||||
}
|
||||
]]
|
||||
|
||||
local headers2 = {
|
||||
["Accept"] = "application/json",
|
||||
["Content-Type"] = "application/json",
|
||||
["Authorization"] = "Bearer " .. token,
|
||||
["Content-Length"] = string.len(body)
|
||||
}
|
||||
|
||||
url = k8s_url .. "/apis/batch/v1/namespaces/" .. namespace .. "/jobs"
|
||||
ngx.log(ngx.ERR, "Creating chaos_node job kubeinvaders-chaos-" ..rand)
|
||||
|
||||
local ok, statusCode, headers, statusText = https.request{
|
||||
url = url,
|
||||
headers = headers2,
|
||||
method = "POST",
|
||||
sink = ltn12.sink.table(resp),
|
||||
source = ltn12.source.string(body)
|
||||
}
|
||||
|
||||
ngx.log(ngx.ERR, ok)
|
||||
ngx.log(ngx.ERR, statusCode)
|
||||
ngx.log(ngx.ERR, statusText)
|
||||
|
||||
local url = k8s_url.. "/apis/batch/v1/namespaces/" .. namespace .. "/jobs"
|
||||
ngx.log(ngx.ERR, "Getting JobList" .. rand)
|
||||
|
||||
local ok, statusCode, headers, statusText = https.request{
|
||||
url = url,
|
||||
headers = headers,
|
||||
method = "GET",
|
||||
sink = ltn12.sink.table(resp)
|
||||
}
|
||||
|
||||
ngx.log(ngx.ERR, ok)
|
||||
ngx.log(ngx.ERR, statusCode)
|
||||
ngx.log(ngx.ERR, statusText)
|
||||
|
||||
for k,v in ipairs(resp) do
|
||||
decoded = json.decode(v)
|
||||
if decoded["kind"] == "JobList" then
|
||||
for k2,v2 in ipairs(decoded["items"]) do
|
||||
if v2["status"]["succeeded"] == 1 and v2["metadata"]["labels"]["approle"] == "chaosnode" then
|
||||
delete_job = "kubectl delete job " .. v2["metadata"]["name"] .. " --token=" .. token .. " --server=" .. k8s_url .. " --insecure-skip-tls-verify=true -n " .. namespace
|
||||
ngx.log(ngx.ERR, delete_pod)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local url = k8s_url.. "/api/v1/namespaces/" .. namespace .. "/pods"
|
||||
ngx.log(ngx.ERR, "Getting PodList" .. rand)
|
||||
|
||||
local ok, statusCode, headers, statusText = https.request{
|
||||
url = url,
|
||||
headers = headers,
|
||||
method = "GET",
|
||||
sink = ltn12.sink.table(resp)
|
||||
}
|
||||
|
||||
ngx.log(ngx.ERR, ok)
|
||||
ngx.log(ngx.ERR, statusCode)
|
||||
ngx.log(ngx.ERR, statusText)
|
||||
|
||||
for k,v in ipairs(resp) do
|
||||
decoded = json.decode(v)
|
||||
if decoded["kind"] == "PodList" then
|
||||
for k2,v2 in ipairs(decoded["items"]) do
|
||||
if v2["status"]["phase"] == "Succeeded" and v2["metadata"]["labels"]["approle"] == "chaosnode" then
|
||||
delete_pod = "kubectl delete pod " .. v2["metadata"]["name"] .. " --token=" .. token .. " --server=" .. k8s_url .. " --insecure-skip-tls-verify=true -n " .. namespace
|
||||
ngx.log(ngx.ERR, delete_pod)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ngx.say("chaos node")
|
||||
+115
-14
@@ -9,7 +9,11 @@ var spaceshipHeight = 60;
|
||||
var spaceshipWidth = 60;
|
||||
var spaceshipX = (canvas.width-spaceshipWidth)/2;
|
||||
var spaceshipY = (canvas.height-spaceshipHeight)/2;
|
||||
var namespace = "kubeinvadersdemo";
|
||||
var cluster = "CLUSTER_PLACEOLDER";
|
||||
var namespaces = ["kubeinvadersdemo", "foobar"];
|
||||
var namespaces_index = 0;
|
||||
var namespace = namespaces[namespaces_index];
|
||||
var endpoint = "";
|
||||
|
||||
// pods list from kubernetes
|
||||
var pods = [];
|
||||
@@ -64,14 +68,43 @@ var collisionDetected = false;
|
||||
var aliensY = [];
|
||||
var aliensIncrementY = 50;
|
||||
|
||||
var shuffle = true;
|
||||
var help = false;
|
||||
|
||||
function getEndpoint(){
|
||||
var oReq = new XMLHttpRequest();
|
||||
oReq.onload = function () {
|
||||
endpoint = this.responseText;
|
||||
};;
|
||||
oReq.open("GET", "http://localhost:8080/kube/endpoint");
|
||||
oReq.send();
|
||||
}
|
||||
|
||||
function startChaosNode(node_name){
|
||||
var oReq = new XMLHttpRequest();
|
||||
oReq.onload = function () {
|
||||
console.log(JSON.parse(this.responseText))
|
||||
};;
|
||||
oReq.open("GET", "http://localhost:8080/kube/chaos/nodes?nodename=node_name=" + node_name + "&namespace=" + namespace);
|
||||
oReq.send();
|
||||
}
|
||||
|
||||
function deletePods(pod_name){
|
||||
var oReq = new XMLHttpRequest();
|
||||
oReq.onload = function () {
|
||||
console.log(JSON.parse(this.responseText))
|
||||
};;
|
||||
oReq.open("GET", "http://localhost:8080/kube/pods?action=delete&pod_name=" + pod_name + "&namespace=" + namespace);
|
||||
oReq.send();
|
||||
}
|
||||
|
||||
function getPods(){
|
||||
foo = pods;
|
||||
var oReq = new XMLHttpRequest();
|
||||
oReq.onload = function () {
|
||||
json_parsed = JSON.parse(this.responseText)
|
||||
pods = json_parsed["items"];
|
||||
pods = json_parsed["items"].concat(nodes);
|
||||
};;
|
||||
oReq.open("GET", "http://localhost:8080/kube/pods?namespace=" + namespace);
|
||||
oReq.open("GET", "http://localhost:8080/kube/pods?action=list&namespace=" + namespace);
|
||||
oReq.send();
|
||||
}
|
||||
|
||||
@@ -86,7 +119,8 @@ function getNodes(){
|
||||
}
|
||||
|
||||
function getKubeItems() {
|
||||
getPods()
|
||||
getNodes();
|
||||
getPods();
|
||||
}
|
||||
|
||||
function keyDownHandler(e) {
|
||||
@@ -114,10 +148,43 @@ function keyDownHandler(e) {
|
||||
console.log("Spaceship Y: " + spaceshipY);
|
||||
console.log("Spaceship X: " + spaceshipX);
|
||||
}
|
||||
else if(e.keyCode == 83) {
|
||||
if (shuffle) {
|
||||
shuffle = false;
|
||||
console.log("Deactivate shuffle");
|
||||
}
|
||||
else {
|
||||
shuffle = true
|
||||
console.log("Activate shuffle");
|
||||
}
|
||||
}
|
||||
else if(e.keyCode == 32) {
|
||||
console.log("Shot");
|
||||
shot = true
|
||||
}
|
||||
else if(e.keyCode == 78) {
|
||||
console.log("Change Namespace");
|
||||
if (namespaces_index < namespaces.length-1) {
|
||||
namespaces_index +=1 ;
|
||||
}
|
||||
else {
|
||||
namespaces_index = 0;
|
||||
}
|
||||
namespace = namespaces[namespaces_index];
|
||||
aliens = [];
|
||||
pods = [];
|
||||
}
|
||||
else if(e.keyCode == 72) {
|
||||
console.log("Help");
|
||||
if (help) {
|
||||
help = false;
|
||||
console.log("Deactivate help");
|
||||
}
|
||||
else {
|
||||
help = true
|
||||
console.log("Activate help");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function keyUpHandler(e) {
|
||||
@@ -138,10 +205,17 @@ function keyUpHandler(e) {
|
||||
document.addEventListener("keydown", keyDownHandler, false);
|
||||
document.addEventListener("keyup", keyUpHandler, false);
|
||||
|
||||
function drawAlien(alienX, alienY) {
|
||||
function drawAlien(alienX, alienY, name) {
|
||||
var image = new Image(); // Image constructor
|
||||
image.src = './sprite_invader.png';
|
||||
ctx.drawImage(image, alienX, alienY, 40, 40);
|
||||
if (nodes.includes(name)) {
|
||||
image.src = './k8s_node.png';
|
||||
ctx.drawImage(image, alienX, alienY, 30, 40);
|
||||
}
|
||||
else {
|
||||
image.src = './sprite_invader.png';
|
||||
ctx.drawImage(image, alienX, alienY, 40, 40);
|
||||
}
|
||||
|
||||
ctx.closePath();
|
||||
}
|
||||
|
||||
@@ -152,8 +226,6 @@ function checkRocketAlienCollision(){
|
||||
for (i=aliens.length - 1; i >= 0; i--) {
|
||||
if (aliens[i]["active"] && (rocketY - aliens[i]["y"] < 5)) {
|
||||
var rangeX = []
|
||||
//console.log(aliens[i]);
|
||||
//console.log(aliens[i]["x"]);
|
||||
rangeX.push(aliens[i]["x"]);
|
||||
|
||||
for (k=aliens[i]["x"]; k<aliens[i]["x"]+aliensWidth; k++) {
|
||||
@@ -166,7 +238,14 @@ function checkRocketAlienCollision(){
|
||||
console.log("collision detected");
|
||||
collisionDetected = true;
|
||||
aliens[i]["active"] = false;
|
||||
aliens[i]["name"] = "killed_pod";
|
||||
if (nodes.includes(aliens[i]["name"])) {
|
||||
startChaosNode(aliens[i]["name"]);
|
||||
aliens[i]["name"] = "killed_pod";
|
||||
}
|
||||
else {
|
||||
deletePods(aliens[i]["name"]);
|
||||
aliens[i]["name"] = "killed_pod";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -175,13 +254,15 @@ function checkRocketAlienCollision(){
|
||||
return false;
|
||||
}
|
||||
|
||||
function shuffleAliens() {
|
||||
pods = pods.sort(() => Math.random() - 0.5)
|
||||
}
|
||||
function drawRocket() {
|
||||
var image = new Image(); // Image constructor
|
||||
image.src = './kuberocket.png';
|
||||
ctx.drawImage(image, rocketX, rocketY, 20, 20);
|
||||
|
||||
ctx.closePath();
|
||||
//console.log("Rocket X: " + rocketX + " Rocket Y: " + rocketY);
|
||||
|
||||
if (checkRocketAlienCollision()) {
|
||||
rocketY = -100;
|
||||
@@ -215,6 +296,7 @@ function drawSpaceship() {
|
||||
}
|
||||
|
||||
function draw() {
|
||||
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
drawSpaceship();
|
||||
|
||||
@@ -258,9 +340,22 @@ function draw() {
|
||||
|
||||
for (i=0; i<aliens.length; i++) {
|
||||
if (aliens[i]["active"]) {
|
||||
drawAlien(aliens[i]["x"], aliens[i]["y"]);
|
||||
drawAlien(aliens[i]["x"], aliens[i]["y"], aliens[i]["name"]);
|
||||
}
|
||||
}
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.font = '15px Verdana';
|
||||
ctx.fillText('Cluster: ' + endpoint, 10, 400);
|
||||
ctx.fillText('Current Namespace: ' + namespace, 10, 420);
|
||||
ctx.fillText('Alien Shuffle: ' + shuffle, 10, 440);
|
||||
ctx.fillText('press \'h\' for help!', 10, 470);
|
||||
|
||||
if (help) {
|
||||
ctx.fillText('Special Keys:', 10, 300);
|
||||
ctx.fillText('h => Activate or deactivate Help', 10, 320);
|
||||
ctx.fillText('s => Activate or deactivate shuffle for aliens', 10, 340);
|
||||
ctx.fillText('n => Change namespace', 10, 360);
|
||||
}
|
||||
}
|
||||
|
||||
function podExists(podName) {
|
||||
@@ -282,6 +377,11 @@ function findReplace() {
|
||||
}
|
||||
|
||||
function setAliens() {
|
||||
if (shuffle) {
|
||||
pods = pods.sort(() => Math.random() - 0.5)
|
||||
}
|
||||
aliens = [];
|
||||
console.log("Length of aliensY array: " + aliensY.length);
|
||||
if (pods.length > 0) {
|
||||
for (k=10; k>0; k--) {
|
||||
if (!aliensY.includes(k)) {
|
||||
@@ -301,7 +401,7 @@ function setAliens() {
|
||||
aliens.push({"name": pods[i], "x": x, "y": y, "active": true});
|
||||
cnt =+ 1;
|
||||
}
|
||||
if (aliens.length == 12) {
|
||||
if (aliens.length % 12 == 0) {
|
||||
console.log("we need another line of aliens for Y="+aliensIncrementY);
|
||||
x = 10;
|
||||
y += aliensIncrementY;
|
||||
@@ -320,6 +420,7 @@ function setAliens() {
|
||||
}
|
||||
}
|
||||
|
||||
getEndpoint();
|
||||
setInterval(draw, 10);
|
||||
setInterval(getKubeItems, 1000);
|
||||
setInterval(setAliens, 1000);
|
||||
|
||||
+57
-26
@@ -2,8 +2,12 @@ server {
|
||||
listen 8080 default_server;
|
||||
root /var/www/html/;
|
||||
index index.html;
|
||||
|
||||
|
||||
location / {
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
|
||||
try_files $uri $uri/ =404;
|
||||
add_header Last-Modified $date_gmt;
|
||||
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
|
||||
@@ -12,12 +16,63 @@ server {
|
||||
etag off;
|
||||
}
|
||||
|
||||
location /kube {
|
||||
rewrite ^/kube(.*)$ /api$1 break;
|
||||
proxy_pass ${ENDPOINT};
|
||||
access_by_lua_file /tmp/metrics.lua;
|
||||
}
|
||||
|
||||
location /kube/api {
|
||||
rewrite ^/kube/api(.*)$ /api$1 break;
|
||||
proxy_pass ${ENDPOINT};
|
||||
access_by_lua_file /tmp/metrics.lua;
|
||||
}
|
||||
|
||||
location /kube/pods {
|
||||
access_by_lua_file "/tmp/pod.lua";
|
||||
}
|
||||
|
||||
location /kube/delete/pods {
|
||||
access_by_lua_file "/tmp/pod.lua";
|
||||
}
|
||||
|
||||
location /kube/nodes {
|
||||
access_by_lua_file "/tmp/node.lua";
|
||||
}
|
||||
|
||||
location /kube/chaos/nodes {
|
||||
access_by_lua_file "/tmp/chaos-node.lua";
|
||||
}
|
||||
|
||||
location /kube/endpoint {
|
||||
content_by_lua_block {
|
||||
ngx.header['Access-Control-Allow-Origin'] = '*'
|
||||
ngx.header['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
|
||||
ngx.header['Access-Control-Allow-Headers'] = 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'
|
||||
ngx.header['Access-Control-Expose-Headers'] = 'Content-Length,Content-Range';
|
||||
ngx.say(os.getenv("ENDPOINT"))
|
||||
}
|
||||
}
|
||||
|
||||
location /kube/namespaces {
|
||||
content_by_lua_block {
|
||||
ngx.header['Access-Control-Allow-Origin'] = '*'
|
||||
ngx.header['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
|
||||
ngx.header['Access-Control-Allow-Headers'] = 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'
|
||||
ngx.header['Access-Control-Expose-Headers'] = 'Content-Length,Content-Range';
|
||||
ngx.say(os.getenv("NAMESPACE"))
|
||||
}
|
||||
}
|
||||
|
||||
location /metrics {
|
||||
default_type text/html;
|
||||
content_by_lua_block {
|
||||
local redis = require "resty.redis"
|
||||
local red = redis:new()
|
||||
|
||||
-- local okredis, errredis = red:connect(os.getenv("REDIS_HOST"), 6379)
|
||||
local okredis, errredis = red:connect("unix:/tmp/redis.sock")
|
||||
|
||||
for i, res in ipairs(red:keys("*")) do
|
||||
ngx.log(ngx.ERR, res)
|
||||
|
||||
@@ -42,34 +97,10 @@ server {
|
||||
content_by_lua_block {
|
||||
local arg = ngx.req.get_uri_args()
|
||||
ngx.req.read_body()
|
||||
local request_body = ngx.req.get_body_data()
|
||||
local request_body = ngx.req.get_body_data()
|
||||
local handle = io.popen("/opt/kube-linter-parser.sh " .. arg['k8s_url'] .. " " .. arg['namespace'] .. " " .. arg['pod_name'] .. " " .. arg['token'])
|
||||
local result = handle:read("*a")
|
||||
ngx.say(result)
|
||||
}
|
||||
}
|
||||
|
||||
location /kube/chaos-node {
|
||||
default_type text/html;
|
||||
access_by_lua_file /tmp/metrics.lua;
|
||||
content_by_lua_block {
|
||||
local arg = ngx.req.get_uri_args()
|
||||
ngx.req.read_body()
|
||||
local request_body = ngx.req.get_body_data()
|
||||
os.execute("/opt/chaos-node.sh " .. arg['k8s_url'] .. " " .. arg['token'] .. " " .. arg['node_name'] .. " " .. arg['namespace'] .. " &")
|
||||
ngx.say('chaos_node started!')
|
||||
}
|
||||
}
|
||||
|
||||
location /kube {
|
||||
rewrite ^/kube(.*)$ /api$1 break;
|
||||
proxy_pass https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_HTTPS};
|
||||
access_by_lua_file /tmp/metrics.lua;
|
||||
}
|
||||
|
||||
location /kube/api {
|
||||
rewrite ^/kube/api(.*)$ /api$1 break;
|
||||
proxy_pass https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_HTTPS};
|
||||
access_by_lua_file /tmp/metrics.lua;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
# lua_package_path '/usr/share/lua/5.1/?.lua;;';
|
||||
|
||||
server {
|
||||
listen 8080 default_server;
|
||||
root /var/www/html/;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
|
||||
try_files $uri $uri/ =404;
|
||||
add_header Last-Modified $date_gmt;
|
||||
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
|
||||
@@ -12,6 +18,54 @@ server {
|
||||
etag off;
|
||||
}
|
||||
|
||||
location /kube {
|
||||
rewrite ^/kube(.*)$ /api$1 break;
|
||||
proxy_pass ${ENDPOINT};
|
||||
access_by_lua_file /tmp/metrics.lua;
|
||||
}
|
||||
|
||||
location /kube/api {
|
||||
rewrite ^/kube/api(.*)$ /api$1 break;
|
||||
proxy_pass ${ENDPOINT};
|
||||
access_by_lua_file /tmp/metrics.lua;
|
||||
}
|
||||
|
||||
location /kube/pods {
|
||||
access_by_lua_file "/tmp/pod.lua";
|
||||
}
|
||||
|
||||
location /kube/delete/pods {
|
||||
access_by_lua_file "/tmp/pod.lua";
|
||||
}
|
||||
|
||||
location /kube/nodes {
|
||||
access_by_lua_file "/tmp/node.lua";
|
||||
}
|
||||
|
||||
location /kube/chaos/nodes {
|
||||
access_by_lua_file "/tmp/chaos-node.lua";
|
||||
}
|
||||
|
||||
location /kube/endpoint {
|
||||
content_by_lua_block {
|
||||
ngx.header['Access-Control-Allow-Origin'] = '*'
|
||||
ngx.header['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
|
||||
ngx.header['Access-Control-Allow-Headers'] = 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'
|
||||
ngx.header['Access-Control-Expose-Headers'] = 'Content-Length,Content-Range';
|
||||
ngx.say(os.getenv("ENDPOINT"))
|
||||
}
|
||||
}
|
||||
|
||||
location /kube/namespaces {
|
||||
content_by_lua_block {
|
||||
ngx.header['Access-Control-Allow-Origin'] = '*'
|
||||
ngx.header['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
|
||||
ngx.header['Access-Control-Allow-Headers'] = 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'
|
||||
ngx.header['Access-Control-Expose-Headers'] = 'Content-Length,Content-Range';
|
||||
ngx.say(os.getenv("NAMESPACE"))
|
||||
}
|
||||
}
|
||||
|
||||
location /metrics {
|
||||
default_type text/html;
|
||||
content_by_lua_block {
|
||||
@@ -51,29 +105,4 @@ server {
|
||||
ngx.say(result)
|
||||
}
|
||||
}
|
||||
|
||||
location /kube/chaos-node {
|
||||
default_type text/html;
|
||||
access_by_lua_file /tmp/metrics.lua;
|
||||
|
||||
content_by_lua_block {
|
||||
local arg = ngx.req.get_uri_args()
|
||||
ngx.req.read_body()
|
||||
local request_body = ngx.req.get_body_data()
|
||||
os.execute("/opt/chaos-node.sh " .. arg['k8s_url'] .. " " .. arg['token'] .. " " .. arg['node_name'] .. " " .. arg['namespace'] .. " &")
|
||||
ngx.say('chaos_node started!')
|
||||
}
|
||||
}
|
||||
|
||||
location /kube {
|
||||
rewrite ^/kube(.*)$ /api$1 break;
|
||||
proxy_pass ${ENDPOINT};
|
||||
access_by_lua_file /tmp/metrics.lua;
|
||||
}
|
||||
|
||||
location /kube/api {
|
||||
rewrite ^/kube/api(.*)$ /api$1 break;
|
||||
proxy_pass ${ENDPOINT};
|
||||
access_by_lua_file /tmp/metrics.lua;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ worker_processes 2;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
env REDIS_HOST;
|
||||
env TOKEN;
|
||||
env ENDPOINT;
|
||||
env NAMESPACE;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
local https = require "ssl.https"
|
||||
local ltn12 = require "ltn12"
|
||||
local json = require 'lunajson'
|
||||
|
||||
local k8s_url = os.getenv("ENDPOINT")
|
||||
local token = os.getenv("TOKEN")
|
||||
local arg = ngx.req.get_uri_args()
|
||||
local url = k8s_url.. "/api/v1/nodes"
|
||||
local decoded = nil
|
||||
local nodes = {}
|
||||
|
||||
ngx.header['Access-Control-Allow-Origin'] = '*'
|
||||
ngx.header['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
|
||||
ngx.header['Access-Control-Allow-Headers'] = 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'
|
||||
ngx.header['Access-Control-Expose-Headers'] = 'Content-Length,Content-Range';
|
||||
|
||||
ngx.log(ngx.ERR, "token: " .. token)
|
||||
ngx.log(ngx.ERR, "url: " .. url)
|
||||
|
||||
local headers = {
|
||||
["Accept"] = "application/json",
|
||||
["Content-Type"] = "application/json",
|
||||
["Authorization"] = "Bearer " .. token,
|
||||
}
|
||||
|
||||
local resp = {}
|
||||
|
||||
local ok, statusCode, headers, statusText = https.request{
|
||||
url = url,
|
||||
headers = headers,
|
||||
method = "GET",
|
||||
sink = ltn12.sink.table(resp)
|
||||
}
|
||||
|
||||
ngx.log(ngx.ERR, "REQUEST LOGS...")
|
||||
ngx.log(ngx.ERR, ok)
|
||||
ngx.log(ngx.ERR, statusCode)
|
||||
ngx.log(ngx.ERR, statusText)
|
||||
|
||||
local i = 0
|
||||
nodes["items"] = {}
|
||||
|
||||
for k,v in ipairs(resp) do
|
||||
decoded = json.decode(v)
|
||||
if decoded["kind"] == "NodeList" then
|
||||
for k2,v2 in ipairs(decoded["items"]) do
|
||||
if not v2["metadata"]["labels"]["node-role.kubernetes.io/master"] then
|
||||
ngx.log(ngx.ERR, "found node " .. v2["metadata"]["name"])
|
||||
nodes["items"][i] = v2["metadata"]["name"]
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ngx.say(json.encode(nodes))
|
||||
@@ -0,0 +1,84 @@
|
||||
local https = require "ssl.https"
|
||||
local ltn12 = require "ltn12"
|
||||
local json = require 'lunajson'
|
||||
|
||||
local k8s_url = os.getenv("ENDPOINT")
|
||||
local token = os.getenv("TOKEN")
|
||||
local arg = ngx.req.get_uri_args()
|
||||
local namespace = arg['namespace']
|
||||
local decoded = nil
|
||||
local action = arg['action']
|
||||
local url = ''
|
||||
local pods = {}
|
||||
local method = 'GET'
|
||||
local pods_not_found = true;
|
||||
|
||||
ngx.header['Access-Control-Allow-Origin'] = '*'
|
||||
ngx.header['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
|
||||
ngx.header['Access-Control-Allow-Headers'] = 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'
|
||||
ngx.header['Access-Control-Expose-Headers'] = 'Content-Length,Content-Range';
|
||||
|
||||
if action == "list" then
|
||||
url = k8s_url.. "/api/v1/namespaces/" .. namespace .. "/pods"
|
||||
|
||||
elseif action == "delete" then
|
||||
local pod_name = arg['pod_name']
|
||||
url = k8s_url.. "/api/v1/namespaces/" .. namespace .. "/pods/" .. pod_name
|
||||
method = "DELETE"
|
||||
|
||||
else
|
||||
ngx.say("Please set the parameter 'action'")
|
||||
ngx.exit(ngx.OK)
|
||||
end
|
||||
|
||||
ngx.log(ngx.ERR, "token: " .. token)
|
||||
ngx.log(ngx.ERR, "url: " .. url)
|
||||
ngx.log(ngx.ERR, "namespace: " .. namespace)
|
||||
|
||||
local headers = {
|
||||
["Accept"] = "application/json",
|
||||
["Content-Type"] = "application/json",
|
||||
["Authorization"] = "Bearer " .. token,
|
||||
}
|
||||
|
||||
local resp = {}
|
||||
|
||||
local ok, statusCode, headers, statusText = https.request{
|
||||
url = url,
|
||||
headers = headers,
|
||||
method = method,
|
||||
sink = ltn12.sink.table(resp)
|
||||
}
|
||||
|
||||
ngx.log(ngx.ERR, "REQUEST LOGS...")
|
||||
ngx.log(ngx.ERR, ok)
|
||||
ngx.log(ngx.ERR, statusCode)
|
||||
ngx.log(ngx.ERR, statusText)
|
||||
|
||||
if action == "list" then
|
||||
local i = 1
|
||||
pods["items"] = {}
|
||||
for k,v in ipairs(resp) do
|
||||
decoded = json.decode(v)
|
||||
if decoded["kind"] == "PodList" then
|
||||
for k2,v2 in ipairs(decoded["items"]) do
|
||||
if v2["status"]["phase"] == "Running" and v2["metadata"]["labels"]["approle"] ~= "chaosnode" then
|
||||
ngx.log(ngx.ERR, "found pod " .. v2["metadata"]["name"])
|
||||
pods["items"][i] = v2["metadata"]["name"]
|
||||
i = i + 1
|
||||
pods_not_found = false;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if pods_not_found then
|
||||
ngx.log(ngx.ERR, "No pods found into the namespace" .. namespace)
|
||||
ngx.say("{\"items\": []}")
|
||||
else
|
||||
ngx.say(json.encode(pods))
|
||||
end
|
||||
|
||||
elseif action == "delete" then
|
||||
ngx.say(table.concat(resp))
|
||||
end
|
||||
Reference in New Issue
Block a user