# 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 $endpoint { return os.getenv("ENDPOINT") } set_by_lua_block $insecure_endpoint { local env_var = os.getenv("INSECURE_ENDPOINT") 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", "INSECURE_ENDPOINT"} 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 endpoint_placeholder $endpoint; sub_filter insecure_endpoint_placeholder $insecure_endpoint; sub_filter selected_env_vars_placeholder $selected_env_vars; } location /kube/ingress/get { access_by_lua_file "/usr/local/openresty/nginx/conf/kubeinvaders/ingress.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("ENDPOINT") == nil then ngx.say("Error! ENDPOINT is nil!") else ngx.say(os.getenv("ENDPOINT")) 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 } } }