From 831c0ad80a5db78deded81255ade965907de4759 Mon Sep 17 00:00:00 2001 From: Marcin Krolik Date: Fri, 16 Sep 2016 16:46:21 +0200 Subject: [PATCH] Large tests and runnable example --- .gitmodules | 3 + examples/tasks/README.md | 27 ++++ examples/tasks/docker-compose.yml | 11 ++ examples/tasks/mock-load.sh | 53 ++++++++ examples/tasks/run-mock-load.sh | 18 +++ .../load-file.json => tasks/task-load.json} | 8 +- scripts/config/k8s-deployment.yml | 26 ++++ scripts/docker/large/docker-compose.yml | 10 ++ scripts/large_compose.sh | 56 ++++++++ scripts/large_k8s.sh | 37 ++++++ scripts/test/__init__.py | 0 scripts/test/large.py | 125 ++++++++++++++++++ scripts/test/pytest | 1 + 13 files changed, 371 insertions(+), 4 deletions(-) create mode 100644 .gitmodules create mode 100644 examples/tasks/README.md create mode 100644 examples/tasks/docker-compose.yml create mode 100755 examples/tasks/mock-load.sh create mode 100755 examples/tasks/run-mock-load.sh rename examples/{task/load-file.json => tasks/task-load.json} (79%) create mode 100644 scripts/config/k8s-deployment.yml create mode 100644 scripts/docker/large/docker-compose.yml create mode 100755 scripts/large_compose.sh create mode 100755 scripts/large_k8s.sh create mode 100644 scripts/test/__init__.py create mode 100644 scripts/test/large.py create mode 160000 scripts/test/pytest diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..3995ddb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "scripts/test/pytest"] + path = scripts/test/pytest + url = https://github.com/marcin-krolik/snap-pytest diff --git a/examples/tasks/README.md b/examples/tasks/README.md new file mode 100644 index 0000000..735eb7a --- /dev/null +++ b/examples/tasks/README.md @@ -0,0 +1,27 @@ +# Example tasks + +[This](task-load.json) example task will collect metrics from **load** and publish +them to file. + +## Running the example + +### Requirements + * `docker` and `docker-compose` are **installed** and **configured** + +Running the sample is as *easy* as running the script `./run-mock-load.sh`. + +## Files +- [mock-load.sh](mock-load.sh) + - Downloads `snapd`, `snapctl`, `snap-plugin-collector-load`, + `snap-plugin-publisher-mock-file` and starts the task +- [run-mock-load.sh](run-mock-load.sh) + - The example is launched with this script +- [task-load.json](task-load.json) + - Snap task definition +- [.setup.sh](.setup.sh) + - Verifies dependencies and starts the containers. It's called + by [run-mock-load.sh](run-mock-load.sh). +- [docker-compose.yml](docker-compose.yml) + - A docker compose file which defines "runner" container where snapd + is run from. You will be dumped into a shell in this container + after running. \ No newline at end of file diff --git a/examples/tasks/docker-compose.yml b/examples/tasks/docker-compose.yml new file mode 100644 index 0000000..0c550c5 --- /dev/null +++ b/examples/tasks/docker-compose.yml @@ -0,0 +1,11 @@ +version: '2' +services: + main: + container_name: runner + image: intelsdi/snap:alpine + environment: + - SNAP_VERSION=latest + volumes: + - ${PLUGIN_SRC}:/snap-plugin-collector-load + - /proc:/var/procfs + network_mode: "host" \ No newline at end of file diff --git a/examples/tasks/mock-load.sh b/examples/tasks/mock-load.sh new file mode 100755 index 0000000..f62faba --- /dev/null +++ b/examples/tasks/mock-load.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +set -e +set -u +set -o pipefail + +# get the directory the script exists in +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# source the common bash script +. "${__dir}/../../scripts/common.sh" + +# ensure PLUGIN_PATH is set +TMPDIR=${TMPDIR:-"/tmp"} +PLUGIN_PATH=${PLUGIN_PATH:-"${TMPDIR}/snap/plugins"} +mkdir -p $PLUGIN_PATH + +_info "downloading plugins" +(cd $PLUGIN_PATH && curl -sSO http://snap.ci.snap-telemetry.io/snap/master/latest/snap-plugin-publisher-mock-file && chmod 755 snap-plugin-publisher-mock-file) +(cd $PLUGIN_PATH && curl -sSO http://snap.ci.snap-telemetry.io/snap/master/latest/snap-plugin-processor-passthru && chmod 755 snap-plugin-processor-passthru) +(cd $PLUGIN_PATH && curl -sSO http://snap.ci.snap-telemetry.io/plugins/snap-plugin-collector-load/latest/linux/x86_64/snap-plugin-collector-load && chmod 755 snap-plugin-collector-load) + +SNAP_FLAG=0 + +# this block will wait check if snapctl and snapd are loaded before the plugins are loaded and the task is started + for i in `seq 1 10`; do + _info "try ${i}" + if [[ -f /usr/local/bin/snapctl && -f /usr/local/bin/snapd ]]; + then + _info "loading plugins" + snapctl plugin load "${PLUGIN_PATH}/snap-plugin-publisher-mock-file" + snapctl plugin load "${PLUGIN_PATH}/snap-plugin-processor-passthru" + snapctl plugin load "${PLUGIN_PATH}/snap-plugin-collector-load" + + _info "creating and starting a task" + snapctl task create -t "${__dir}/task-load.json" + + SNAP_FLAG=1 + + break + fi + + _info "snapctl and/or snapd are unavailable, sleeping for 5 seconds" + sleep 5 +done + + +# check if snapctl/snapd have loaded +if [ $SNAP_FLAG -eq 0 ] + then + echo "Could not load snapctl or snapd" + exit 1 +fi \ No newline at end of file diff --git a/examples/tasks/run-mock-load.sh b/examples/tasks/run-mock-load.sh new file mode 100755 index 0000000..d1ad024 --- /dev/null +++ b/examples/tasks/run-mock-load.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e +set -u +set -o pipefail + +# get the directory the script exists in +__dir=$(cd $(dirname ${BASH_SOURCE[0]})/../../scripts/docker/large && pwd) +__proj_dir="$(cd $(dirname ${BASH_SOURCE[0]})/../../ && pwd)" +__proj_name="$(basename $__proj_dir)" + +export PLUGIN_SRC="${__proj_dir}" + +# verifies dependencies +. "${__proj_dir}/examples/tasks/.setup.sh" + +# downloads plugins, starts snap, load plugins and start a task +cd "${__proj_dir}/examples/tasks" && docker-compose exec main bash -c "PLUGIN_PATH=/etc/snap/plugins /${__proj_name}/examples/tasks/mock-load.sh && printf \"\n\nhint: type 'snapctl task list'\ntype 'exit' when your done\n\n\" && bash" \ No newline at end of file diff --git a/examples/task/load-file.json b/examples/tasks/task-load.json similarity index 79% rename from examples/task/load-file.json rename to examples/tasks/task-load.json index a2d77f0..4854fe6 100644 --- a/examples/task/load-file.json +++ b/examples/tasks/task-load.json @@ -9,11 +9,11 @@ "metrics": { "/intel/procfs/load/min1": {}, "/intel/procfs/load/min15": {}, - "/intel/procfs/load/scheduling": {} + "/intel/procfs/load/runnable_scheduling": {} }, "config": { - "/intel/procfs/load/": { - "proc_path": "/var/procfs/loadavg" + "/intel/procfs/load": { + "proc_path": "/var/procfs" } }, "process": [ @@ -22,7 +22,7 @@ "process": null, "publish": [ { - "plugin_name": "file", + "plugin_name": "mock-file", "config": { "file": "/tmp/published_load" } diff --git a/scripts/config/k8s-deployment.yml b/scripts/config/k8s-deployment.yml new file mode 100644 index 0000000..cb81709 --- /dev/null +++ b/scripts/config/k8s-deployment.yml @@ -0,0 +1,26 @@ +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: load-deployment +spec: + replicas: 1 + template: + metadata: + labels: + app: load-large-test + spec: + containers: + - name: load-large-test + image: mkrolik/snap-pytest:lscpu + imagePullPolicy: "IfNotPresent" + volumeMounts: + - mountPath: /var/procfs + name: procfs + command: + - sleep + - "3000" + volumes: + - name: procfs + hostPath: + path: /proc \ No newline at end of file diff --git a/scripts/docker/large/docker-compose.yml b/scripts/docker/large/docker-compose.yml new file mode 100644 index 0000000..25f98ac --- /dev/null +++ b/scripts/docker/large/docker-compose.yml @@ -0,0 +1,10 @@ +version: '2' +services: + main: + container_name: load_large_test + image: mkrolik/python-apline:lscpu + network_mode: "host" + volumes: + - ${PLUGIN_SRC}:/snap-plugin-collector-load + - /proc:/var/procfs + entrypoint: sh -c 'python /snap-plugin-collector-load/scripts/test/large.py' \ No newline at end of file diff --git a/scripts/large_compose.sh b/scripts/large_compose.sh new file mode 100755 index 0000000..0922edd --- /dev/null +++ b/scripts/large_compose.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +#http://www.apache.org/licenses/LICENSE-2.0.txt +# +# +#Copyright 2016 Intel Corporation +# +#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. + +set -e +set -u +set -o pipefail + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__proj_dir="$(dirname "$__dir")" +__proj_name="$(basename $__proj_dir)" + +. "${__dir}/common.sh" + +# NOTE: these variables control the docker-compose image. +export PLUGIN_SRC="${__proj_dir}" +export LOG_LEVEL="${LOG_LEVEL:-"7"}" +export PROJECT_NAME="${__proj_name}" + +TEST_TYPE="${TEST_TYPE:-"large"}" + +docker_folder="${__proj_dir}/scripts/docker/${TEST_TYPE}" + +_docker_project () { + echo ${docker_folder} + cd "${docker_folder}" && "$@" +} + +_debug "updating repository submodule with pytest" +git submodule update --init --recursive + +_debug "building docker compose images" +_docker_project docker-compose build +_debug "running test: ${TEST_TYPE}" +_docker_project docker-compose up +test_res=`docker-compose ps -q | xargs docker inspect -f '{{ .Name }} exited with status {{ .State.ExitCode }}' | awk '{print $5}'` +echo "exit code from large_jenkins $test_res" +_debug "stopping docker compose images" +_docker_project docker-compose stop +_docker_project docker-compose rm -f +exit $test_res \ No newline at end of file diff --git a/scripts/large_k8s.sh b/scripts/large_k8s.sh new file mode 100755 index 0000000..02928c0 --- /dev/null +++ b/scripts/large_k8s.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +#http://www.apache.org/licenses/LICENSE-2.0.txt +# +# +#Copyright 2016 Intel Corporation +# +#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. + +set -e +set -u +set -o pipefail + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__proj_dir="$(dirname "$__dir")" +__proj_name="$(basename $__proj_dir)" + +. "${__dir}/common.sh" + +_info "updating repository submodule with pytest" +cd ${__proj_dir} && git submodule update --init --recursive + +export PROJECT_NAME="${__proj_name}" + +_info "execute large test" +test_result=`python "${__proj_dir}/scripts/test/large.py"` +exit $test_result \ No newline at end of file diff --git a/scripts/test/__init__.py b/scripts/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/test/large.py b/scripts/test/large.py new file mode 100644 index 0000000..d4b4854 --- /dev/null +++ b/scripts/test/large.py @@ -0,0 +1,125 @@ +#http://www.apache.org/licenses/LICENSE-2.0.txt +# +# +#Copyright 2016 Intel Corporation +# +#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. + +import sys +import os +import unittest + +from pytest import bins +from pytest import utils +from pytest.logger import log +from unittest import TextTestRunner + + +class LoadCollectorLargeTest(unittest.TestCase): + + def setUp(self): + plugins_dir = os.getenv("PLUGINS_DIR", "/etc/snap/plugins") + snap_dir = os.getenv("SNAP_DIR", "/usr/local/bin") + + snapd_url = "http://snap.ci.snap-telemetry.io/snap/master/latest/snapd" + snapctl_url = "http://snap.ci.snap-telemetry.io/snap/master/latest/snapctl" + load_url = "http://snap.ci.snap-telemetry.io/plugins/snap-plugin-collector-load/latest_build/linux/x86_64/snap-plugin-collector-load" + passthru_url = "http://snap.ci.snap-telemetry.io/snap/master/latest/snap-plugin-processor-passthru" + mockfile_url = "http://snap.ci.snap-telemetry.io/snap/master/latest/snap-plugin-publisher-mock-file" + + # set and download required binaries (snapd, snapctl, plugins) + self.binaries = bins.Binaries() + self.binaries.snapd = bins.Snapd(snapd_url, snap_dir) + self.binaries.snapctl = bins.Snapctl(snapctl_url, snap_dir) + self.binaries.collector = bins.Plugin(load_url, plugins_dir, "collector", 3) + self.binaries.processor = bins.Plugin(passthru_url, plugins_dir, "processor", -1) + self.binaries.publisher = bins.Plugin(mockfile_url, plugins_dir, "publisher", -1) + + utils.download_binaries(self.binaries) + + self.task_file = "/{}/examples/tasks/task-load.json".format( + os.getenv("PROJECT_NAME", "snap-plugin-collector-load")) + + log.info("starting snapd") + self.binaries.snapd.start() + if not self.binaries.snapd.isAlive(): + self.fail("snapd thread died") + + log.debug("Waiting for snapd to finish starting") + if not self.binaries.snapd.wait(): + log.error("snapd errors: {}".format(self.binaries.snapd.errors)) + self.binaries.snapd.kill() + self.fail("snapd not ready, timeout!") + + def test_load_collector_plugin(self): + # load plugins + for plugin in self.binaries.get_all_plugins(): + log.info("snapctl plugin load {}".format(os.path.join(plugin.dir, plugin.name))) + loaded = self.binaries.snapctl.load_plugin(plugin) + self.assertTrue(loaded, "{} loaded".format(plugin.name)) + + # check available metrics, plugins and tasks + metrics = self.binaries.snapctl.list_metrics() + plugins = self.binaries.snapctl.list_plugins() + tasks = self.binaries.snapctl.list_tasks() + self.assertGreater(len(metrics), 0, "Metrics available {} expected {}".format(len(metrics), 0)) + self.assertEqual(len(plugins), 3, "Plugins available {} expected {}".format(len(plugins), 3)) + self.assertEqual(len(tasks), 0, "Tasks available {} expected {}".format(len(tasks), 0)) + + # check config policy for metric + rules = self.binaries.snapctl.metric_get("/intel/procfs/load/min5") + self.assertEqual(len(rules), 1, "Rules available {} expected {}".format(len(rules), 1)) + + # create and list available task + log.info("snapctl task create -t {}".format(self.task_file)) + task_id = self.binaries.snapctl.create_task(self.task_file) + tasks = self.binaries.snapctl.list_tasks() + self.assertEqual(len(tasks), 1, "Tasks available {} expected {}".format(len(tasks), 1)) + + # check if task hits and fails + hits = self.binaries.snapctl.task_hits_count(task_id) + fails = self.binaries.snapctl.task_fails_count(task_id) + self.assertGreater(hits, 0, "Task hits {} expected {}".format(hits, ">0")) + self.assertEqual(fails, 0, "Task fails {} expected {}".format(fails, 0)) + + # stop task and list available tasks + log.info("snapctl task stop {}".format(task_id)) + stopped = self.binaries.snapctl.stop_task(task_id) + self.assertTrue(stopped, "Task stopped") + tasks = self.binaries.snapctl.list_tasks() + self.assertEqual(len(tasks), 1, "Tasks available {} expected {}".format(len(tasks), 1)) + + # unload plugin, list metrics and plugins + log.info("snapctl plugin unload {}".format(self.binaries.collector)) + self.binaries.snapctl.unload_plugin(self.binaries.collector) + metrics = self.binaries.snapctl.list_metrics() + plugins = self.binaries.snapctl.list_plugins() + self.assertEqual(len(metrics), 0, "Metrics available {} expected {}".format(len(metrics), 0)) + self.assertEqual(len(plugins), 2, "Plugins available {} expected {}".format(len(plugins), 2)) + + # check for snapd errors + self.assertEqual(len(self.binaries.snapd.errors), 0, "Errors found during snapd execution") + + def tearDown(self): + log.info("stopping snapd") + self.binaries.snapd.stop() + if self.binaries.snapd.isAlive(): + log.warn("snapd thread did not die") + +if __name__ == "__main__": + test_suite = unittest.TestLoader().loadTestsFromTestCase(LoadCollectorLargeTest) + test_result = TextTestRunner().run(test_suite) + # exit with return code equal to number of failures + sys.exit(len(test_result.failures)) + + diff --git a/scripts/test/pytest b/scripts/test/pytest new file mode 160000 index 0000000..38fd075 --- /dev/null +++ b/scripts/test/pytest @@ -0,0 +1 @@ +Subproject commit 38fd075b0992e3c339adeb4ded7a872de8240977