From a2d94d9a3f50c47f2a000f54941c4a83b291c1b4 Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Mon, 6 Aug 2018 16:54:04 -0400 Subject: [PATCH] Multi-arch images for echoserver Originally from: https://github.com/kubernetes/ingress-nginx/tree/master/images/echoheaders Moving the code here to prevent bit-rot and to be sure we can recreate or update the images on demand. Moving it here also ensures we can use the common harness to build the multi-arch manifests needed for running the e2e test that use this container. Change-Id: I15009268da4e7809a1c03d9af3181b585afa8139 --- test/images/BUILD | 1 + test/images/echoserver/BASEIMAGE | 4 + test/images/echoserver/BUILD | 13 + test/images/echoserver/Dockerfile | 27 ++ test/images/echoserver/README.md | 10 + test/images/echoserver/VERSION | 1 + test/images/echoserver/nginx.conf | 96 ++++++ test/images/echoserver/run.sh | 25 ++ test/images/echoserver/template.lua | 509 ++++++++++++++++++++++++++++ test/utils/image/manifest.go | 2 +- 10 files changed, 687 insertions(+), 1 deletion(-) create mode 100644 test/images/echoserver/BASEIMAGE create mode 100644 test/images/echoserver/BUILD create mode 100644 test/images/echoserver/Dockerfile create mode 100644 test/images/echoserver/README.md create mode 100644 test/images/echoserver/VERSION create mode 100644 test/images/echoserver/nginx.conf create mode 100644 test/images/echoserver/run.sh create mode 100644 test/images/echoserver/template.lua diff --git a/test/images/BUILD b/test/images/BUILD index e8a70a0930..efd8e2859d 100644 --- a/test/images/BUILD +++ b/test/images/BUILD @@ -12,6 +12,7 @@ filegroup( srcs = [ ":package-srcs", "//test/images/apparmor-loader:all-srcs", + "//test/images/echoserver:all-srcs", "//test/images/entrypoint-tester:all-srcs", "//test/images/fakegitserver:all-srcs", "//test/images/liveness:all-srcs", diff --git a/test/images/echoserver/BASEIMAGE b/test/images/echoserver/BASEIMAGE new file mode 100644 index 0000000000..baff9efd08 --- /dev/null +++ b/test/images/echoserver/BASEIMAGE @@ -0,0 +1,4 @@ +amd64=nginx:1.15-alpine +arm=arm32v6/nginx:1.15-alpine +arm64=arm64v8/nginx:1.15-alpine +ppc64le=ppc64le/nginx:1.15-alpine diff --git a/test/images/echoserver/BUILD b/test/images/echoserver/BUILD new file mode 100644 index 0000000000..6df04e38cd --- /dev/null +++ b/test/images/echoserver/BUILD @@ -0,0 +1,13 @@ +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/test/images/echoserver/Dockerfile b/test/images/echoserver/Dockerfile new file mode 100644 index 0000000000..919f39dba8 --- /dev/null +++ b/test/images/echoserver/Dockerfile @@ -0,0 +1,27 @@ +# Copyright 2017 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM BASEIMAGE + +CROSS_BUILD_COPY qemu-QEMUARCH-static /usr/bin/ + +RUN apk add --no-cache openssl nginx-mod-http-lua nginx-mod-http-lua-upstream +RUN mkdir -p /run/nginx + +ADD nginx.conf /etc/nginx/nginx.conf +ADD template.lua /usr/local/share/lua/5.1/ +ADD README.md README.md +ADD run.sh /usr/local/bin/run.sh +RUN chmod +x /usr/local/bin/run.sh +ENTRYPOINT ["/usr/local/bin/run.sh"] diff --git a/test/images/echoserver/README.md b/test/images/echoserver/README.md new file mode 100644 index 0000000000..624394e829 --- /dev/null +++ b/test/images/echoserver/README.md @@ -0,0 +1,10 @@ +# Echoserver + +This is a simple server that responds with the http headers it received. + +Image Versions >= 1.10 support HTTP2 on :8443. +Image Versions >= 1.9 expose HTTPS endpoint on :8443. +Image versions >= 1.4 removes the redirect introduced in 1.3. +Image versions >= 1.3 redirect requests on :80 with `X-Forwarded-Proto: http` to :443. +Image versions > 1.0 run an nginx server, and implement the echoserver using lua in the nginx config. +Image versions <= 1.0 run a python http server instead of nginx, and don't redirect any requests. diff --git a/test/images/echoserver/VERSION b/test/images/echoserver/VERSION new file mode 100644 index 0000000000..879b416e60 --- /dev/null +++ b/test/images/echoserver/VERSION @@ -0,0 +1 @@ +2.1 diff --git a/test/images/echoserver/nginx.conf b/test/images/echoserver/nginx.conf new file mode 100644 index 0000000000..6c07c51785 --- /dev/null +++ b/test/images/echoserver/nginx.conf @@ -0,0 +1,96 @@ +load_module modules/ndk_http_module.so; +load_module modules/ngx_http_lua_module.so; +load_module modules/ngx_http_lua_upstream_module.so; + +events { + worker_connections 1024; +} + +env HOSTNAME; +env NODE_NAME; +env POD_NAME; +env POD_NAMESPACE; +env POD_IP; + +http { + default_type 'text/plain'; + # maximum allowed size of the client request body. By default this is 1m. + # Request with bigger bodies nginx will return error code 413. + # http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size + client_max_body_size 10m; + + init_by_lua_block { + local template = require("template") + -- template syntax documented here: + -- https://github.com/bungle/lua-resty-template/blob/master/README.md + tmpl = template.compile([[ + + +Hostname: {{os.getenv("HOSTNAME") or "N/A"}} + +Pod Information: +{% if os.getenv("POD_NAME") then %} + node name: {{os.getenv("NODE_NAME") or "N/A"}} + pod name: {{os.getenv("POD_NAME") or "N/A"}} + pod namespace: {{os.getenv("POD_NAMESPACE") or "N/A"}} + pod IP: {{os.getenv("POD_IP") or "N/A"}} +{% else %} + -no pod information available- +{% end %} + +Server values: + server_version=nginx: {{ngx.var.nginx_version}} - lua: {{ngx.config.ngx_lua_version}} + +Request Information: + client_address={{ngx.var.remote_addr}} + method={{ngx.req.get_method()}} + real path={{ngx.var.request_uri}} + query={{ngx.var.query_string or ""}} + request_version={{ngx.req.http_version()}} + request_scheme={{ngx.var.scheme}} + request_uri={{ngx.var.scheme.."://"..ngx.var.host..":"..ngx.var.server_port..ngx.var.request_uri}} + +Request Headers: +{% for i, key in ipairs(keys) do %} + {{key}}={{headers[key]}} +{% end %} + +Request Body: +{{ngx.var.request_body or " -no body in request-"}} +]]) + } + + server { + # please check the benefits of reuseport https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1 + # basically instructs to create an individual listening socket for each worker process (using the SO_REUSEPORT + # socket option), allowing a kernel to distribute incoming connections between worker processes. + listen 8080 default_server reuseport; + listen 8443 default_server ssl http2 reuseport; + + ssl_certificate /certs/certificate.crt; + ssl_certificate_key /certs/privateKey.key; + + # Replace '_' with your hostname. + server_name _; + + # set long keepalive_timeout because some loadbalancer proxies expect the connection + # to remain open for at least ten minutes. + keepalive_timeout 620s; + + location / { + lua_need_request_body on; + content_by_lua_block { + ngx.header["Server"] = "echoserver" + + local headers = ngx.req.get_headers() + local keys = {} + for key, val in pairs(headers) do + table.insert(keys, key) + end + table.sort(keys) + + ngx.say(tmpl({os=os, ngx=ngx, keys=keys, headers=headers})) + } + } + } +} diff --git a/test/images/echoserver/run.sh b/test/images/echoserver/run.sh new file mode 100644 index 0000000000..1f023cb98c --- /dev/null +++ b/test/images/echoserver/run.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +# Copyright 2018 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +echo "Generating self-signed cert" +mkdir -p /certs +openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 \ +-keyout /certs/privateKey.key \ +-out /certs/certificate.crt \ +-subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=IT Department/CN=example.com" + +echo "Starting nginx" +nginx -g "daemon off;" diff --git a/test/images/echoserver/template.lua b/test/images/echoserver/template.lua new file mode 100644 index 0000000000..cc80308a75 --- /dev/null +++ b/test/images/echoserver/template.lua @@ -0,0 +1,509 @@ +-- vendored from https://raw.githubusercontent.com/bungle/lua-resty-template/1f9a5c24fc7572dbf5be0b9f8168cc3984b03d24/lib/resty/template.lua +-- only modification: remove / from HTML_ENTITIES to not escape it, and fix the appropriate regex. +--[[ +Copyright (c) 2014 - 2017 Aapo Talvensaari +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +* Neither the name of the {organization} nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--]] + +local setmetatable = setmetatable +local loadstring = loadstring +local loadchunk +local tostring = tostring +local setfenv = setfenv +local require = require +local capture +local concat = table.concat +local assert = assert +local prefix +local write = io.write +local pcall = pcall +local phase +local open = io.open +local load = load +local type = type +local dump = string.dump +local find = string.find +local gsub = string.gsub +local byte = string.byte +local null +local sub = string.sub +local ngx = ngx +local jit = jit +local var + +local _VERSION = _VERSION +local _ENV = _ENV +local _G = _G + +local HTML_ENTITIES = { + ["&"] = "&", + ["<"] = "<", + [">"] = ">", + ['"'] = """, + ["'"] = "'", +} + +local CODE_ENTITIES = { + ["{"] = "{", + ["}"] = "}", + ["&"] = "&", + ["<"] = "<", + [">"] = ">", + ['"'] = """, + ["'"] = "'", + ["/"] = "/" +} + +local VAR_PHASES + +local ok, newtab = pcall(require, "table.new") +if not ok then newtab = function() return {} end end + +local caching = true +local template = newtab(0, 12) + +template._VERSION = "1.9" +template.cache = {} + +local function enabled(val) + if val == nil then return true end + return val == true or (val == "1" or val == "true" or val == "on") +end + +local function trim(s) + return gsub(gsub(s, "^%s+", ""), "%s+$", "") +end + +local function rpos(view, s) + while s > 0 do + local c = sub(view, s, s) + if c == " " or c == "\t" or c == "\0" or c == "\x0B" then + s = s - 1 + else + break + end + end + return s +end + +local function escaped(view, s) + if s > 1 and sub(view, s - 1, s - 1) == "\\" then + if s > 2 and sub(view, s - 2, s - 2) == "\\" then + return false, 1 + else + return true, 1 + end + end + return false, 0 +end + +local function readfile(path) + local file = open(path, "rb") + if not file then return nil end + local content = file:read "*a" + file:close() + return content +end + +local function loadlua(path) + return readfile(path) or path +end + +local function loadngx(path) + local vars = VAR_PHASES[phase()] + local file, location = path, vars and var.template_location + if sub(file, 1) == "/" then file = sub(file, 2) end + if location and location ~= "" then + if sub(location, -1) == "/" then location = sub(location, 1, -2) end + local res = capture(concat{ location, '/', file}) + if res.status == 200 then return res.body end + end + local root = vars and (var.template_root or var.document_root) or prefix + if sub(root, -1) == "/" then root = sub(root, 1, -2) end + return readfile(concat{ root, "/", file }) or path +end + +do + if ngx then + VAR_PHASES = { + set = true, + rewrite = true, + access = true, + content = true, + header_filter = true, + body_filter = true, + log = true + } + template.print = ngx.print or write + template.load = loadngx + prefix, var, capture, null, phase = ngx.config.prefix(), ngx.var, ngx.location.capture, ngx.null, ngx.get_phase + if VAR_PHASES[phase()] then + caching = enabled(var.template_cache) + end + else + template.print = write + template.load = loadlua + end + if _VERSION == "Lua 5.1" then + local context = { __index = function(t, k) + return t.context[k] or t.template[k] or _G[k] + end } + if jit then + loadchunk = function(view) + return assert(load(view, nil, nil, setmetatable({ template = template }, context))) + end + else + loadchunk = function(view) + local func = assert(loadstring(view)) + setfenv(func, setmetatable({ template = template }, context)) + return func + end + end + else + local context = { __index = function(t, k) + return t.context[k] or t.template[k] or _ENV[k] + end } + loadchunk = function(view) + return assert(load(view, nil, nil, setmetatable({ template = template }, context))) + end + end +end + +function template.caching(enable) + if enable ~= nil then caching = enable == true end + return caching +end + +function template.output(s) + if s == nil or s == null then return "" end + if type(s) == "function" then return template.output(s()) end + return tostring(s) +end + +function template.escape(s, c) + if type(s) == "string" then + if c then return gsub(s, "[}{\">/<'&]", CODE_ENTITIES) end + return gsub(s, "[\"><'&]", HTML_ENTITIES) + end + return template.output(s) +end + +function template.new(view, layout) + assert(view, "view was not provided for template.new(view, layout).") + local render, compile = template.render, template.compile + if layout then + if type(layout) == "table" then + return setmetatable({ render = function(self, context) + local context = context or self + context.blocks = context.blocks or {} + context.view = compile(view)(context) + layout.blocks = context.blocks or {} + layout.view = context.view or "" + return layout:render() + end }, { __tostring = function(self) + local context = self + context.blocks = context.blocks or {} + context.view = compile(view)(context) + layout.blocks = context.blocks or {} + layout.view = context.view + return tostring(layout) + end }) + else + return setmetatable({ render = function(self, context) + local context = context or self + context.blocks = context.blocks or {} + context.view = compile(view)(context) + return render(layout, context) + end }, { __tostring = function(self) + local context = self + context.blocks = context.blocks or {} + context.view = compile(view)(context) + return compile(layout)(context) + end }) + end + end + return setmetatable({ render = function(self, context) + return render(view, context or self) + end }, { __tostring = function(self) + return compile(view)(self) + end }) +end + +function template.precompile(view, path, strip) + local chunk = dump(template.compile(view), strip ~= false) + if path then + local file = open(path, "wb") + file:write(chunk) + file:close() + end + return chunk +end + +function template.compile(view, key, plain) + assert(view, "view was not provided for template.compile(view, key, plain).") + if key == "no-cache" then + return loadchunk(template.parse(view, plain)), false + end + key = key or view + local cache = template.cache + if cache[key] then return cache[key], true end + local func = loadchunk(template.parse(view, plain)) + if caching then cache[key] = func end + return func, false +end + +function template.parse(view, plain) + assert(view, "view was not provided for template.parse(view, plain).") + if not plain then + view = template.load(view) + if byte(view, 1, 1) == 27 then return view end + end + local j = 2 + local c = {[[ +context=... or {} +local function include(v, c) return template.compile(v)(c or context) end +local ___,blocks,layout={},blocks or {} +]] } + local i, s = 1, find(view, "{", 1, true) + while s do + local t, p = sub(view, s + 1, s + 1), s + 2 + if t == "{" then + local e = find(view, "}}", p, true) + if e then + local z, w = escaped(view, s) + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + if z then + i = s + else + c[j] = "___[#___+1]=template.escape(" + c[j+1] = trim(sub(view, p, e - 1)) + c[j+2] = ")\n" + j=j+3 + s, i = e + 1, e + 2 + end + end + elseif t == "*" then + local e = find(view, "*}", p, true) + if e then + local z, w = escaped(view, s) + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + if z then + i = s + else + c[j] = "___[#___+1]=template.output(" + c[j+1] = trim(sub(view, p, e - 1)) + c[j+2] = ")\n" + j=j+3 + s, i = e + 1, e + 2 + end + end + elseif t == "%" then + local e = find(view, "%}", p, true) + if e then + local z, w = escaped(view, s) + if z then + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + i = s + else + local n = e + 2 + if sub(view, n, n) == "\n" then + n = n + 1 + end + local r = rpos(view, s - 1) + if i <= r then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, r) + c[j+2] = "]=]\n" + j=j+3 + end + c[j] = trim(sub(view, p, e - 1)) + c[j+1] = "\n" + j=j+2 + s, i = n - 1, n + end + end + elseif t == "(" then + local e = find(view, ")}", p, true) + if e then + local z, w = escaped(view, s) + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + if z then + i = s + else + local f = sub(view, p, e - 1) + local x = find(f, ",", 2, true) + if x then + c[j] = "___[#___+1]=include([=[" + c[j+1] = trim(sub(f, 1, x - 1)) + c[j+2] = "]=]," + c[j+3] = trim(sub(f, x + 1)) + c[j+4] = ")\n" + j=j+5 + else + c[j] = "___[#___+1]=include([=[" + c[j+1] = trim(f) + c[j+2] = "]=])\n" + j=j+3 + end + s, i = e + 1, e + 2 + end + end + elseif t == "[" then + local e = find(view, "]}", p, true) + if e then + local z, w = escaped(view, s) + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + if z then + i = s + else + c[j] = "___[#___+1]=include(" + c[j+1] = trim(sub(view, p, e - 1)) + c[j+2] = ")\n" + j=j+3 + s, i = e + 1, e + 2 + end + end + elseif t == "-" then + local e = find(view, "-}", p, true) + if e then + local x, y = find(view, sub(view, s, e + 1), e + 2, true) + if x then + local z, w = escaped(view, s) + if z then + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + i = s + else + y = y + 1 + x = x - 1 + if sub(view, y, y) == "\n" then + y = y + 1 + end + local b = trim(sub(view, p, e - 1)) + if b == "verbatim" or b == "raw" then + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + c[j] = "___[#___+1]=[=[" + c[j+1] = sub(view, e + 2, x) + c[j+2] = "]=]\n" + j=j+3 + else + if sub(view, x, x) == "\n" then + x = x - 1 + end + local r = rpos(view, s - 1) + if i <= r then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, r) + c[j+2] = "]=]\n" + j=j+3 + end + c[j] = 'blocks["' + c[j+1] = b + c[j+2] = '"]=include[=[' + c[j+3] = sub(view, e + 2, x) + c[j+4] = "]=]\n" + j=j+5 + end + s, i = y - 1, y + end + end + end + elseif t == "#" then + local e = find(view, "#}", p, true) + if e then + local z, w = escaped(view, s) + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = sub(view, i, s - 1 - w) + c[j+2] = "]=]\n" + j=j+3 + end + if z then + i = s + else + e = e + 2 + if sub(view, e, e) == "\n" then + e = e + 1 + end + s, i = e - 1, e + end + end + end + s = find(view, "{", s + 1, true) + end + s = sub(view, i) + if s and s ~= "" then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = s + c[j+2] = "]=]\n" + j=j+3 + end + c[j] = "return layout and include(layout,setmetatable({view=table.concat(___),blocks=blocks},{__index=context})) or table.concat(___)" + return concat(c) +end + +function template.render(view, context, key, plain) + assert(view, "view was not provided for template.render(view, context, key, plain).") + return template.print(template.compile(view, key, plain)(context)) +end + +return template \ No newline at end of file diff --git a/test/utils/image/manifest.go b/test/utils/image/manifest.go index 52e29a65ba..af15d5e153 100644 --- a/test/utils/image/manifest.go +++ b/test/utils/image/manifest.go @@ -56,7 +56,7 @@ var ( CheckMetadataConcealment = ImageConfig{gcRegistry, "check-metadata-concealment", "v0.0.3", false} CudaVectorAdd = ImageConfig{e2eRegistry, "cuda-vector-add", "1.0", false} Dnsutils = ImageConfig{e2eRegistry, "dnsutils", "1.1", false} - EchoServer = ImageConfig{gcRegistry, "echoserver", "1.10", false} + EchoServer = ImageConfig{e2eRegistry, "echoserver", "2.1", false} EntrypointTester = ImageConfig{e2eRegistry, "entrypoint-tester", "1.0", false} Fakegitserver = ImageConfig{e2eRegistry, "fakegitserver", "1.0", false} GBFrontend = ImageConfig{sampleRegistry, "gb-frontend", "v6", false}