diff options
author | Raúl Benencia <raul@thousandeyes.com> | 2018-04-13 16:30:31 -0700 |
---|---|---|
committer | Raúl Benencia <raul@thousandeyes.com> | 2018-05-11 15:02:34 -0700 |
commit | 77c172b823b64ebface655681ab0749b9d2f7081 (patch) | |
tree | 09c13e626eb95ae1d33e76ed683172eab1ab6c96 /test/integ-test |
First public commit
Diffstat (limited to 'test/integ-test')
19 files changed, 417 insertions, 0 deletions
diff --git a/test/integ-test/INTEGTEST.md b/test/integ-test/INTEGTEST.md new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/integ-test/INTEGTEST.md diff --git a/test/integ-test/expected-results/configs-static-default.txt b/test/integ-test/expected-results/configs-static-default.txt new file mode 100644 index 0000000..0aa8329 --- /dev/null +++ b/test/integ-test/expected-results/configs-static-default.txt @@ -0,0 +1,4 @@ +<pre> +<a href="bootstrap.sh">bootstrap.sh</a> +<a href="rc.local-bootstrap">rc.local-bootstrap</a> +</pre> diff --git a/test/integ-test/expected-results/ipxemenu.txt b/test/integ-test/expected-results/ipxemenu.txt new file mode 100644 index 0000000..0560fcc --- /dev/null +++ b/test/integ-test/expected-results/ipxemenu.txt @@ -0,0 +1,12 @@ +#!ipxe +chain /poll/1/${netX/mac:hexhyp} +menu Choose target to boot +item /configs/coreos.ipxe coreos.ipxe +item /env/production/configs/coreos.ipxe coreos.ipxe [production] + +choose target +echo -n Enter hostname or none: +read hostname +set baseurl localhost:18888 +# Boot it as intended. +chain ${target} diff --git a/test/integ-test/expected-results/poll-k8s1-1.txt b/test/integ-test/expected-results/poll-k8s1-1.txt new file mode 100644 index 0000000..46043fd --- /dev/null +++ b/test/integ-test/expected-results/poll-k8s1-1.txt @@ -0,0 +1,14 @@ +#!ipxe + +set coreos-url http://stable.release.core-os.net/amd64-usr/1122.3.0 + +echo This will currently autologin into tty1 on the console. +echo From there you can su to root and install CoreOS to disk using: +echo coreos-install -d /dev/sda -C stable +echo You will probably need to chroot into /dev/sda9 to configure accounts. +echo More info @ http://coreos.com/docs/running-coreos/bare-metal/installing-to-disk/ + +kernel ${coreos-url}/coreos_production_pxe.vmlinuz cloud-config-url=http://localhost:18888/configs/coreos-baremetal.yaml?release=stable console=tty1 coreos.autologin=tty1 +initrd ${coreos-url}/coreos_production_pxe_image.cpio.gz + +boot diff --git a/test/integ-test/expected-results/poll-k8s1-2.txt b/test/integ-test/expected-results/poll-k8s1-2.txt new file mode 100644 index 0000000..46043fd --- /dev/null +++ b/test/integ-test/expected-results/poll-k8s1-2.txt @@ -0,0 +1,14 @@ +#!ipxe + +set coreos-url http://stable.release.core-os.net/amd64-usr/1122.3.0 + +echo This will currently autologin into tty1 on the console. +echo From there you can su to root and install CoreOS to disk using: +echo coreos-install -d /dev/sda -C stable +echo You will probably need to chroot into /dev/sda9 to configure accounts. +echo More info @ http://coreos.com/docs/running-coreos/bare-metal/installing-to-disk/ + +kernel ${coreos-url}/coreos_production_pxe.vmlinuz cloud-config-url=http://localhost:18888/configs/coreos-baremetal.yaml?release=stable console=tty1 coreos.autologin=tty1 +initrd ${coreos-url}/coreos_production_pxe_image.cpio.gz + +boot diff --git a/test/integ-test/expected-results/poll-k8s1-3-stg.txt b/test/integ-test/expected-results/poll-k8s1-3-stg.txt new file mode 100644 index 0000000..46043fd --- /dev/null +++ b/test/integ-test/expected-results/poll-k8s1-3-stg.txt @@ -0,0 +1,14 @@ +#!ipxe + +set coreos-url http://stable.release.core-os.net/amd64-usr/1122.3.0 + +echo This will currently autologin into tty1 on the console. +echo From there you can su to root and install CoreOS to disk using: +echo coreos-install -d /dev/sda -C stable +echo You will probably need to chroot into /dev/sda9 to configure accounts. +echo More info @ http://coreos.com/docs/running-coreos/bare-metal/installing-to-disk/ + +kernel ${coreos-url}/coreos_production_pxe.vmlinuz cloud-config-url=http://localhost:18888/configs/coreos-baremetal.yaml?release=stable console=tty1 coreos.autologin=tty1 +initrd ${coreos-url}/coreos_production_pxe_image.cpio.gz + +boot diff --git a/test/integ-test/expected-results/poll-k8s1-4-stg.txt b/test/integ-test/expected-results/poll-k8s1-4-stg.txt new file mode 100644 index 0000000..6195e9e --- /dev/null +++ b/test/integ-test/expected-results/poll-k8s1-4-stg.txt @@ -0,0 +1,14 @@ +#!ipxe + +set coreos-url http://stable.release.core-os.net/amd64-usr/1298.6.0 + +echo This will currently autologin into tty1 on the console. +echo From there you can su to root and install CoreOS to disk using: +echo coreos-install -d /dev/sda -C stable +echo You will probably need to chroot into /dev/sda9 to configure accounts. +echo More info @ http://coreos.com/docs/running-coreos/bare-metal/installing-to-disk/ + +kernel ${coreos-url}/coreos_production_pxe.vmlinuz cloud-config-url=http://localhost:18888/env/staging/configs/coreos-baremetal.yaml?release=stable console=tty1 coreos.autologin=tty1 +initrd ${coreos-url}/coreos_production_pxe_image.cpio.gz + +boot diff --git a/test/integ-test/expected-results/poll-unknown-set-from-ui.txt b/test/integ-test/expected-results/poll-unknown-set-from-ui.txt new file mode 100644 index 0000000..08e161e --- /dev/null +++ b/test/integ-test/expected-results/poll-unknown-set-from-ui.txt @@ -0,0 +1,14 @@ +#!ipxe + +set coreos-url http://stable.release.core-os.net/amd64-usr/666.0 + +echo This will currently autologin into tty1 on the console. +echo From there you can su to root and install CoreOS to disk using: +echo coreos-install -d /dev/sda -C stable +echo You will probably need to chroot into /dev/sda9 to configure accounts. +echo More info @ http://coreos.com/docs/running-coreos/bare-metal/installing-to-disk/ + +kernel ${coreos-url}/coreos_production_pxe.vmlinuz cloud-config-url=http://localhost:18888/configs/coreos-virtual.yaml?release=stable console=tty1 coreos.autologin=tty1 +initrd ${coreos-url}/coreos_production_pxe_image.cpio.gz + +boot diff --git a/test/integ-test/expected-results/poll-unknown.txt b/test/integ-test/expected-results/poll-unknown.txt new file mode 100644 index 0000000..f17022f --- /dev/null +++ b/test/integ-test/expected-results/poll-unknown.txt @@ -0,0 +1,2 @@ +#!ipxe +prompt --key 0x02 --timeout 10000 shoelaces: Press Ctrl-B for manual override... && chain -ar http://localhost:18888/ipxemenu || chain -ar http://localhost:18888/poll/1/06-66-de-ad-be-ef diff --git a/test/integ-test/expected-results/poll.txt b/test/integ-test/expected-results/poll.txt new file mode 100644 index 0000000..dbba541 --- /dev/null +++ b/test/integ-test/expected-results/poll.txt @@ -0,0 +1,2 @@ +#!ipxe +prompt --key 0x02 --timeout 10000 shoelaces: Press Ctrl-B for manual override... && chain -ar http://localhost:18888/ipxemenu || chain -ar http://localhost:18888/poll/1/ff-ff-ff-ff-ff-ff diff --git a/test/integ-test/expected-results/rc.local-bootstrap b/test/integ-test/expected-results/rc.local-bootstrap new file mode 100644 index 0000000..084ccc2 --- /dev/null +++ b/test/integ-test/expected-results/rc.local-bootstrap @@ -0,0 +1,4 @@ +#!/bin/sh + +/usr/local/sbin/bootstrap > /var/log/bootstrap.log 2>&1 & +exit 0 diff --git a/test/integ-test/expected-results/static.html b/test/integ-test/expected-results/static.html new file mode 100644 index 0000000..eff16d6 --- /dev/null +++ b/test/integ-test/expected-results/static.html @@ -0,0 +1,7 @@ +<pre> +<a href="css/">css/</a> +<a href="fonts/">fonts/</a> +<a href="img/">img/</a> +<a href="js/">js/</a> +<a href="templates/">templates/</a> +</pre> diff --git a/test/integ-test/integ-test-configs/env_overrides/production/ipxe/coreos.ipxe.slc b/test/integ-test/integ-test-configs/env_overrides/production/ipxe/coreos.ipxe.slc new file mode 100644 index 0000000..f60b478 --- /dev/null +++ b/test/integ-test/integ-test-configs/env_overrides/production/ipxe/coreos.ipxe.slc @@ -0,0 +1,16 @@ +{{define "coreos.ipxe" -}} +#!ipxe + +set coreos-url http://stable.release.core-os.net/amd64-usr/{{.version}} + +echo This will currently autologin into tty1 on the console. +echo From there you can su to root and install CoreOS to disk using: +echo coreos-install -d /dev/sda -C stable +echo You will probably need to chroot into /dev/sda9 to configure accounts. +echo More info @ http://coreos.com/docs/running-coreos/bare-metal/installing-to-disk/ + +kernel ${coreos-url}/coreos_production_pxe.vmlinuz cloud-config-url=http://{{.baseURL}}/configs/coreos-{{.cloudconfig}}.yaml?release=stable&hostname={{.hostname}} console=tty1 coreos.autologin=tty1 +initrd ${coreos-url}/coreos_production_pxe_image.cpio.gz + +boot +{{end}} diff --git a/test/integ-test/integ-test-configs/env_overrides/staging/preseed/example.preseed.slc b/test/integ-test/integ-test-configs/env_overrides/staging/preseed/example.preseed.slc new file mode 100644 index 0000000..824bcdd --- /dev/null +++ b/test/integ-test/integ-test-configs/env_overrides/staging/preseed/example.preseed.slc @@ -0,0 +1,14 @@ +{{define "example.preseed" -}} + +d-i partman-auto-raid/recipe string \ + 1 4 0 ext3 /boot \ + /dev/sda1#/dev/sdb1#/dev/sdc1#/dev/sdd1 \ + . \ + 10 4 0 lvm - \ + /dev/sda5#/dev/sdb5#/dev/sdc5#/dev/sdd5 \ + . + +d-i partman-auto/disk string /dev/sda /dev/sdb /dev/sdc /dev/sdd +d-i grub-installer/bootdev string /dev/sda /dev/sdb /dev/sdc /dev/sdd + +{{end}} diff --git a/test/integ-test/integ-test-configs/ipxe/coreos.ipxe.slc b/test/integ-test/integ-test-configs/ipxe/coreos.ipxe.slc new file mode 100644 index 0000000..7cb5156 --- /dev/null +++ b/test/integ-test/integ-test-configs/ipxe/coreos.ipxe.slc @@ -0,0 +1,16 @@ +{{define "coreos.ipxe" -}} +#!ipxe + +set coreos-url http://stable.release.core-os.net/amd64-usr/{{.version}} + +echo This will currently autologin into tty1 on the console. +echo From there you can su to root and install CoreOS to disk using: +echo coreos-install -d /dev/sda -C stable +echo You will probably need to chroot into /dev/sda9 to configure accounts. +echo More info @ http://coreos.com/docs/running-coreos/bare-metal/installing-to-disk/ + +kernel ${coreos-url}/coreos_production_pxe.vmlinuz cloud-config-url=http://{{.baseURL}}/configs/coreos-{{.cloudconfig}}.yaml?release=stable console=tty1 coreos.autologin=tty1 +initrd ${coreos-url}/coreos_production_pxe_image.cpio.gz + +boot +{{end}} diff --git a/test/integ-test/integ-test-configs/mappings.yaml b/test/integ-test/integ-test-configs/mappings.yaml new file mode 100644 index 0000000..534b72f --- /dev/null +++ b/test/integ-test/integ-test-configs/mappings.yaml @@ -0,0 +1,39 @@ +networkMaps: + - network: 20.20.20.20/24 + script: + name: ubuntu-minimal.ipxe + params: + hostname: placeholder + +hostnameMaps: + - hostname: '(etcd|k8s)\d-m\d' + script: + name: coreos.ipxe + params: + version: 1122.3.0 + cloudconfig: virtual + - hostname: '(etcd|k8s)\d-m\d' + script: + name: coreos.ipxe + params: + version: 1122.3.0 + cloudconfig: virtual + - hostname: 'k8s1-4' + script: + name: coreos.ipxe + environment: staging + params: + version: 1298.6.0 + cloudconfig: baremetal + - hostname: 'k8s1-\d' + script: + name: coreos.ipxe + params: + version: 1122.3.0 + cloudconfig: baremetal + - hostname: 'k8s1-\d' + script: + name: coreos.ipxe + params: + version: 1122.3.0 + cloudconfig: baremetal diff --git a/test/integ-test/integ-test-configs/static/bootstrap.sh b/test/integ-test/integ-test-configs/static/bootstrap.sh new file mode 100644 index 0000000..847ae99 --- /dev/null +++ b/test/integ-test/integ-test-configs/static/bootstrap.sh @@ -0,0 +1,11 @@ +#!/bin/bash +export DEBIAN_FRONTEND=noninteractive + +echo Example boostrap configuration +apt-get install hello + +echo '#!/bin/sh +exit 0' > /etc/rc.local + +# Don't want to run this accidentally. +chmod 0 /usr/local/sbin/bootstrap diff --git a/test/integ-test/integ-test-configs/static/rc.local-bootstrap b/test/integ-test/integ-test-configs/static/rc.local-bootstrap new file mode 100644 index 0000000..084ccc2 --- /dev/null +++ b/test/integ-test/integ-test-configs/static/rc.local-bootstrap @@ -0,0 +1,4 @@ +#!/bin/sh + +/usr/local/sbin/bootstrap > /var/log/bootstrap.log 2>&1 & +exit 0 diff --git a/test/integ-test/integ_test.py b/test/integ-test/integ_test.py new file mode 100755 index 0000000..4f02fe3 --- /dev/null +++ b/test/integ-test/integ_test.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python + +# Copyright 2018 ThousandEyes Inc. +# +# 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. + +""" Test shoelaces """ + +import os +import signal +import subprocess +import sys +import time +import tempfile +import string +import pytest +import requests +import datetime +import dateutil.parser +from requests.exceptions import RequestException + +API_HOST = 'localhost' +API_PORT = '18888' +API_URL = "http://{}:{}".format(API_HOST, API_PORT) +TEST_DIR = os.path.dirname(os.path.abspath(__file__)) +BASE_DIR = os.path.dirname(os.path.dirname(TEST_DIR)) +FIXTURE_DIR = os.path.join(TEST_DIR, 'expected-results') +STATIC_DIR = os.path.join(BASE_DIR, "web") +SHOELACES_BINARY = os.path.join(BASE_DIR, "shoelaces") + + +@pytest.fixture(scope="session", autouse=True) +def shoelaces_binary(): + os.chdir(BASE_DIR) + subprocess.check_call(["go", "build"]) + os.chdir(TEST_DIR) + + +@pytest.fixture(scope="session", autouse=True) +def config_file(shoelaces_binary): + """ Create a temporary config file """ + temp_config_tpl = string.Template("domain=$host\n" + "port=$port\n" + "data-dir=integ-test-configs\n" + "static-dir=$static_dir\n" + "template-extension=.slc\n" + "mappings-file=mappings.yaml\n" + "debug=true\n") + temp_config = temp_config_tpl.substitute(host=API_HOST, + port=API_PORT, + static_dir=STATIC_DIR) + + sys.stderr.write("Using:\n{}".format(temp_config)) + temp_cfg_file = tempfile.NamedTemporaryFile(delete=False) + temp_cfg_file.write(temp_config) + temp_cfg_file.flush() + temp_cfg_file_name = temp_cfg_file.name + temp_cfg_file.close() + yield temp_cfg_file_name + os.unlink(temp_cfg_file_name) + + +@pytest.fixture(scope="session", autouse=True) +def shoelaces_instance(config_file): + """ Shoelaces test fixture. """ + shoelaces_start_cmd = [SHOELACES_BINARY, "-config", config_file] + shoelaces = subprocess.Popen(shoelaces_start_cmd, preexec_fn=os.setsid) + sys.stderr.write("\nStarting Shoelaces...\n") + yield shoelaces + sys.stderr.write("\nShutting down Shoelaces...\n") + os.killpg(os.getpgid(shoelaces.pid), signal.SIGTERM) + sys.stderr.write("\nDone\n") + + +def test_shoelaces_startup(shoelaces_instance): + """ Test API liveness """ + attempts = 0 + while True: + try: + req = requests.get('{}/'.format(API_URL)) + req.raise_for_status() + sys.stderr.write('\n\nApi startup successful.\n') + break + except RequestException: + attempts += 1 + if attempts > 10: + raise + sys.stderr.write(".") + time.sleep(1) + + +@pytest.mark.parametrize(("path"), [("/"), ("/events"), ("/mappings")]) +def test_response_success(shoelaces_instance, path): + r = requests.get("{}{}".format(API_URL, path)) + r.raise_for_status() + + +REQUEST_RESPONSE_PAIRS = [("/static/", "static.html"), + ("/configs/static/", "configs-static-default.txt"), + ("/configs/static/rc.local-bootstrap", + "rc.local-bootstrap"), + ("/ipxemenu", "ipxemenu.txt")] + + +@pytest.mark.parametrize(("request_path", "response_file"), REQUEST_RESPONSE_PAIRS) +def test_request_response(shoelaces_instance, request_path, response_file): + with open(os.path.join(FIXTURE_DIR, response_file)) as response_body: + assert requests.get( + API_URL + request_path).text == response_body.read() + + +def gen_mac_server_pairs(): + generated = [] + for m in range(0x00, 0x100, 0x11): + o = "{:02x}".format(m) + generated.append({'IP': '127.0.0.1', 'Mac': "ff:ff:ff:ff:ff:{}".format(o), 'Hostname': 'localhost'}) + yield (o, list(generated)) + + +@pytest.mark.parametrize(("mac_last_octet", "servers"), gen_mac_server_pairs()) +def test_servers(shoelaces_instance, mac_last_octet, servers): + poll_url = "{}/poll/1/ff-ff-ff-ff-ff-{}".format(API_URL, mac_last_octet) + req = requests.get(poll_url) + req = requests.get("{}/ajax/servers".format(API_URL)) + assert sorted(req.json()) == sorted(servers) + + +def test_unknown_server(shoelaces_instance): + poll_url = "{}/poll/1/06-66-de-ad-be-ef".format(API_URL) + # Request for unknown host will give result in retries/polling + with open(os.path.join(FIXTURE_DIR, "poll-unknown.txt")) as poll: + assert requests.get(poll_url).text == poll.read() + # Setting the config for the new host should succeed. + requests.post(API_URL + '/update/target', + {"target": "coreos.ipxe", + "mac": "06:66:de:ad:be:ef", + "version": "666.0", + "cloudconfig": "virtual"}).raise_for_status() + # After setting we should be able to get the new config. + with open(os.path.join(FIXTURE_DIR, "poll-unknown-set-from-ui.txt")) as poll: + assert requests.get(poll_url).text == poll.read() + # Once fetched the host is now again "unknown" + with open(os.path.join(FIXTURE_DIR, "poll-unknown.txt")) as poll: + assert requests.get(poll_url).text == poll.read() + + +def test_events(shoelaces_instance): + url = "{}/ajax/events".format(API_URL) + req = requests.get(url) + req.raise_for_status() + res = req.json() + # assert mac is in dictionary + assert '06:66:de:ad:be:ef' in res + # assert array with one element + assert isinstance(res['06:66:de:ad:be:ef'], list) and len(res['06:66:de:ad:be:ef']) == 4 + # assert we have a date field + assert 'date' in res['06:66:de:ad:be:ef'][0] + # assert our date actually parses + assert dateutil.parser.parse(res['06:66:de:ad:be:ef'][0]['date']) + del res['06:66:de:ad:be:ef'][0]['date'] + # compare to the expected result sans the date as it would be different + assert sorted(res['06:66:de:ad:be:ef'][0]) == sorted({'eventType': '0', + 'message': '0', + 'bootType': 'Manual', + 'server': {'mac':'', + 'ip': '', + 'hostname': '06-66-de-ad-be-ef'}, + 'params': {'baseURL': 'localhost:18888', + 'cloudconfig': 'virtual', + 'hostname': '06-66-de-ad-be-ef', + 'version': '666.0'}, + 'script': 'coreos.ipxe'}) + + +POLL_PAIRS = [(None, "poll.txt"), + ({"host": "k8s1-3"}, "poll-k8s1-3-stg.txt"), + ({"host": "k8s1-4"}, "poll-k8s1-4-stg.txt"), + ({"host": "k8s1-1"}, "poll-k8s1-1.txt"), + ({"host": "k8s1-2"}, "poll-k8s1-2.txt")] + + +@pytest.mark.parametrize(("params", "expected"), POLL_PAIRS) +def test_poll(shoelaces_instance, params, expected): + """ Test Poll handler """ + poll_url = "{}/poll/1/ff-ff-ff-ff-ff-ff".format(API_URL) + req = requests.get(poll_url, params=params) + req.raise_for_status() + with open(os.path.join(FIXTURE_DIR, expected), 'r') as poll: + assert poll.read() == req.text + + +TPL_VARS_PAIRS = [("coreos.ipxe", "", ["cloudconfig", "version"]), + ("coreos.ipxe", "default", ["cloudconfig", "version"]), + ("coreos.ipxe", "production", ["cloudconfig", "version", "hostname"])] + + +@pytest.mark.parametrize(("script", "env", "vars"), TPL_VARS_PAIRS) +def test_template_variables_list(shoelaces_instance, script, env, vars): + url = "{}/ajax/script/params".format(API_URL) + req = requests.get(url, params={"script": script, "environment": env}) + req.raise_for_status() + assert sorted(req.json()) == sorted(vars) + + +if __name__ == "__main__": + pytest.main(args=['-v'], plugins=None) |