# lua_package_path '/usr/share/lua/5.1/?.lua;;'; server { listen 8080 default_server; root /var/www/html/; index index.html; set_by_lua_block $endpoint { return os.getenv("ENDPOINT") } set_by_lua_block $insecure_endpoint { return os.getenv("INSECURE_ENDPOINT") } 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 endpoint_placeholder $endpoint; sub_filter insecure_endpoint_placeholder $insecure_endpoint; } 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 /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 /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 { 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 = red:get("logs:chaoslogs-" .. logid) 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/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' 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-DIAGRAM] data sent from web interface => " .. data) math.randomseed(os.clock()*100000000000) local rand = math.random(999, 9999) local filename = "/tmp/chaosprogram" .. rand ngx.log(ngx.INFO, "[PROGRAMMING-MODE-DIAGRAM] write temp file " .. filename) local yamlfile = io.open(filename, "w") yamlfile:write(data) yamlfile:close() local handle = io.popen("python3 -c 'import yaml, sys; print(yaml.safe_load(sys.stdin))' < " .. filename) local result = handle:read("*a") local rc = handle:close() ngx.log(ngx.INFO, "[programming_mode_diagram] Result of yaml syntax check =>||" .. result .. "||") if data == nil then error = "[PROGRAMMING-MODE-DIAGRAM] No chaos program already loaded." ngx.log(ngx.INFO, error) ngx.say(error) elseif not string.match(result, '{.*}') then error = "[PROGRAMMING-MODE-DIAGRAM] Chaos program seems not correct. Please check yaml syntax." ngx.log(ngx.INFO, error) ngx.say(error) end local yaml_data = lyaml.load(data) if not key_exists(yaml_data, "jobs") then error = "[PROGRAMMING-MODE-DIAGRAM] Chaos program does not contain 'jobs' key. Please fix yaml definition." ngx.log(ngx.INFO, error) ngx.say(error) elseif not key_exists(yaml_data, "experiments") then error = "[PROGRAMMING-MODE-DIAGRAM] Chaos program does not contain 'experiments' key. Please fix yaml definition" ngx.log(ngx.INFO, error) ngx.say(error) elseif is_key_empty(yaml_data, "jobs") then error = "[PROGRAMMING-MODE-DIAGRAM] Chaos program does not contain valid 'jobs' key. Please fix yaml definition, add jobs" ngx.log(ngx.INFO, error) ngx.say(error) elseif is_key_empty(yaml_data, "experiments") then error = "[PROGRAMMING-MODE-DIAGRAM] Chaos program does not contain valid 'experiments' key. Please fix yaml definition, add experiments" ngx.log(ngx.INFO, error) ngx.say(error) else os.remove(filename) local response = json.encode(yaml_data) ngx.say(response) end } } }