diff --git a/scripts/chaos-containers.lua b/scripts/chaos-containers.lua new file mode 100644 index 0000000..61f84e9 --- /dev/null +++ b/scripts/chaos-containers.lua @@ -0,0 +1,40 @@ +local arg = ngx.req.get_uri_args() +local action = arg['action'] +local config = require "config_kubeinv" +local redis = require "resty.redis" +local red = redis:new() +local okredis, errredis = red:connect("unix:/tmp/redis.sock") + +if okredis then + ngx.log(ngx.ERR, "Connection to Redis is ok") +else + ngx.log(ngx.ERR, "Connection to Redis is not ok") + ngx.log(ngx.ERR, errredis) +end + +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 ngx.var.request_method == "GET" and action == 'default' then + ngx.say(config['default_chaos_container']) + +elseif ngx.var.request_method == "GET" and action == 'chaos_container' then + local res, err = red:get("chaos_container") + if res == ngx.null then + ngx.say('A custom chaos container has not yet been configured') + else + ngx.say(res) + end + +elseif ngx.var.request_method == "POST" and action == 'set' then + ngx.say('New chaos container has been configured') + + if arg['chaos_container'] == nil then + ngx.say("Please set the parameter 'chaos_container'") + else + red:set("chaos_container", arg['chaos_container']) + ngx.say("chaos_container has been configured in Redis") + end +end \ No newline at end of file diff --git a/scripts/chaos-node.lua b/scripts/chaos-node.lua new file mode 100644 index 0000000..7ef4166 --- /dev/null +++ b/scripts/chaos-node.lua @@ -0,0 +1,194 @@ +loadfile("/usr/local/openresty/nginx/conf/kubeinvaders/metrics.lua") + +local https = require "ssl.https" +local ltn12 = require "ltn12" +local json = require "lunajson" +local redis = require "resty.redis" +local arg = ngx.req.get_uri_args() +local incr = 0 +local config = require "config_kubeinv" +local chaos_container = "" + +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() + +if os.getenv("KUBERNETES_SERVICE_HOST") then + k8s_url = "https://" .. os.getenv("KUBERNETES_SERVICE_HOST") .. ":" .. os.getenv("KUBERNETES_SERVICE_PORT_HTTPS") +else + k8s_url = os.getenv("ENDPOINT") +end + +local token = os.getenv("TOKEN") +local namespace = arg['namespace'] +local node_name = arg['nodename'] +local url = k8s_url .. "/apis/batch/v1/namespaces/" .. namespace .. "/jobs" +local resp = {} + +if ngx.var.request_method == "GET" then + local red = redis:new() + local okredis, errredis = red:connect("unix:/tmp/redis.sock") + + if okredis then + ngx.log(ngx.ERR, "Connection to Redis is ok") + else + ngx.log(ngx.ERR, "Connection to Redis is not ok") + ngx.log(ngx.ERR, errredis) + end + -- Count the total of chaos jobs launched against nodes + local chaos_node_res, err = red:get("chaos_node_jobs_total") + + if chaos_node_res == ngx.null then + ngx.say(err) + red:set("chaos_node_jobs_total", 0) + else + local incr = chaos_node_res + 1 + local res, err = red:set("chaos_node_jobs_total",incr) + end + + -- Count the total of chaos jobs launched against nodes per node + local node_name = arg['nodename'] + local chaos_node_res, err = red:get("chaos_node_jobs_total_on_" .. node_name) + if chaos_node_res == ngx.null then + ngx.say(err) + red:set("chaos_node_jobs_total_on_" .. node_name, 1) + else + local incr = chaos_node_res + 1 + local res, err = red:set("chaos_node_jobs_total_on_" .. node_name,incr) + end +end + +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, +} + +local res, err = red:get("chaos_container") + +if res == ngx.null then + ngx.log(ngx.ERR, "Found chaos_container defined in Redis!") + ngx.log(ngx.ERR, res) + chaos_container = res +else + ngx.log(ngx.ERR, "Using default chaos container") + chaos_container = config["default_chaos_container"] +end + +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": [ ]] .. chaos_container .. [[ ], + "restartPolicy": "Never" + } + }, + "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") diff --git a/scripts/config_kubeinv.lua b/scripts/config_kubeinv.lua new file mode 100644 index 0000000..fb7d6d3 --- /dev/null +++ b/scripts/config_kubeinv.lua @@ -0,0 +1,23 @@ +local config = {} +config['default_chaos_container'] = [[ +{ + "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" + ] +} +]] + +return config \ No newline at end of file diff --git a/scripts/metrics.lua b/scripts/metrics.lua new file mode 100644 index 0000000..d524060 --- /dev/null +++ b/scripts/metrics.lua @@ -0,0 +1,77 @@ +local redis = require "resty.redis" +local arg = ngx.req.get_uri_args() +local incr = 0 +if ngx.var.request_method == "DELETE" then + local red = redis:new() + local okredis, errredis = red:connect("unix:/tmp/redis.sock") + + if okredis then + ngx.log(ngx.ERR, "Connection to Redis is ok") + else + ngx.log(ngx.ERR, "Connection to Redis is not ok") + ngx.log(ngx.ERR, errredis) + end + -- Count the total of deleted pods + local res, err = red:get("deleted_pods_total") + + if res == ngx.null then + ngx.say(err) + red:set("deleted_pods_total", 1) + else + incr = res + 1 + red:set("deleted_pods_total", incr) + end + + -- TODO: Make a better regular expression! + local namespace_pods = string.match(ngx.var.request_uri, '^.*/namespaces/(.*)/.*$') + local namespace = namespace_pods:gsub("/pods", "") + + -- Count the total of deleted pods for namespace + local res, err = red:get("deleted_pods_total_on_" .. namespace) + + if res == ngx.null then + red:set("deleted_pods_total_on_" .. namespace, 1) + else + incr = res + 1 + red:set("deleted_pods_total_on_" .. namespace, incr) + end + + +elseif ngx.var.request_method == "GET" and string.match(ngx.var.request_uri, "^.*/chaos[-]node.*$") then + local red = redis:new() + local okredis, errredis = red:connect("unix:/tmp/redis.sock") + + if okredis then + ngx.log(ngx.ERR, "Connection to Redis is ok") + else + ngx.log(ngx.ERR, "Connection to Redis is not ok") + ngx.log(ngx.ERR, errredis) + end + -- Count the total of chaos jobs launched against nodes + local chaos_node_res, err = red:get("chaos_node_jobs_total") + + if chaos_node_res == ngx.null then + ngx.say(err) + red:set("chaos_node_jobs_total", 0) + else + local incr = chaos_node_res + 1 + local res, err = red:set("chaos_node_jobs_total",incr) + end + + -- Count the total of chaos jobs launched against nodes per node + local node_name = arg['node_name'] + local chaos_node_res, err = red:get("chaos_node_jobs_total_on_" .. node_name) + if chaos_node_res == ngx.null then + ngx.say(err) + red:set("chaos_node_jobs_total_on_" .. node_name, 1) + else + local incr = chaos_node_res + 1 + local res, err = red:set("chaos_node_jobs_total_on_" .. node_name,incr) + end + +elseif ngx.var.request_method == "GET" and ngx.var.request_uri == "/metrics" then + for i, res in ipairs(red:keys("*")) do + ngx.log(ngx.ERR, res) + ngx.say(res .. " " .. red:get(res)) + end +end diff --git a/scripts/node.lua b/scripts/node.lua new file mode 100644 index 0000000..2ab8d90 --- /dev/null +++ b/scripts/node.lua @@ -0,0 +1,98 @@ +local https = require "ssl.https" +local ltn12 = require "ltn12" +local json = require 'lunajson' +local redis = require "resty.redis" + +if os.getenv("KUBERNETES_SERVICE_HOST") then + k8s_url = "https://" .. os.getenv("KUBERNETES_SERVICE_HOST") .. ":" .. os.getenv("KUBERNETES_SERVICE_PORT_HTTPS") +else + k8s_url = os.getenv("ENDPOINT") +end + +local token = os.getenv("TOKEN") +local arg = ngx.req.get_uri_args() +local url = k8s_url.. "/api/v1/nodes" +local decoded = nil +local nodes = {} + + +if ngx.var.request_method == "GET" and string.match(ngx.var.request_uri, "^.*/chaos[-]node.*$") then + local red = redis:new() + local okredis, errredis = red:connect("unix:/tmp/redis.sock") + + if okredis then + ngx.log(ngx.ERR, "Connection to Redis is ok") + else + ngx.log(ngx.ERR, "Connection to Redis is not ok") + ngx.log(ngx.ERR, errredis) + end + -- Count the total of chaos jobs launched against nodes + local chaos_node_res, err = red:get("chaos_node_jobs_total") + + if chaos_node_res == ngx.null then + ngx.say(err) + red:set("chaos_node_jobs_total", 0) + else + local incr = chaos_node_res + 1 + local res, err = red:set("chaos_node_jobs_total",incr) + end + + -- Count the total of chaos jobs launched against nodes per node + local node_name = arg['node_name'] + local chaos_node_res, err = red:get("chaos_node_jobs_total_on_" .. node_name) + if chaos_node_res == ngx.null then + ngx.say(err) + red:set("chaos_node_jobs_total_on_" .. node_name, 1) + else + local incr = chaos_node_res + 1 + local res, err = red:set("chaos_node_jobs_total_on_" .. node_name,incr) + end +end + + +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 + -- TODO: masters should be included? + -- 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)) diff --git a/scripts/pod.lua b/scripts/pod.lua new file mode 100644 index 0000000..796b746 --- /dev/null +++ b/scripts/pod.lua @@ -0,0 +1,116 @@ +local https = require "ssl.https" +local ltn12 = require "ltn12" +local json = require 'lunajson' +local redis = require "resty.redis" +local incr = 0 + +if os.getenv("KUBERNETES_SERVICE_HOST") then + k8s_url = "https://" .. os.getenv("KUBERNETES_SERVICE_HOST") .. ":" .. os.getenv("KUBERNETES_SERVICE_PORT_HTTPS") +else + k8s_url = os.getenv("ENDPOINT") +end + +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 == "delete" then + local red = redis:new() + local okredis, errredis = red:connect("unix:/tmp/redis.sock") + + if okredis then + ngx.log(ngx.ERR, "Connection to Redis is ok") + else + ngx.log(ngx.ERR, "Connection to Redis is not ok") + ngx.log(ngx.ERR, errredis) + end + -- Count the total of deleted pods + + -- Count the total of deleted pods for namespace + local res, err = red:get("deleted_pods_total_on_" .. namespace) + + if res == ngx.null then + red:set("deleted_pods_total_on_" .. namespace, 1) + else + incr = res + 1 + red:set("deleted_pods_total_on_" .. namespace, incr) + end +end + + +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