# 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")
  }

  set_by_lua_block $platform_engineering_demo_mode {
    if (os.getenv("PLATFORM_ENGINEERING_DEMO_MODE") == nil) then
      return "false"
    else 
      return os.getenv("PLATFORM_ENGINEERING_DEMO_MODE")
    end
  }

  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;
    sub_filter platform_engineering_demo_mode_placeholder $platform_engineering_demo_mode;
  }

  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"]

      ngx.say(tostring(red:get(key)))
    }
  }

  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/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'

      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
    }
  }
}
