Files
kubeinvaders/nginx/KubeInvaders.conf
2024-12-31 10:11:25 +00:00

705 lines
25 KiB
Plaintext

# lua_package_path '/usr/share/lua/5.1/?.lua;;';
server {
listen 8080 default_server;
root /var/www/html/;
index index.html;
access_log off;
set_by_lua_block $application_url {
return os.getenv("APPLICATION_URL")
}
set_by_lua_block $disable_tls {
local env_var = os.getenv("DISABLE_TLS")
if env_var then
return env_var
else
return "false"
end
}
set_by_lua_block $demo_mode {
local env_var = os.getenv("PLATFORM_ENGINEERING_DEMO_MODE")
if env_var then
return env_var
else
return "false"
end
}
set_by_lua_block $selected_env_vars {
local selected_env_vars = {"KUBERNETES_SERVICE_HOST", "KUBERNETES_SERVICE_PORT_HTTPS", "NAMESPACE", "DISABLE_TLS", "APPLICATION_URL"}
local result = {}
for _, var_name in ipairs(selected_env_vars) do
local value = os.getenv(var_name)
if value then
table.insert(result, var_name .. " = " .. value)
else
table.insert(result, var_name .. " = nil")
end
end
return table.concat(result, ", ")
}
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;
if_modified_since off;
expires off;
etag off;
sub_filter_types "*";
sub_filter demo_mode_placeholder $demo_mode;
sub_filter application_url_placeholder $application_url;
sub_filter disable_tls_placeholder $disable_tls;
sub_filter selected_env_vars_placeholder $selected_env_vars;
}
location /kube/ingresses {
access_by_lua_file "/usr/local/openresty/nginx/conf/kubeinvaders/ingress.lua";
}
location /kube/vm {
access_by_lua_file "/usr/local/openresty/nginx/conf/kubeinvaders/vm.lua";
}
location /kube/vm_reboot {
access_by_lua_file "/usr/local/openresty/nginx/conf/kubeinvaders/vm_reboot.lua";
}
location /kube/chaos/programming_mode {
access_by_lua_file "/usr/local/openresty/nginx/conf/kubeinvaders/programming_mode.lua";
}
location /kube/pods {
access_by_lua_file "/usr/local/openresty/nginx/conf/kubeinvaders/pod.lua";
}
location /kube/delete/pods {
access_by_lua_file "/usr/local/openresty/nginx/conf/kubeinvaders/pod.lua";
}
location /kube/nodes {
access_by_lua_file "/usr/local/openresty/nginx/conf/kubeinvaders/node.lua";
}
location /kube/chaos/nodes {
access_by_lua_file "/usr/local/openresty/nginx/conf/kubeinvaders/chaos-node.lua";
}
location /kube/kube-linter {
access_by_lua_file "/usr/local/openresty/nginx/conf/kubeinvaders/kube-linter.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';
if os.getenv("APPLICATION_URL") == nil then
ngx.say("Error! APPLICATION_URL is nil!")
else
ngx.say(os.getenv("APPLICATION_URL"))
end
}
}
location /chaos/redis/get {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local okredis, errredis = red:connect("unix:/tmp/redis.sock")
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.req.read_body()
local data = ngx.req.get_body_data()
local args = ngx.req.get_uri_args()
local key = args["key"]
if red:exists(key) == 0 then
ngx.say("Key not found")
else
ngx.say(tostring(red:get(key)))
end
}
}
location /chaos/redis/set {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local okredis, errredis = red:connect("unix:/tmp/redis.sock")
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.req.read_body()
local data = ngx.req.get_body_data()
local args = ngx.req.get_uri_args()
local key = args["key"]
ngx.log(ngx.INFO, "[KUBEPING] Setting key " .. key .. " with data |" .. tostring(data) .. "|")
ngx.say(tostring(red:set(key, tostring(data))))
if key == "kubeping" and data == "1" then
local http = require("socket.http")
local function get_data(url)
local respbody = {}
local code, response = http.request(url)
if code == 200 then
local json = require("json")
respbody = json.decode(response)
else
print("Errore nella richiesta: " .. code)
end
return respbody
end
local data = get_data("https://devopstribe.it/kubeping?msg=" .. args["msg"])
print(data.result)
end
ngx.say("OK")
}
}
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';
-- TO DO
-- Check when NAMESPACE is nil
ngx.say(os.getenv("NAMESPACE"))
}
}
location /codename {
content_by_lua_block {
local open = io.open
local function read_rand_line_from_file(path)
local handle = io.popen("shuf -n 1 " .. path)
local result = handle:read("*a")
local rc = handle:close()
return result
end
local random_word = read_rand_line_from_file("/usr/local/openresty/nginx/conf/kubeinvaders/data/codenames.txt")
local redis = require "resty.redis"
local red = redis:new()
local okredis, errredis = red:connect("unix:/tmp/redis.sock")
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';
while (red:get("latest_codename") == random_word)
do
random_word = read_rand_line_from_file("/usr/local/openresty/nginx/conf/kubeinvaders/data/codenames.txt")
end
red:set("latest_codename", random_word)
-- ngx.log(ngx.INFO, "[programming_mode_codename] Choosing random word " .. random_word)
ngx.say(random_word)
}
}
location /metrics {
default_type text/html;
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local okredis, errredis = red:connect("unix:/tmp/redis.sock")
for i, res in ipairs(red:keys("*total*")) do
if string.find(res, "chaos_node_jobs_total_on") then
local node = string.gsub(res, "chaos_node_jobs_total_on_", "")
local metric = "chaos_jobs_node_count{node=\"".. node .."\"}"
ngx.say(metric .. " " .. red:get(res))
elseif string.find(res, "deleted_pods_total_on") then
local namespace = string.gsub(res, "deleted_pods_total_on_", "")
local metric = "deleted_namespace_pods_count{namespace=\"".. namespace .."\"}"
ngx.say(metric .. " " .. red:get(res))
end
end
for i, res in ipairs(red:keys("pods_match_regex:*")) do
ngx.say(res .. " " .. red:get(res))
end
local metrics = {
'chaos_node_jobs_total',
'deleted_pods_total',
'fewer_replicas_seconds',
'latest_fewer_replicas_seconds',
'pods_not_running_on_selected_ns',
'current_chaos_job_pod',
'pods_match_regex'
}
local metric_name = ""
local metric_value = ""
for key, value in ipairs(metrics) do
metric_name = value
if (red:exists(metric_name) == 1) then
metric_value = red:get(metric_name)
ngx.say(metric_name .. " " .. metric_value)
end
end
for i, res in ipairs(red:keys("chaos_jobs_status*")) do
ngx.say(res .. " " .. red:get(res))
end
}
}
location /chaos_jobs_pod_phase {
default_type text/html;
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local okredis, errredis = red:connect("unix:/tmp/redis.sock")
for i, res in ipairs(red:keys("chaos_jobs_pod_phase*")) do
ngx.say(res .. " " .. red:get(res))
end
}
}
location /kube/chaos/containers {
lua_need_request_body 'on';
access_by_lua_file "/usr/local/openresty/nginx/conf/kubeinvaders/chaos-containers.lua";
}
location /chaos/logs/keepalive {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local args = ngx.req.get_uri_args()
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.req.read_body()
local data = ngx.req.get_body_data()
local okredis, errredis = red:connect("unix:/tmp/redis.sock")
local logid = args["logid"]
red:set("do_not_clean_log:" .. logid, "1")
red:expire("do_not_clean_log:" .. logid, "20")
red:set("logs_enabled:" .. logid, "1")
red:expire("logs_enabled:" .. logid, "10")
if red:exists("log_status:".. logid) then
ngx.say(red:get("log_status:".. logid))
else
ngx.say('Waiting for log collector status...')
end
}
}
location /chaos/logs/count {
default_type text/html;
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local args = ngx.req.get_uri_args()
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.req.read_body()
local data = ngx.req.get_body_data()
local okredis, errredis = red:connect("unix:/tmp/redis.sock")
local logid = args["logid"]
local res, err = red:get("logs:chaoslogs-" .. logid .. "-count")
if res == ngx.null then
ngx.say("0")
else
ngx.say(res)
end
}
}
location /chaos/logs {
default_type text/html;
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local args = ngx.req.get_uri_args()
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.req.read_body()
local data = ngx.req.get_body_data()
local okredis, errredis = red:connect("unix:/tmp/redis.sock")
local logid = args["logid"]
local pos = args["pos"]
local res = red:get("logs:chaoslogs-" .. logid .. "-" .. pos)
ngx.say(res)
}
}
location /chaos/loadpreset {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local args = ngx.req.get_uri_args()
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.req.read_body()
local okredis, errredis = red:connect("unix:/tmp/redis.sock")
local key_name = args["lang"] .. "_" .. args["name"]
local res, err = red:get(key_name)
if res == ngx.null then
ngx.say(err)
else
ngx.say(res)
end
}
}
location /chaos/loadpreset/reset {
content_by_lua_block {
local function mysplit (inputstr, sep)
if sep == nil then
sep = "%s"
end
local t={}
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
table.insert(t, str)
end
return t
end
local redis = require "resty.redis"
local red = redis:new()
local args = ngx.req.get_uri_args()
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.req.read_body()
local okredis, errredis = red:connect("unix:/tmp/redis.sock")
local key_name = args["lang"] .. "_" .. args["name"]
-- ngx.log(ngx.INFO, "[PRESETS-RESET] The item " .. key_name .. " should be removed from the key presets_list")
local handle = io.popen("redis-cli --scan --pattern " .. key_name .. " | xargs redis-cli del")
local result = handle:read("*a")
local rc = handle:close()
local res = red:get("presets_list")
if res == ngx.null then
ngx.say("There are no presets saved yet")
else
local preset = "";
local new_preset_list = "";
local cnt = 0;
for key, value in ipairs(mysplit(res, ",")) do
-- ngx.log(ngx.INFO, "[PRESETS-RESET] Split the key presets_list. Current preset is: " .. value)
if value ~= key_name then
preset = value
if cnt > 0 then
new_preset_list = new_preset_list .. "," .. preset
else
new_preset_list = preset
end
end
cnt = cnt + 1
end
-- ngx.log(ngx.INFO, "[PRESETS-RESET] New key presets_list is: " .. new_preset_list)
red:set("presets_list", new_preset_list)
end
}
}
location /chaos/loadpreset/savedpresets {
default_type text/html;
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local args = ngx.req.get_uri_args()
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';
local okredis, errredis = red:connect("unix:/tmp/redis.sock")
local res, err = red:get("presets_list")
if res == ngx.null then
ngx.say(err)
else
ngx.say(res)
-- ngx.log(ngx.INFO, "[PRESETS_LIST] " .. res)
end
}
}
location /chaos/report/keepalive {
content_by_lua_block {
local function check_if_string_contain_project(project_list, project)
if string.find(project_list, project) then
return true
end
return false
end
local function add_project(project_list, new_project)
local new_project_list = project_list .. "," .. new_project
return new_project_list
end
local function check_if_redis_key_exists(redis, key)
if redis:exists(key) == 1 then
return true
end
return false
end
local redis = require "resty.redis"
local red = redis:new()
local args = ngx.req.get_uri_args()
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.req.read_body()
local data = ngx.req.get_body_data()
local okredis, errredis = red:connect("unix:/tmp/redis.sock")
if check_if_redis_key_exists(red, "chaos_report_project_list") then
if not check_if_string_contain_project(red:get("chaos_report_project_list"), args["project"]) then
red:set("chaos_report_project_list", add_project(red:get("chaos_report_project_list"), args["project"]))
end
else
red:set("chaos_report_project_list", args["project"])
end
red:expire("chaos_report_project_list", 5)
}
}
location /chaos/report/save {
content_by_lua_block {
local function check_if_string_contain_project(project_list, project)
if string.find(project_list, project) then
return true
end
return false
end
local function add_project(project_list, new_project)
local new_project_list = project_list .. "," .. new_project
return new_project_list
end
local function check_if_redis_key_exists(redis, key)
if redis:exists(key) == 1 then
return true
end
return false
end
local redis = require "resty.redis"
local red = redis:new()
local args = ngx.req.get_uri_args()
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.req.read_body()
local data = ngx.req.get_body_data()
local okredis, errredis = red:connect("unix:/tmp/redis.sock")
red:set("chaos_report_project_" .. args["project"], data)
if check_if_redis_key_exists(red, "chaos_report_project_list") then
if not check_if_string_contain_project(red:get("chaos_report_project_list"), args["project"]) then
red:set("chaos_report_project_list", add_project(red:get("chaos_report_project_list"), args["project"]))
end
else
red:set("chaos_report_project_list", args["project"])
end
red:expire("chaos_report_project_list", 5)
-- ngx.log(ngx.INFO, "[CHAOS-REPORT-SAVE] " .. data)
}
}
location /chaos/loadpreset/save {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local args = ngx.req.get_uri_args()
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.req.read_body()
local data = ngx.req.get_body_data()
local okredis, errredis = red:connect("unix:/tmp/redis.sock")
local key_name = args["lang"] .. "_" .. args["name"]
local all_presets_key = ""
if data ~= nil then
-- ngx.log(ngx.INFO, "[SAVE-PRESETS] lang:" .. args["lang"] .. ", name:" .. args["name"] .. ", payload:" .. data .. "\n key_name:" .. key_name .. "\n key_exists: " .. red:exists(key_name))
-- if (red:exists(key_name) == 0) then
red:set(key_name, data)
-- ngx.log(ngx.INFO, "[SAVE-PRESETS] key_name:" .. key_name .. ", data:" .. data)
local res, err = red:get(key_name)
if res == ngx.null then
ngx.say("error: " .. args["name"] .. " has not been saved.")
else
local file = io.open("/var/www/html/" .. key_name, "w")
io.output(file)
io.write(data)
io.close(file)
ngx.say(args["name"] .. " has been saved.")
local redis_presets_list = red:get("presets_list")
if redis_presets_list == ngx.null then
red:set("presets_list", key_name)
else
local presets_list = redis_presets_list .. "," .. key_name
red:set("presets_list", presets_list)
end
end
-- end
else
ngx.say("Error in payload sent by web interface.")
end
}
}
location /chaos/programs/json-flow {
content_by_lua_block {
local function key_exists(data, mykey)
for key, value in pairs(data) do
if key == mykey then
return true
end
end
return false
end
local function is_key_empty(data, mykey)
local cnt = 0
for key, value in pairs(data[mykey]) do
cnt = cnt + 1
end
if cnt > 0 then
return false
end
return true
end
local lyaml = require "lyaml"
local json = require 'lunajson'
math.randomseed(os.clock()*100000000000)
local rand = math.random(999, 9999)
local file_name = "/tmp/chaosprogram" .. rand
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.req.read_body()
local data = ngx.req.get_body_data()
ngx.log(ngx.INFO, "[PROGRAMMING_MODE] Chaos program payload sent from client: " .. data)
ngx.log(ngx.INFO, "[PROGRAMMING_MODE] Write temp file: " .. file_name)
local yamlfile = io.open(file_name, "w")
yamlfile:write(data)
yamlfile:close()
ngx.log(ngx.INFO, "[PROGRAMMING_MODE] Checking if " .. file_name .. " is a valid YAML file")
local handle = io.popen("python3 -c 'import yaml, sys; print(yaml.safe_load(sys.stdin))' < " .. file_name .. " ; echo $? > " .. file_name .. ".check")
local result = handle:read("*a")
handle = io.popen("cat " .. file_name .. ".check")
result = handle:read("*a")
ngx.log(ngx.INFO, "[PROGRAMMING_MODE] Exit code for checking YAML syntax of " .. file_name .. " is " .. result)
if result == "0\n" then
ngx.log(ngx.INFO, "[PROGRAMMING_MODE] YAML Syntax is OK")
else
ngx.log(ngx.INFO, "[PROGRAMMING_MODE] YAML Syntax is NOT OK")
handle = io.popen("rm -f " .. file_name .. ".check")
handle:read("*a")
ngx.status = 400
ngx.say("Invalid YAML Chaos Program")
end
if data == nil then
error = "[PROGRAMMING_MODE] No chaos program already loaded."
ngx.status = 400
ngx.say(error)
else
local yaml_data = lyaml.load(data)
if not key_exists(yaml_data, "k8s_jobs") then
error = "[PROGRAMMING_MODE] Chaos program does not contain 'jobs' key."
ngx.status = 400
ngx.say(error)
elseif not key_exists(yaml_data, "experiments") then
error = "[PROGRAMMING_MODE] Chaos program does not contain 'experiments' key."
ngx.status = 400
ngx.say(error)
elseif is_key_empty(yaml_data, "k8s_jobs") then
error = "[PROGRAMMING_MODE] Chaos program does not contain valid 'jobs' key."
ngx.status = 400
ngx.say(error)
elseif is_key_empty(yaml_data, "experiments") then
error = "[PROGRAMMING_MODE] Chaos program does not contain valid 'experiments' key."
ngx.status = 400
ngx.say(error)
else
os.remove(file_name)
local response = json.encode(yaml_data)
ngx.log(ngx.INFO, response)
ngx.status = 200
ngx.say(response)
end
end
}
}
}