Tox + Integration testing
This commit implements a VNF Descriptor-driven integration
test framework, which will lead to integration tests being able
to run via jenkins, and more robust testing in general.
N2VC:
- Allow the use of an event loop passed when instantiating N2VC
- Refactor the execution of the initial-config-primitive so that it can
be easily re-run, such as the case of when a proxy charm is deployed
before the VNF's VM is created.
- Refactor GetPrimitiveStatus, to return the status (queued, running,
complete, failed) of a primitive.
- Add GetPrimitiveOutput, to return the output of a completed primitive
- Fix model disconnection when executing a primitive (it was happening
in the wrong scope)
- Fix wait_for_application, which was previously unused and broken
- Add support for parameter's 'data-type' field
- Add support for better SSH key management, allowing for a proxy charm
to be deployed before the VNF, so that it's public SSH key can be
injected when the VNF's VM is created.
Integration Tests:
The integration tests are intended to exercise the expected
functionality of a VNF/charm: deploy the charm, configure it as required
(i.e., ssh credentials), and execute the VNF's
initial-config-primitives.
- test_native_charm: deploy a native charm to a juju-managed machine and
verify primitive execution works
- test_proxy_charm: deploy a proxy charm, configured to talk to a remote
machine, and verify primitive execution works
- test_metrics_native: deploy a native charm and collect a metric
- test_metrics_proxy: deploy a proxy charm and collect a metric from the
vnf
- test_no_initial-config-primitive: deploy a vnf without an
initial-config-primitive
- test_non-string_parameter: deploy a vnf with a non-string parameter in
initial-config-primitive
- test_no_parameter: deploy a vnf with a primitive with no parameters
General:
- Add a build target to tox.ini so that a .deb is built via Jenkins
TODO (in a follow-up commit):
- test multi-vdu, multi-charm
- test deploying a native charm to a manually-provisioned machine
- Update inline pydoc
- Add more integration tests
- Add global per-test timeout to catch stalled tests
Signed-off-by: Adam Israel <adam.israel@canonical.com>
Change-Id: Id322b45d65c44714e8051fc5764f8c20b76d846c
diff --git a/tests/charms/layers/metrics-ci/deps/layer/basic b/tests/charms/layers/metrics-ci/deps/layer/basic
deleted file mode 160000
index d59d361..0000000
--- a/tests/charms/layers/metrics-ci/deps/layer/basic
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit d59d3613006a5afe1b9322aed9d77b5945b44356
diff --git a/tests/charms/layers/metrics-ci/deps/layer/metrics b/tests/charms/layers/metrics-ci/deps/layer/metrics
deleted file mode 160000
index 6861ce3..0000000
--- a/tests/charms/layers/metrics-ci/deps/layer/metrics
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 6861ce384f0dcf4e3eb1eaddf421143f4f76e64e
diff --git a/tests/charms/layers/metrics-ci/deps/layer/options b/tests/charms/layers/metrics-ci/deps/layer/options
deleted file mode 160000
index fcdcea4..0000000
--- a/tests/charms/layers/metrics-ci/deps/layer/options
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit fcdcea4e5de3e1556c24e6704607862d0ba00a56
diff --git a/tests/charms/layers/metrics-ci/icon.svg b/tests/charms/layers/metrics-ci/icon.svg
deleted file mode 100755
index e092eef..0000000
--- a/tests/charms/layers/metrics-ci/icon.svg
+++ /dev/null
@@ -1,279 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="96"
- height="96"
- id="svg6517"
- version="1.1"
- inkscape:version="0.48+devel r12274"
- sodipodi:docname="Juju_charm_icon_template.svg">
- <defs
- id="defs6519">
- <linearGradient
- inkscape:collect="always"
- xlink:href="#Background"
- id="linearGradient6461"
- gradientUnits="userSpaceOnUse"
- x1="0"
- y1="970.29498"
- x2="144"
- y2="970.29498"
- gradientTransform="matrix(0,-0.66666669,0.6660448,0,-866.25992,731.29077)" />
- <linearGradient
- id="Background">
- <stop
- id="stop4178"
- offset="0"
- style="stop-color:#b8b8b8;stop-opacity:1" />
- <stop
- id="stop4180"
- offset="1"
- style="stop-color:#c9c9c9;stop-opacity:1" />
- </linearGradient>
- <filter
- style="color-interpolation-filters:sRGB;"
- inkscape:label="Inner Shadow"
- id="filter1121">
- <feFlood
- flood-opacity="0.59999999999999998"
- flood-color="rgb(0,0,0)"
- result="flood"
- id="feFlood1123" />
- <feComposite
- in="flood"
- in2="SourceGraphic"
- operator="out"
- result="composite1"
- id="feComposite1125" />
- <feGaussianBlur
- in="composite1"
- stdDeviation="1"
- result="blur"
- id="feGaussianBlur1127" />
- <feOffset
- dx="0"
- dy="2"
- result="offset"
- id="feOffset1129" />
- <feComposite
- in="offset"
- in2="SourceGraphic"
- operator="atop"
- result="composite2"
- id="feComposite1131" />
- </filter>
- <filter
- style="color-interpolation-filters:sRGB;"
- inkscape:label="Drop Shadow"
- id="filter950">
- <feFlood
- flood-opacity="0.25"
- flood-color="rgb(0,0,0)"
- result="flood"
- id="feFlood952" />
- <feComposite
- in="flood"
- in2="SourceGraphic"
- operator="in"
- result="composite1"
- id="feComposite954" />
- <feGaussianBlur
- in="composite1"
- stdDeviation="1"
- result="blur"
- id="feGaussianBlur956" />
- <feOffset
- dx="0"
- dy="1"
- result="offset"
- id="feOffset958" />
- <feComposite
- in="SourceGraphic"
- in2="offset"
- operator="over"
- result="composite2"
- id="feComposite960" />
- </filter>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath873">
- <g
- transform="matrix(0,-0.66666667,0.66604479,0,-258.25992,677.00001)"
- id="g875"
- inkscape:label="Layer 1"
- style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline">
- <path
- style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline"
- d="m 46.702703,898.22775 50.594594,0 C 138.16216,898.22775 144,904.06497 144,944.92583 l 0,50.73846 c 0,40.86071 -5.83784,46.69791 -46.702703,46.69791 l -50.594594,0 C 5.8378378,1042.3622 0,1036.525 0,995.66429 L 0,944.92583 C 0,904.06497 5.8378378,898.22775 46.702703,898.22775 Z"
- id="path877"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="sssssssss" />
- </g>
- </clipPath>
- <filter
- inkscape:collect="always"
- id="filter891"
- inkscape:label="Badge Shadow">
- <feGaussianBlur
- inkscape:collect="always"
- stdDeviation="0.71999962"
- id="feGaussianBlur893" />
- </filter>
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="4.0745362"
- inkscape:cx="18.514671"
- inkscape:cy="49.018169"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="true"
- fit-margin-top="0"
- fit-margin-left="0"
- fit-margin-right="0"
- fit-margin-bottom="0"
- inkscape:window-width="1920"
- inkscape:window-height="1029"
- inkscape:window-x="0"
- inkscape:window-y="24"
- inkscape:window-maximized="1"
- showborder="true"
- showguides="true"
- inkscape:guide-bbox="true"
- inkscape:showpageshadow="false">
- <inkscape:grid
- type="xygrid"
- id="grid821" />
- <sodipodi:guide
- orientation="1,0"
- position="16,48"
- id="guide823" />
- <sodipodi:guide
- orientation="0,1"
- position="64,80"
- id="guide825" />
- <sodipodi:guide
- orientation="1,0"
- position="80,40"
- id="guide827" />
- <sodipodi:guide
- orientation="0,1"
- position="64,16"
- id="guide829" />
- </sodipodi:namedview>
- <metadata
- id="metadata6522">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="BACKGROUND"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(268,-635.29076)"
- style="display:inline">
- <path
- style="fill:url(#linearGradient6461);fill-opacity:1;stroke:none;display:inline;filter:url(#filter1121)"
- d="m -268,700.15563 0,-33.72973 c 0,-27.24324 3.88785,-31.13513 31.10302,-31.13513 l 33.79408,0 c 27.21507,0 31.1029,3.89189 31.1029,31.13513 l 0,33.72973 c 0,27.24325 -3.88783,31.13514 -31.1029,31.13514 l -33.79408,0 C -264.11215,731.29077 -268,727.39888 -268,700.15563 Z"
- id="path6455"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="sssssssss" />
- </g>
- <g
- inkscape:groupmode="layer"
- id="layer3"
- inkscape:label="PLACE YOUR PICTOGRAM HERE"
- style="display:inline" />
- <g
- inkscape:groupmode="layer"
- id="layer2"
- inkscape:label="BADGE"
- style="display:none"
- sodipodi:insensitive="true">
- <g
- style="display:inline"
- transform="translate(-340.00001,-581)"
- id="g4394"
- clip-path="none">
- <g
- id="g855">
- <g
- inkscape:groupmode="maskhelper"
- id="g870"
- clip-path="url(#clipPath873)"
- style="opacity:0.6;filter:url(#filter891)">
- <path
- transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-237.54282)"
- d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"
- sodipodi:ry="12"
- sodipodi:rx="12"
- sodipodi:cy="552.36218"
- sodipodi:cx="252"
- id="path844"
- style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- sodipodi:type="arc" />
- </g>
- <g
- id="g862">
- <path
- sodipodi:type="arc"
- style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- id="path4398"
- sodipodi:cx="252"
- sodipodi:cy="552.36218"
- sodipodi:rx="12"
- sodipodi:ry="12"
- d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"
- transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-238.54282)" />
- <path
- transform="matrix(1.25,0,0,1.25,33,-100.45273)"
- d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"
- sodipodi:ry="12"
- sodipodi:rx="12"
- sodipodi:cy="552.36218"
- sodipodi:cx="252"
- id="path4400"
- style="color:#000000;fill:#dd4814;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- sodipodi:type="arc" />
- <path
- sodipodi:type="star"
- style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- id="path4459"
- sodipodi:sides="5"
- sodipodi:cx="666.19574"
- sodipodi:cy="589.50385"
- sodipodi:r1="7.2431178"
- sodipodi:r2="4.3458705"
- sodipodi:arg1="1.0471976"
- sodipodi:arg2="1.6755161"
- inkscape:flatsided="false"
- inkscape:rounded="0.1"
- inkscape:randomized="0"
- d="m 669.8173,595.77657 c -0.39132,0.22593 -3.62645,-1.90343 -4.07583,-1.95066 -0.44938,-0.0472 -4.05653,1.36297 -4.39232,1.06062 -0.3358,-0.30235 0.68963,-4.03715 0.59569,-4.47913 -0.0939,-0.44198 -2.5498,-3.43681 -2.36602,-3.8496 0.18379,-0.41279 4.05267,-0.59166 4.44398,-0.81759 0.39132,-0.22593 2.48067,-3.48704 2.93005,-3.4398 0.44938,0.0472 1.81505,3.67147 2.15084,3.97382 0.3358,0.30236 4.08294,1.2817 4.17689,1.72369 0.0939,0.44198 -2.9309,2.86076 -3.11469,3.27355 C 669.9821,591.68426 670.20862,595.55064 669.8173,595.77657 Z"
- transform="matrix(1.511423,-0.16366377,0.16366377,1.511423,-755.37346,-191.93651)" />
- </g>
- </g>
- </g>
- </g>
-</svg>
diff --git a/tests/charms/layers/metrics-proxy-ci/README.ex b/tests/charms/layers/metrics-proxy-ci/README.ex
new file mode 100644
index 0000000..b6816b2
--- /dev/null
+++ b/tests/charms/layers/metrics-proxy-ci/README.ex
@@ -0,0 +1,65 @@
+# Overview
+
+Describe the intended usage of this charm and anything unique about how this
+charm relates to others here.
+
+This README will be displayed in the Charm Store, it should be either Markdown
+or RST. Ideal READMEs include instructions on how to use the charm, expected
+usage, and charm features that your audience might be interested in. For an
+example of a well written README check out Hadoop:
+http://jujucharms.com/charms/precise/hadoop
+
+Use this as a Markdown reference if you need help with the formatting of this
+README: http://askubuntu.com/editing-help
+
+This charm provides [service][]. Add a description here of what the service
+itself actually does.
+
+Also remember to check the [icon guidelines][] so that your charm looks good
+in the Juju GUI.
+
+# Usage
+
+Step by step instructions on using the charm:
+
+juju deploy servicename
+
+and so on. If you're providing a web service or something that the end user
+needs to go to, tell them here, especially if you're deploying a service that
+might listen to a non-default port.
+
+You can then browse to http://ip-address to configure the service.
+
+## Scale out Usage
+
+If the charm has any recommendations for running at scale, outline them in
+examples here. For example if you have a memcached relation that improves
+performance, mention it here.
+
+## Known Limitations and Issues
+
+This not only helps users but gives people a place to start if they want to help
+you add features to your charm.
+
+# Configuration
+
+The configuration options will be listed on the charm store, however If you're
+making assumptions or opinionated decisions in the charm (like setting a default
+administrator password), you should detail that here so the user knows how to
+change it immediately, etc.
+
+# Contact Information
+
+Though this will be listed in the charm store itself don't assume a user will
+know that, so include that information here:
+
+## Upstream Project Name
+
+ - Upstream website
+ - Upstream bug tracker
+ - Upstream mailing list or contact information
+ - Feel free to add things if it's useful for users
+
+
+[service]: http://example.com
+[icon guidelines]: https://jujucharms.com/docs/stable/authors-charm-icon
diff --git a/tests/charms/layers/metrics-proxy-ci/config.yaml b/tests/charms/layers/metrics-proxy-ci/config.yaml
new file mode 100644
index 0000000..51f2ce4
--- /dev/null
+++ b/tests/charms/layers/metrics-proxy-ci/config.yaml
@@ -0,0 +1,14 @@
+options:
+ string-option:
+ type: string
+ default: "Default Value"
+ description: "A short description of the configuration option"
+ boolean-option:
+ type: boolean
+ default: False
+ description: "A short description of the configuration option"
+ int-option:
+ type: int
+ default: 9001
+ description: "A short description of the configuration option"
+
diff --git a/tests/charms/layers/metrics-proxy-ci/layer.yaml b/tests/charms/layers/metrics-proxy-ci/layer.yaml
new file mode 100644
index 0000000..790dee6
--- /dev/null
+++ b/tests/charms/layers/metrics-proxy-ci/layer.yaml
@@ -0,0 +1,4 @@
+includes:
+ - 'layer:basic'
+ - 'layer:vnfproxy'
+ - 'layer:sshproxy'
diff --git a/tests/charms/layers/metrics-proxy-ci/metadata.yaml b/tests/charms/layers/metrics-proxy-ci/metadata.yaml
new file mode 100644
index 0000000..ae42434
--- /dev/null
+++ b/tests/charms/layers/metrics-proxy-ci/metadata.yaml
@@ -0,0 +1,12 @@
+name: metrics-proxy-ci
+summary: <Fill in summary here>
+maintainer: Adam Israel <Adam.Israel@ronin>
+description: |
+ <Multi-line description here>
+tags:
+ # Replace "misc" with one or more whitelisted tags from this list:
+ # https://jujucharms.com/docs/stable/authors-charm-metadata
+ - misc
+subordinate: false
+series:
+ - xenial
diff --git a/tests/charms/layers/metrics-proxy-ci/metrics.yaml b/tests/charms/layers/metrics-proxy-ci/metrics.yaml
new file mode 100644
index 0000000..dae092f
--- /dev/null
+++ b/tests/charms/layers/metrics-proxy-ci/metrics.yaml
@@ -0,0 +1,9 @@
+metrics:
+ users:
+ type: gauge
+ description: "# of users"
+ command: who|wc -l
+ load:
+ type: gauge
+ description: "5 minute load average"
+ command: cat /proc/loadavg |awk '{print $1}'
diff --git a/tests/charms/layers/metrics-proxy-ci/reactive/metrics_ci.py b/tests/charms/layers/metrics-proxy-ci/reactive/metrics_ci.py
new file mode 100644
index 0000000..51ce49e
--- /dev/null
+++ b/tests/charms/layers/metrics-proxy-ci/reactive/metrics_ci.py
@@ -0,0 +1,13 @@
+from charmhelpers.core.hookenv import (
+ status_set,
+)
+from charms.reactive import (
+ set_flag,
+ when_not,
+)
+
+
+@when_not('metrics-ci.installed')
+def install_metrics_ci():
+ status_set('blocked', "Waiting for SSH credentials.")
+ set_flag('metrics-ci.installed')
diff --git a/tests/charms/layers/metrics-proxy-ci/tests/00-setup b/tests/charms/layers/metrics-proxy-ci/tests/00-setup
new file mode 100644
index 0000000..f0616a5
--- /dev/null
+++ b/tests/charms/layers/metrics-proxy-ci/tests/00-setup
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+sudo add-apt-repository ppa:juju/stable -y
+sudo apt-get update
+sudo apt-get install amulet python-requests -y
diff --git a/tests/charms/layers/metrics-proxy-ci/tests/10-deploy b/tests/charms/layers/metrics-proxy-ci/tests/10-deploy
new file mode 100644
index 0000000..7595ecf
--- /dev/null
+++ b/tests/charms/layers/metrics-proxy-ci/tests/10-deploy
@@ -0,0 +1,35 @@
+#!/usr/bin/python3
+
+import amulet
+import requests
+import unittest
+
+
+class TestCharm(unittest.TestCase):
+ def setUp(self):
+ self.d = amulet.Deployment()
+
+ self.d.add('metrics-demo')
+ self.d.expose('metrics-demo')
+
+ self.d.setup(timeout=900)
+ self.d.sentry.wait()
+
+ self.unit = self.d.sentry['metrics-demo'][0]
+
+ def test_service(self):
+ # test we can access over http
+ page = requests.get('http://{}'.format(self.unit.info['public-address']))
+ self.assertEqual(page.status_code, 200)
+ # Now you can use self.d.sentry[SERVICE][UNIT] to address each of the units and perform
+ # more in-depth steps. Each self.d.sentry[SERVICE][UNIT] has the following methods:
+ # - .info - An array of the information of that unit from Juju
+ # - .file(PATH) - Get the details of a file on that unit
+ # - .file_contents(PATH) - Get plain text output of PATH file from that unit
+ # - .directory(PATH) - Get details of directory
+ # - .directory_contents(PATH) - List files and folders in PATH on that unit
+ # - .relation(relation, service:rel) - Get relation data from return service
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/charms/layers/native-ci/README.md b/tests/charms/layers/native-ci/README.md
new file mode 100644
index 0000000..d58b762
--- /dev/null
+++ b/tests/charms/layers/native-ci/README.md
@@ -0,0 +1,3 @@
+# Overview
+
+A native charm.
diff --git a/tests/charms/layers/native-ci/actions.yaml b/tests/charms/layers/native-ci/actions.yaml
new file mode 100644
index 0000000..6adcba7
--- /dev/null
+++ b/tests/charms/layers/native-ci/actions.yaml
@@ -0,0 +1,8 @@
+test:
+ description: "Verify that the action can run."
+testint:
+ description: "Test a primitive with a non-string parameter"
+ params:
+ intval:
+ type: integer
+ default: 0
diff --git a/tests/charms/layers/native-ci/actions/test b/tests/charms/layers/native-ci/actions/test
new file mode 100755
index 0000000..7e30af4
--- /dev/null
+++ b/tests/charms/layers/native-ci/actions/test
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+##
+# Copyright 2016 Canonical Ltd.
+# All rights reserved.
+#
+# 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
+sys.path.append('lib')
+
+from charms.reactive import main, set_flag
+from charmhelpers.core.hookenv import action_fail, action_name
+
+"""
+`set_state` only works here because it's flushed to disk inside the `main()`
+loop. remove_state will need to be called inside the action method.
+"""
+set_flag('actions.{}'.format(action_name()))
+
+try:
+ main()
+except Exception as e:
+ action_fail(repr(e))
diff --git a/tests/charms/layers/native-ci/actions/testint b/tests/charms/layers/native-ci/actions/testint
new file mode 100755
index 0000000..7e30af4
--- /dev/null
+++ b/tests/charms/layers/native-ci/actions/testint
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+##
+# Copyright 2016 Canonical Ltd.
+# All rights reserved.
+#
+# 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
+sys.path.append('lib')
+
+from charms.reactive import main, set_flag
+from charmhelpers.core.hookenv import action_fail, action_name
+
+"""
+`set_state` only works here because it's flushed to disk inside the `main()`
+loop. remove_state will need to be called inside the action method.
+"""
+set_flag('actions.{}'.format(action_name()))
+
+try:
+ main()
+except Exception as e:
+ action_fail(repr(e))
diff --git a/tests/charms/layers/native-ci/layer.yaml b/tests/charms/layers/native-ci/layer.yaml
new file mode 100644
index 0000000..edc8839
--- /dev/null
+++ b/tests/charms/layers/native-ci/layer.yaml
@@ -0,0 +1,4 @@
+includes: ['layer:basic']
+options:
+ basic:
+ use_venv: false
diff --git a/tests/charms/layers/native-ci/metadata.yaml b/tests/charms/layers/native-ci/metadata.yaml
new file mode 100644
index 0000000..6acf296
--- /dev/null
+++ b/tests/charms/layers/native-ci/metadata.yaml
@@ -0,0 +1,6 @@
+name: native-ci
+summary: A native VNF charm
+description: A native VNF charm
+maintainer: Adam Israel <adam.israel@canonical.com>
+subordinate: false
+series: ['xenial']
diff --git a/tests/charms/layers/native-ci/reactive/native-ci.py b/tests/charms/layers/native-ci/reactive/native-ci.py
new file mode 100644
index 0000000..17bf5f4
--- /dev/null
+++ b/tests/charms/layers/native-ci/reactive/native-ci.py
@@ -0,0 +1,44 @@
+from charmhelpers.core.hookenv import (
+ action_fail,
+ action_set,
+ action_get,
+ status_set,
+)
+from charms.reactive import (
+ clear_flag,
+ set_flag,
+ when,
+ when_not,
+)
+
+
+@when_not('native-ci.installed')
+def install_native_ci_charm():
+ set_flag('native-ci.installed')
+ status_set('active', 'Ready!')
+
+
+@when('actions.test', 'native-ci.installed')
+def test():
+ try:
+ result = True
+ except Exception as e:
+ action_fail('command failed: {}'.format(e))
+ else:
+ action_set({'output': result})
+ finally:
+ clear_flag('actions.test')
+
+
+@when('actions.testint', 'native-ci.installed')
+def testint():
+ try:
+ # Test the value is an int by performing a mathmatical operation on it.
+ intval = action_get('intval')
+ intval = intval + 1
+ except Exception as e:
+ action_fail('command failed: {}'.format(e))
+ else:
+ action_set({'output': intval})
+ finally:
+ clear_flag('actions.testint')
diff --git a/tests/charms/layers/proxy-ci/README.md b/tests/charms/layers/proxy-ci/README.md
new file mode 100644
index 0000000..c16d9d8
--- /dev/null
+++ b/tests/charms/layers/proxy-ci/README.md
@@ -0,0 +1,3 @@
+# Overview
+
+A `charm layer` to test the functionality of proxy charms.
diff --git a/tests/charms/layers/proxy-ci/actions.yaml b/tests/charms/layers/proxy-ci/actions.yaml
new file mode 100644
index 0000000..5af8591
--- /dev/null
+++ b/tests/charms/layers/proxy-ci/actions.yaml
@@ -0,0 +1,2 @@
+test:
+ description: "Verify that the action can run."
diff --git a/tests/charms/layers/proxy-ci/actions/test b/tests/charms/layers/proxy-ci/actions/test
new file mode 100755
index 0000000..7e30af4
--- /dev/null
+++ b/tests/charms/layers/proxy-ci/actions/test
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+##
+# Copyright 2016 Canonical Ltd.
+# All rights reserved.
+#
+# 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
+sys.path.append('lib')
+
+from charms.reactive import main, set_flag
+from charmhelpers.core.hookenv import action_fail, action_name
+
+"""
+`set_state` only works here because it's flushed to disk inside the `main()`
+loop. remove_state will need to be called inside the action method.
+"""
+set_flag('actions.{}'.format(action_name()))
+
+try:
+ main()
+except Exception as e:
+ action_fail(repr(e))
diff --git a/tests/charms/layers/proxy-ci/layer.yaml b/tests/charms/layers/proxy-ci/layer.yaml
new file mode 100644
index 0000000..790dee6
--- /dev/null
+++ b/tests/charms/layers/proxy-ci/layer.yaml
@@ -0,0 +1,4 @@
+includes:
+ - 'layer:basic'
+ - 'layer:vnfproxy'
+ - 'layer:sshproxy'
diff --git a/tests/charms/layers/proxy-ci/metadata.yaml b/tests/charms/layers/proxy-ci/metadata.yaml
new file mode 100644
index 0000000..b96abe4
--- /dev/null
+++ b/tests/charms/layers/proxy-ci/metadata.yaml
@@ -0,0 +1,12 @@
+name: proxy-ci
+summary: <Fill in summary here>
+maintainer: Adam Israel <Adam.Israel@ronin>
+description: |
+ <Multi-line description here>
+tags:
+ # Replace "misc" with one or more whitelisted tags from this list:
+ # https://jujucharms.com/docs/stable/authors-charm-metadata
+ - misc
+subordinate: false
+series:
+ - xenial
diff --git a/tests/charms/layers/proxy-ci/reactive/proxy_ci.py b/tests/charms/layers/proxy-ci/reactive/proxy_ci.py
new file mode 100644
index 0000000..30e4eea
--- /dev/null
+++ b/tests/charms/layers/proxy-ci/reactive/proxy_ci.py
@@ -0,0 +1,34 @@
+from charmhelpers.core.hookenv import (
+ action_fail,
+ action_set,
+ status_set,
+)
+from charms.reactive import (
+ set_flag,
+ clear_flag,
+ when_not,
+ when,
+)
+import charms.sshproxy
+
+
+@when_not('proxy-ci.installed')
+def install_metrics_ci():
+ status_set('blocked', "Waiting for SSH credentials.")
+ set_flag('proxy-ci.installed')
+
+
+@when('actions.test', 'proxy-ci.installed')
+def test():
+ err = ''
+ try:
+ cmd = ['hostname']
+ result, err = charms.sshproxy._run(cmd)
+ if len(result) == 0:
+ raise Exception("Proxy failed")
+ except Exception as e:
+ action_fail('command failed: {}'.format(e))
+ else:
+ action_set({'output': result})
+ finally:
+ clear_flag('actions.test')