mirror of
https://github.com/lucky-sideburn/kubeinvaders.git
synced 2026-05-17 06:06:40 +00:00
improve k8s client configuration
This commit is contained in:
@@ -61,6 +61,7 @@ COPY scripts/chaos-containers.lua /usr/local/openresty/nginx/conf/kubeinvaders/c
|
||||
COPY scripts/programming_mode.lua /usr/local/openresty/nginx/conf/kubeinvaders/programming_mode.lua
|
||||
COPY scripts/config_kubeinv.lua /usr/local/openresty/lualib/config_kubeinv.lua
|
||||
COPY scripts/data/codenames.txt /usr/local/openresty/nginx/conf/kubeinvaders/data/codenames.txt
|
||||
COPY scripts/demo_deploy.lua /usr/local/openresty/nginx/conf/kubeinvaders/demo_deploy.lua
|
||||
|
||||
# Copy Python helpers
|
||||
COPY scripts/programming_mode /opt/programming_mode/
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/codemirror@5.65.18/lib/codemirror.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js "></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1/dist/echarts.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/js-yaml@4.1.0/dist/js-yaml.min.js"></script>
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
@@ -549,6 +550,20 @@ EOF</pre>
|
||||
<div class="settings">
|
||||
<h2>Kubernetes Connection</h2>
|
||||
<div class="setting-group">
|
||||
<label>Load from KUBECONFIG file</label>
|
||||
<div style="display:flex; align-items:center; gap:10px; margin-bottom:6px;">
|
||||
<input type="file" id="kubeconfig_file" style="display:none;" onchange="loadKubeconfigFile(event)">
|
||||
<button type="button" onclick="document.getElementById('kubeconfig_file').click()">Choose KUBECONFIG file</button>
|
||||
<span id="kubeconfig_filename" style="color:#6c757d; font-size:13px;"></span>
|
||||
</div>
|
||||
<div id="kubeconfig-load-status" style="margin-bottom:10px; display:none;">
|
||||
<div id="kubeconfig-load-success" class="alert alert-success" style="display:none; padding:6px 10px; font-size:13px;"></div>
|
||||
<div id="kubeconfig-load-error" class="alert alert-danger" style="display:none; padding:6px 10px; font-size:13px;"></div>
|
||||
</div>
|
||||
<small style="display:block; margin-bottom:14px; color:#6c757d; font-weight:normal;">
|
||||
Parses the selected context and auto-fills the fields below. Or fill them manually.
|
||||
</small>
|
||||
|
||||
<label for="k8s_api_endpoint">Kubernetes API Endpoint</label>
|
||||
<input type="text" id="k8s_api_endpoint" name="k8s_api_endpoint" placeholder="https://kubernetes.default.svc">
|
||||
|
||||
@@ -603,6 +618,27 @@ EOF</pre>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END KUBERNETES CONNECTION FORM -->
|
||||
|
||||
<!-- START DEMO DEPLOY BLOCK -->
|
||||
<div class="row" id="demo-deploy-block" style="display:none; margin-bottom: 2%;">
|
||||
<div class="col">
|
||||
<div class="settings">
|
||||
<h2>Deploy Demo Resources</h2>
|
||||
<div class="setting-group">
|
||||
<p style="font-weight:normal; color:#6c757d; font-size:13px; margin-bottom:14px;">
|
||||
Creates namespaces <strong>ns-1</strong> and <strong>ns-2</strong>, each with a Deployment of <strong>10 nginx pods</strong> — ready to use as chaos experiment targets.
|
||||
</p>
|
||||
<button id="demo-deploy-btn" onclick="deployDemoResources()">Deploy ns-1 & ns-2 (10 pods each)</button>
|
||||
<div id="demo-deploy-status" style="margin-top: 10px; display: none;">
|
||||
<div id="demo-deploy-success" class="alert alert-success" style="display: none;"></div>
|
||||
<div id="demo-deploy-error" class="alert alert-danger" style="display: none;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END DEMO DEPLOY BLOCK -->
|
||||
|
||||
<!-- START SWITCH BUTTONS FOR MODE (GAME, PROGRAMMING)-->
|
||||
<div class="accordion" id="guide" style="margin-top: 2%; margin-bottom: 2%;">
|
||||
<div class="accordion-item">
|
||||
|
||||
240
html/js/utils.js
240
html/js/utils.js
@@ -16,6 +16,183 @@
|
||||
|
||||
/* Utility Functions */
|
||||
|
||||
function loadKubeconfigFile(event) {
|
||||
var file = event.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
var filenameSpan = document.getElementById('kubeconfig_filename');
|
||||
var statusDiv = document.getElementById('kubeconfig-load-status');
|
||||
var successDiv = document.getElementById('kubeconfig-load-success');
|
||||
var errorDiv = document.getElementById('kubeconfig-load-error');
|
||||
|
||||
if (filenameSpan) filenameSpan.textContent = file.name;
|
||||
if (statusDiv) statusDiv.style.display = 'none';
|
||||
if (successDiv) { successDiv.style.display = 'none'; successDiv.textContent = ''; }
|
||||
if (errorDiv) { errorDiv.style.display = 'none'; errorDiv.textContent = ''; }
|
||||
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
var kubeconfig;
|
||||
try {
|
||||
kubeconfig = jsyaml.load(e.target.result);
|
||||
} catch (err) {
|
||||
showKubeconfigStatus('Failed to parse KUBECONFIG: ' + err.message, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!kubeconfig || typeof kubeconfig !== 'object') {
|
||||
showKubeconfigStatus('Invalid KUBECONFIG file.', true);
|
||||
return;
|
||||
}
|
||||
|
||||
var currentContextName = kubeconfig['current-context'];
|
||||
var contexts = kubeconfig.contexts || [];
|
||||
var contextEntry = contexts.find(function(c) { return c.name === currentContextName; }) || contexts[0];
|
||||
|
||||
if (!contextEntry) {
|
||||
showKubeconfigStatus('No context found in KUBECONFIG.', true);
|
||||
return;
|
||||
}
|
||||
|
||||
var ctx = contextEntry.context || {};
|
||||
var clusterName = ctx.cluster;
|
||||
var userName = ctx.user;
|
||||
var namespace = ctx.namespace || '';
|
||||
|
||||
var clusters = kubeconfig.clusters || [];
|
||||
var clusterEntry = clusters.find(function(c) { return c.name === clusterName; });
|
||||
var clusterData = clusterEntry ? (clusterEntry.cluster || {}) : {};
|
||||
|
||||
var users = kubeconfig.users || [];
|
||||
var userEntry = users.find(function(u) { return u.name === userName; });
|
||||
var userData = userEntry ? (userEntry.user || {}) : {};
|
||||
|
||||
var server = clusterData.server || '';
|
||||
var caCertB64 = clusterData['certificate-authority-data'] || '';
|
||||
var caCertFilePath = clusterData['certificate-authority'] || '';
|
||||
var token = userData.token || userData['token-data'] || '';
|
||||
var usesClientCert = !token && (
|
||||
userData['client-certificate'] || userData['client-certificate-data'] ||
|
||||
userData['exec']
|
||||
);
|
||||
|
||||
var caCert = '';
|
||||
if (caCertB64) {
|
||||
try {
|
||||
caCert = atob(caCertB64);
|
||||
} catch (_) {
|
||||
caCert = caCertB64;
|
||||
}
|
||||
}
|
||||
|
||||
var endpointInput = document.getElementById('k8s_api_endpoint');
|
||||
var tokenInput = document.getElementById('k8s_token');
|
||||
var caCertInput = document.getElementById('k8s_ca_cert');
|
||||
var namespacesInput = document.getElementById('k8s_namespaces');
|
||||
|
||||
if (endpointInput && server) endpointInput.value = server;
|
||||
if (tokenInput && token) { tokenInput.value = token; localStorage.setItem('k8s_token', token); }
|
||||
if (caCertInput) caCertInput.value = caCert;
|
||||
if (namespacesInput && namespace && !namespacesInput.value) namespacesInput.value = namespace;
|
||||
|
||||
if (server) localStorage.setItem('k8s_api_endpoint', server);
|
||||
if (caCert) localStorage.setItem('k8s_ca_cert', caCert);
|
||||
|
||||
if (usesClientCert) {
|
||||
var ns = namespace || 'default';
|
||||
var cmds = [
|
||||
'kubectl create serviceaccount kubeinvaders -n ' + ns,
|
||||
'kubectl create clusterrolebinding kubeinvaders \\',
|
||||
' --clusterrole=cluster-admin \\',
|
||||
' --serviceaccount=' + ns + ':kubeinvaders',
|
||||
'kubectl create token kubeinvaders -n ' + ns
|
||||
];
|
||||
var caCertWarning = (!caCert && caCertFilePath)
|
||||
? '<br><br><strong>CA certificate:</strong> your kubeconfig references a local file (<code>' +
|
||||
escapeHtml(caCertFilePath) + '</code>) that cannot be read by the browser. ' +
|
||||
'Paste its contents manually into the CA Certificate field above.'
|
||||
: '';
|
||||
var statusDiv = document.getElementById('kubeconfig-load-status');
|
||||
var errorDiv = document.getElementById('kubeconfig-load-error');
|
||||
var successDiv = document.getElementById('kubeconfig-load-success');
|
||||
if (statusDiv) statusDiv.style.display = 'block';
|
||||
if (successDiv) successDiv.style.display = 'none';
|
||||
if (errorDiv) {
|
||||
errorDiv.style.display = 'block';
|
||||
errorDiv.innerHTML =
|
||||
'Endpoint loaded (context: <strong>' + contextEntry.name + '</strong>), but no token found.<br>' +
|
||||
'This context uses client-certificate auth, which is not supported directly.<br>' +
|
||||
'We suggest running these commands to create a <strong>cluster-admin</strong> service account token:<br><br>' +
|
||||
'<code style="display:block; background:#f0f0f0; color:#222; padding:8px 10px; border-radius:4px; white-space:pre-wrap; overflow-wrap:break-word; font-size:12px;">' +
|
||||
cmds.map(function(c) { return escapeHtml(c); }).join('\n') +
|
||||
'</code>' +
|
||||
'<button type="button" onclick="copyKubeconfigCommands(this)" ' +
|
||||
'data-commands="' + escapeAttr(cmds.join('\n')) + '" ' +
|
||||
'style="margin-top:8px; font-size:12px; padding:3px 10px;">Copy commands</button>' +
|
||||
caCertWarning;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var loaded = [];
|
||||
if (server) loaded.push('endpoint');
|
||||
if (token) loaded.push('token');
|
||||
if (caCert) loaded.push('CA cert');
|
||||
var contextLabel = contextEntry.name ? ' (context: ' + contextEntry.name + ')' : '';
|
||||
var msg = 'Loaded ' + loaded.join(', ') + contextLabel + '.';
|
||||
if (!caCert && caCertFilePath) {
|
||||
msg += '\nCA certificate references a local file (' + caCertFilePath + ') — paste it manually into the CA Certificate field.';
|
||||
}
|
||||
|
||||
showKubeconfigStatus(msg, false);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
function showKubeconfigStatus(message, isError) {
|
||||
var statusDiv = document.getElementById('kubeconfig-load-status');
|
||||
var successDiv = document.getElementById('kubeconfig-load-success');
|
||||
var errorDiv = document.getElementById('kubeconfig-load-error');
|
||||
if (!statusDiv) return;
|
||||
statusDiv.style.display = 'block';
|
||||
if (isError) {
|
||||
if (errorDiv) { errorDiv.style.display = 'block'; errorDiv.textContent = message; }
|
||||
if (successDiv) successDiv.style.display = 'none';
|
||||
} else {
|
||||
if (successDiv) { successDiv.style.display = 'block'; successDiv.textContent = message; }
|
||||
if (errorDiv) errorDiv.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
function escapeAttr(str) {
|
||||
return str.replace(/&/g, '&').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
function copyKubeconfigCommands(btn) {
|
||||
var commands = btn.getAttribute('data-commands');
|
||||
navigator.clipboard.writeText(commands).then(function() {
|
||||
var orig = btn.textContent;
|
||||
btn.textContent = 'Copied!';
|
||||
setTimeout(function() { btn.textContent = orig; }, 2000);
|
||||
}).catch(function() {
|
||||
var ta = document.createElement('textarea');
|
||||
ta.value = commands;
|
||||
ta.style.position = 'fixed';
|
||||
ta.style.opacity = '0';
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(ta);
|
||||
var orig = btn.textContent;
|
||||
btn.textContent = 'Copied!';
|
||||
setTimeout(function() { btn.textContent = orig; }, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
function parseNamespacesInput(input) {
|
||||
if (!input) {
|
||||
return [];
|
||||
@@ -336,6 +513,7 @@ function saveK8sConnection() {
|
||||
}
|
||||
|
||||
showConnectionStatus('', false, 'k8s-save', 3000);
|
||||
showDemoDeployBlock();
|
||||
return true;
|
||||
})
|
||||
.catch(function (error) {
|
||||
@@ -344,6 +522,64 @@ function saveK8sConnection() {
|
||||
});
|
||||
}
|
||||
|
||||
function showDemoDeployBlock() {
|
||||
var block = document.getElementById('demo-deploy-block');
|
||||
if (block) {
|
||||
block.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
function deployDemoResources() {
|
||||
var btn = document.getElementById('demo-deploy-btn');
|
||||
var statusDiv = document.getElementById('demo-deploy-status');
|
||||
var successDiv = document.getElementById('demo-deploy-success');
|
||||
var errorDiv = document.getElementById('demo-deploy-error');
|
||||
|
||||
if (btn) {
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Deploying...';
|
||||
}
|
||||
|
||||
var url = appendK8sTargetParam('/kube/demo/deploy');
|
||||
var oReq = new XMLHttpRequest();
|
||||
oReq.open('POST', url, true);
|
||||
applyK8sConnectionHeaders(oReq);
|
||||
oReq.timeout = 30000;
|
||||
|
||||
oReq.onreadystatechange = function () {
|
||||
if (this.readyState !== XMLHttpRequest.DONE) return;
|
||||
|
||||
if (btn) {
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Deploy ns-1 & ns-2 (10 pods each)';
|
||||
}
|
||||
|
||||
statusDiv.style.display = 'block';
|
||||
var payload = null;
|
||||
try { payload = JSON.parse(this.responseText); } catch (e) {}
|
||||
|
||||
if (this.status === 200 || this.status === 207) {
|
||||
var msg = (payload && payload.message) ? payload.message : 'Deployed successfully.';
|
||||
if (payload && payload.errors && payload.errors.length > 0) {
|
||||
errorDiv.style.display = 'block';
|
||||
successDiv.style.display = 'none';
|
||||
errorDiv.textContent = 'Partial failure: ' + payload.errors.join('; ');
|
||||
} else {
|
||||
successDiv.style.display = 'block';
|
||||
errorDiv.style.display = 'none';
|
||||
successDiv.textContent = '✅ ' + msg;
|
||||
}
|
||||
} else {
|
||||
var errMsg = (payload && payload.error) ? payload.error : this.responseText;
|
||||
errorDiv.style.display = 'block';
|
||||
successDiv.style.display = 'none';
|
||||
errorDiv.textContent = '❌ Deploy failed: ' + errMsg;
|
||||
}
|
||||
};
|
||||
|
||||
oReq.send();
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// Migrate old localStorage key name used in earlier versions
|
||||
var legacyK8sUrl = localStorage.getItem("k8s_url");
|
||||
@@ -386,6 +622,10 @@ document.addEventListener("DOMContentLoaded", function() {
|
||||
|
||||
configured_namespaces = parseNamespacesInput(storedNamespaces || "");
|
||||
|
||||
if (storedK8sUrl && storedToken) {
|
||||
showDemoDeployBlock();
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
var splashScreen = document.getElementById("splash-screen");
|
||||
var mainGameDiv = document.getElementById("main-game-div");
|
||||
|
||||
@@ -62,6 +62,10 @@ server {
|
||||
sub_filter selected_env_vars_placeholder $selected_env_vars;
|
||||
}
|
||||
|
||||
location /kube/demo/deploy {
|
||||
access_by_lua_file "/usr/local/openresty/nginx/conf/kubeinvaders/demo_deploy.lua";
|
||||
}
|
||||
|
||||
location /kube/ingresses {
|
||||
access_by_lua_file "/usr/local/openresty/nginx/conf/kubeinvaders/ingress.lua";
|
||||
}
|
||||
|
||||
147
scripts/demo_deploy.lua
Normal file
147
scripts/demo_deploy.lua
Normal file
@@ -0,0 +1,147 @@
|
||||
local https = require "ssl.https"
|
||||
local ltn12 = require "ltn12"
|
||||
local json = require "lunajson"
|
||||
|
||||
local arg = ngx.req.get_uri_args()
|
||||
local req_headers = ngx.req.get_headers()
|
||||
|
||||
ngx.header['Content-Type'] = 'application/json'
|
||||
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,X-K8S-Target,X-K8S-Token,X-K8S-CA-CERT-B64'
|
||||
ngx.header['Access-Control-Expose-Headers'] = 'Content-Length,Content-Range'
|
||||
|
||||
if ngx.var.request_method == "OPTIONS" then
|
||||
ngx.status = 204
|
||||
return
|
||||
end
|
||||
|
||||
local k8s_url = ""
|
||||
local kube_host = os.getenv("KUBERNETES_SERVICE_HOST")
|
||||
local kube_port = os.getenv("KUBERNETES_SERVICE_PORT_HTTPS")
|
||||
local endpoint = os.getenv("ENDPOINT")
|
||||
|
||||
if kube_host and kube_host ~= "" then
|
||||
local port_suffix = kube_port and kube_port ~= "" and (":" .. kube_port) or ""
|
||||
k8s_url = "https://" .. kube_host .. port_suffix
|
||||
else
|
||||
k8s_url = endpoint or ""
|
||||
end
|
||||
|
||||
local target = arg['target'] or req_headers["x-k8s-target"] or req_headers["X-K8S-Target"]
|
||||
if target and target ~= "" then
|
||||
if not string.match(target, "^https?://") then
|
||||
target = "https://" .. target
|
||||
end
|
||||
k8s_url = string.gsub(target, "/+$", "")
|
||||
end
|
||||
|
||||
if k8s_url == "" then
|
||||
ngx.status = 500
|
||||
ngx.say(json.encode({ok = false, error = "Missing Kubernetes endpoint"}))
|
||||
return
|
||||
end
|
||||
|
||||
if not string.match(k8s_url, "^https?://") then
|
||||
k8s_url = "https://" .. k8s_url
|
||||
end
|
||||
k8s_url = string.gsub(k8s_url, "/+$", "")
|
||||
|
||||
local header_token = req_headers["x-k8s-token"] or req_headers["X-K8S-Token"]
|
||||
local token = header_token and header_token ~= "" and header_token or tostring(os.getenv("TOKEN") or "")
|
||||
if token == "" then
|
||||
local f = io.open("/var/run/secrets/kubernetes.io/serviceaccount/token", "r")
|
||||
if f then
|
||||
token = f:read("*a") or ""
|
||||
token = token:gsub("%s+$", "")
|
||||
f:close()
|
||||
end
|
||||
end
|
||||
|
||||
if token == "" then
|
||||
ngx.status = 500
|
||||
ngx.say(json.encode({ok = false, error = "Missing Kubernetes API token"}))
|
||||
return
|
||||
end
|
||||
|
||||
local ca_cert_b64 = req_headers["x-k8s-ca-cert-b64"] or req_headers["X-K8S-CA-CERT-B64"]
|
||||
local ca_cert = nil
|
||||
if ca_cert_b64 and ca_cert_b64 ~= "" then
|
||||
ca_cert = ngx.decode_base64(ca_cert_b64)
|
||||
end
|
||||
|
||||
local disable_tls_env = string.lower(tostring(os.getenv("DISABLE_TLS") or "false"))
|
||||
local disable_tls = disable_tls_env == "true" or disable_tls_env == "1" or disable_tls_env == "yes"
|
||||
|
||||
local ca_file_path = "/tmp/kubeinv-demo-ca.crt"
|
||||
if not disable_tls and ca_cert and ca_cert ~= "" then
|
||||
local ca_file = io.open(ca_file_path, "w")
|
||||
if ca_file then
|
||||
ca_file:write(ca_cert)
|
||||
ca_file:close()
|
||||
end
|
||||
end
|
||||
|
||||
local function k8s_request(url, method, body)
|
||||
local resp = {}
|
||||
local headers = {
|
||||
["Accept"] = "application/json",
|
||||
["Content-Type"] = "application/json",
|
||||
["Authorization"] = "Bearer " .. token,
|
||||
}
|
||||
local opts = {
|
||||
url = url,
|
||||
headers = headers,
|
||||
method = method,
|
||||
verify = disable_tls and "none" or "peer",
|
||||
sink = ltn12.sink.table(resp),
|
||||
}
|
||||
if body and body ~= "" then
|
||||
headers["Content-Length"] = tostring(#body)
|
||||
opts.source = ltn12.source.string(body)
|
||||
end
|
||||
if not disable_tls and ca_cert and ca_cert ~= "" then
|
||||
opts.cafile = ca_file_path
|
||||
end
|
||||
local ok, status_code, _, _ = https.request(opts)
|
||||
return ok, status_code, table.concat(resp)
|
||||
end
|
||||
|
||||
local ns_body_fmt = '{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"%s","labels":{"app":"kubeinvaders-demo"}}}'
|
||||
local deploy_body_fmt = '{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"kubeinvaders-demo","namespace":"%s","labels":{"app":"kubeinvaders-demo"}},"spec":{"replicas":10,"selector":{"matchLabels":{"app":"kubeinvaders-demo"}},"template":{"metadata":{"labels":{"app":"kubeinvaders-demo"}},"spec":{"containers":[{"name":"nginx","image":"nginx:alpine","resources":{"requests":{"memory":"32Mi","cpu":"10m"},"limits":{"memory":"64Mi","cpu":"50m"}}}]}}}}'
|
||||
|
||||
local created = {}
|
||||
local errors = {}
|
||||
|
||||
for _, ns in ipairs({"ns-1", "ns-2"}) do
|
||||
local ns_body = string.format(ns_body_fmt, ns)
|
||||
local ok, status, _ = k8s_request(k8s_url .. "/api/v1/namespaces", "POST", ns_body)
|
||||
|
||||
if not ok then
|
||||
table.insert(errors, "namespace " .. ns .. ": request failed")
|
||||
elseif status ~= 200 and status ~= 201 and status ~= 409 then
|
||||
table.insert(errors, "namespace " .. ns .. ": HTTP " .. tostring(status))
|
||||
else
|
||||
local deploy_body = string.format(deploy_body_fmt, ns)
|
||||
local dok, dstatus, _ = k8s_request(
|
||||
k8s_url .. "/apis/apps/v1/namespaces/" .. ns .. "/deployments",
|
||||
"POST",
|
||||
deploy_body
|
||||
)
|
||||
if not dok then
|
||||
table.insert(errors, "deployment in " .. ns .. ": request failed")
|
||||
elseif dstatus ~= 200 and dstatus ~= 201 and dstatus ~= 409 then
|
||||
table.insert(errors, "deployment in " .. ns .. ": HTTP " .. tostring(dstatus))
|
||||
else
|
||||
table.insert(created, ns)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #errors == 0 then
|
||||
ngx.status = 200
|
||||
ngx.say(json.encode({ok = true, created = created, message = "Deployed ns-1 and ns-2 with 10 nginx pods each"}))
|
||||
else
|
||||
ngx.status = 207
|
||||
ngx.say(json.encode({ok = #created > 0, created = created, errors = errors}))
|
||||
end
|
||||
Reference in New Issue
Block a user