diff --git a/calico/calico-arm64.tgz b/calico/calico-arm64.tgz
deleted file mode 100644
index 02024d5..0000000
Binary files a/calico/calico-arm64.tgz and /dev/null differ
diff --git a/calico/calico-upgrade-arm64.tgz b/calico/calico-upgrade-arm64.tgz
deleted file mode 100644
index 34f3ccd..0000000
Binary files a/calico/calico-upgrade-arm64.tgz and /dev/null differ
diff --git a/calico/calico-upgrade.tgz b/calico/calico-upgrade.tgz
deleted file mode 100644
index d647ee0..0000000
Binary files a/calico/calico-upgrade.tgz and /dev/null differ
diff --git a/calico/calico.tgz b/calico/calico.tgz
deleted file mode 100644
index 031a87e..0000000
Binary files a/calico/calico.tgz and /dev/null differ
diff --git a/coredns/venv/__pycache__/oci_image.cpython-38.pyc b/coredns/venv/__pycache__/oci_image.cpython-38.pyc
new file mode 100644
index 0000000..031a742
Binary files /dev/null and b/coredns/venv/__pycache__/oci_image.cpython-38.pyc differ
diff --git a/coredns/venv/ops/__pycache__/__init__.cpython-38.pyc b/coredns/venv/ops/__pycache__/__init__.cpython-38.pyc
new file mode 100644
index 0000000..c37336c
Binary files /dev/null and b/coredns/venv/ops/__pycache__/__init__.cpython-38.pyc differ
diff --git a/coredns/venv/ops/__pycache__/charm.cpython-38.pyc b/coredns/venv/ops/__pycache__/charm.cpython-38.pyc
new file mode 100644
index 0000000..0cdaada
Binary files /dev/null and b/coredns/venv/ops/__pycache__/charm.cpython-38.pyc differ
diff --git a/coredns/venv/ops/__pycache__/framework.cpython-38.pyc b/coredns/venv/ops/__pycache__/framework.cpython-38.pyc
new file mode 100644
index 0000000..37a070e
Binary files /dev/null and b/coredns/venv/ops/__pycache__/framework.cpython-38.pyc differ
diff --git a/coredns/venv/ops/__pycache__/jujuversion.cpython-38.pyc b/coredns/venv/ops/__pycache__/jujuversion.cpython-38.pyc
new file mode 100644
index 0000000..be159fd
Binary files /dev/null and b/coredns/venv/ops/__pycache__/jujuversion.cpython-38.pyc differ
diff --git a/coredns/venv/ops/__pycache__/log.cpython-38.pyc b/coredns/venv/ops/__pycache__/log.cpython-38.pyc
new file mode 100644
index 0000000..83de3b9
Binary files /dev/null and b/coredns/venv/ops/__pycache__/log.cpython-38.pyc differ
diff --git a/coredns/venv/ops/__pycache__/main.cpython-38.pyc b/coredns/venv/ops/__pycache__/main.cpython-38.pyc
new file mode 100644
index 0000000..f4f10a5
Binary files /dev/null and b/coredns/venv/ops/__pycache__/main.cpython-38.pyc differ
diff --git a/coredns/venv/ops/__pycache__/model.cpython-38.pyc b/coredns/venv/ops/__pycache__/model.cpython-38.pyc
new file mode 100644
index 0000000..db6dc9e
Binary files /dev/null and b/coredns/venv/ops/__pycache__/model.cpython-38.pyc differ
diff --git a/coredns/venv/ops/__pycache__/storage.cpython-38.pyc b/coredns/venv/ops/__pycache__/storage.cpython-38.pyc
new file mode 100644
index 0000000..1bb4a07
Binary files /dev/null and b/coredns/venv/ops/__pycache__/storage.cpython-38.pyc differ
diff --git a/coredns/venv/ops/__pycache__/testing.cpython-38.pyc b/coredns/venv/ops/__pycache__/testing.cpython-38.pyc
new file mode 100644
index 0000000..2d9cb29
Binary files /dev/null and b/coredns/venv/ops/__pycache__/testing.cpython-38.pyc differ
diff --git a/coredns/venv/ops/__pycache__/version.cpython-38.pyc b/coredns/venv/ops/__pycache__/version.cpython-38.pyc
new file mode 100644
index 0000000..1be0b7a
Binary files /dev/null and b/coredns/venv/ops/__pycache__/version.cpython-38.pyc differ
diff --git a/coredns/venv/ops/lib/__pycache__/__init__.cpython-38.pyc b/coredns/venv/ops/lib/__pycache__/__init__.cpython-38.pyc
new file mode 100644
index 0000000..f06f4b5
Binary files /dev/null and b/coredns/venv/ops/lib/__pycache__/__init__.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/__init__.cpython-38.pyc b/coredns/venv/yaml/__pycache__/__init__.cpython-38.pyc
new file mode 100644
index 0000000..18da437
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/__init__.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/composer.cpython-38.pyc b/coredns/venv/yaml/__pycache__/composer.cpython-38.pyc
new file mode 100644
index 0000000..483755e
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/composer.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/constructor.cpython-38.pyc b/coredns/venv/yaml/__pycache__/constructor.cpython-38.pyc
new file mode 100644
index 0000000..b0e3021
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/constructor.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/cyaml.cpython-38.pyc b/coredns/venv/yaml/__pycache__/cyaml.cpython-38.pyc
new file mode 100644
index 0000000..0e4fdf0
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/cyaml.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/dumper.cpython-38.pyc b/coredns/venv/yaml/__pycache__/dumper.cpython-38.pyc
new file mode 100644
index 0000000..0f2c1e6
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/dumper.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/emitter.cpython-38.pyc b/coredns/venv/yaml/__pycache__/emitter.cpython-38.pyc
new file mode 100644
index 0000000..fbe86dd
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/emitter.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/error.cpython-38.pyc b/coredns/venv/yaml/__pycache__/error.cpython-38.pyc
new file mode 100644
index 0000000..481c6cf
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/error.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/events.cpython-38.pyc b/coredns/venv/yaml/__pycache__/events.cpython-38.pyc
new file mode 100644
index 0000000..ee8ca1b
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/events.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/loader.cpython-38.pyc b/coredns/venv/yaml/__pycache__/loader.cpython-38.pyc
new file mode 100644
index 0000000..ed7fdcf
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/loader.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/nodes.cpython-38.pyc b/coredns/venv/yaml/__pycache__/nodes.cpython-38.pyc
new file mode 100644
index 0000000..7766e80
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/nodes.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/parser.cpython-38.pyc b/coredns/venv/yaml/__pycache__/parser.cpython-38.pyc
new file mode 100644
index 0000000..7047475
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/parser.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/reader.cpython-38.pyc b/coredns/venv/yaml/__pycache__/reader.cpython-38.pyc
new file mode 100644
index 0000000..d1c3b1b
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/reader.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/representer.cpython-38.pyc b/coredns/venv/yaml/__pycache__/representer.cpython-38.pyc
new file mode 100644
index 0000000..c4544e2
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/representer.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/resolver.cpython-38.pyc b/coredns/venv/yaml/__pycache__/resolver.cpython-38.pyc
new file mode 100644
index 0000000..ec45829
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/resolver.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/scanner.cpython-38.pyc b/coredns/venv/yaml/__pycache__/scanner.cpython-38.pyc
new file mode 100644
index 0000000..70d13ce
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/scanner.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/serializer.cpython-38.pyc b/coredns/venv/yaml/__pycache__/serializer.cpython-38.pyc
new file mode 100644
index 0000000..e4ce884
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/serializer.cpython-38.pyc differ
diff --git a/coredns/venv/yaml/__pycache__/tokens.cpython-38.pyc b/coredns/venv/yaml/__pycache__/tokens.cpython-38.pyc
new file mode 100644
index 0000000..2ad8c48
Binary files /dev/null and b/coredns/venv/yaml/__pycache__/tokens.cpython-38.pyc differ
diff --git a/easyrsa/easyrsa.tar.gz b/easyrsa/easyrsa.tar.gz
deleted file mode 100644
index 65b2ebb..0000000
Binary files a/easyrsa/easyrsa.tar.gz and /dev/null differ
diff --git a/kata/.build.manifest b/kata/.build.manifest
new file mode 100644
index 0000000..5589d6b
--- /dev/null
+++ b/kata/.build.manifest
@@ -0,0 +1,531 @@
+{
+ "layers": [
+ {
+ "branch": "refs/heads/master\nrefs/heads/stable",
+ "rev": "fcdcea4e5de3e1556c24e6704607862d0ba00a56",
+ "url": "layer:options"
+ },
+ {
+ "branch": "refs/heads/master\nrefs/heads/stable",
+ "rev": "a3ff62c32c993d80417f6e093e3ef95e42f62083",
+ "url": "layer:basic"
+ },
+ {
+ "branch": "refs/heads/master\nrefs/heads/stable",
+ "rev": "527dd64fc4b9a6b0f8d80a3c2c0b865155050275",
+ "url": "layer:debug"
+ },
+ {
+ "branch": "refs/heads/master\nrefs/heads/stable",
+ "rev": "a7d7b6423db37a47611310039e6ed1929c0a2eab",
+ "url": "layer:status"
+ },
+ {
+ "branch": "refs/heads/stable",
+ "rev": "b2fa345285b14fe339084fd35865973ca05eefbf",
+ "url": "kata"
+ },
+ {
+ "branch": "refs/heads/master\nrefs/heads/stable",
+ "rev": "6f927f10b97f45c566481cf57a29d433f17373e1",
+ "url": "interface:container-runtime"
+ },
+ {
+ "branch": "refs/heads/master\nrefs/heads/stable",
+ "rev": "b59ce0c44bc52c789175750ce18b42f76c9a4578",
+ "url": "interface:untrusted-container-runtime"
+ }
+ ],
+ "signatures": {
+ ".build.manifest": [
+ "build",
+ "dynamic",
+ "unchecked"
+ ],
+ ".github/workflows/main.yml": [
+ "layer:basic",
+ "static",
+ "96a48a981ceb2a96f427a6b5226d2da6d7191981793804055d70a88ca1987473"
+ ],
+ ".gitignore": [
+ "kata",
+ "static",
+ "589384c900fb8e573ae6939a9efa0813087ea526761ba661d96aa2526a494eef"
+ ],
+ ".travis.yml": [
+ "kata",
+ "static",
+ "714ed5453bd5a053676efb64370194a7c130f426ec11acba7d1509d558dc979c"
+ ],
+ "CONTRIBUTING.md": [
+ "kata",
+ "static",
+ "c44755a6800e330bd939b7a27a4bb75adaef3a1ccdc15df62cb5533a3ea6252f"
+ ],
+ "LICENSE": [
+ "kata",
+ "static",
+ "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4"
+ ],
+ "Makefile": [
+ "layer:basic",
+ "static",
+ "b7ab3a34e5faf79b96a8632039a0ad0aa87f2a9b5f0ba604e007cafb22190301"
+ ],
+ "README.md": [
+ "kata",
+ "static",
+ "ac3b4f06b6e4a23f80a12f898645c4d4c2daedf961e72a2d851cf9c4b37d538a"
+ ],
+ "actions.yaml": [
+ "layer:debug",
+ "dynamic",
+ "cea290e28bc78458ea4a56dcad39b9a880c67e4ba53b774ac46bd8778618c7b9"
+ ],
+ "actions/debug": [
+ "layer:debug",
+ "static",
+ "db0a42dae4c5045b2c06385bf22209dfe0e2ded55822ef847d84b01d9ff2b046"
+ ],
+ "bin/charm-env": [
+ "layer:basic",
+ "static",
+ "fb6a20fac4102a6a4b6ffe903fcf666998f9a95a3647e6f9af7a1eeb44e58fd5"
+ ],
+ "bin/layer_option": [
+ "layer:options",
+ "static",
+ "e959bf29da4c5edff28b2602c24113c4df9e25cdc9f2aa3b5d46c8577b2a40cc"
+ ],
+ "copyright": [
+ "layer:status",
+ "static",
+ "7c0e36e618a8544faaaa3f8e0533c2f1f4a18bcacbdd8b99b537742e6b587d58"
+ ],
+ "copyright.layer-basic": [
+ "layer:basic",
+ "static",
+ "f6740d66fd60b60f2533d9fcb53907078d1e20920a0219afce7182e2a1c97629"
+ ],
+ "copyright.layer-options": [
+ "layer:options",
+ "static",
+ "f6740d66fd60b60f2533d9fcb53907078d1e20920a0219afce7182e2a1c97629"
+ ],
+ "debug-scripts/charm-unitdata": [
+ "layer:debug",
+ "static",
+ "c952b9d31f3942e4e722cb3e70f5119707b69b8e76cc44e2e906bc6d9aef49b7"
+ ],
+ "debug-scripts/filesystem": [
+ "layer:debug",
+ "static",
+ "d29cc8687f4422d024001c91b1ac756ee6bf8a2a125bc98db1199ba775eb8fd7"
+ ],
+ "debug-scripts/juju-logs": [
+ "layer:debug",
+ "static",
+ "d260b35753a917368cb8c64c1312546a0a40ef49cba84c75bc6369549807c55e"
+ ],
+ "debug-scripts/juju-network-get": [
+ "layer:debug",
+ "static",
+ "6d849a1f8e6569bd0d5ea38299f7937cb8b36a5f505e3532f6c756eabeb8b6c5"
+ ],
+ "debug-scripts/network": [
+ "layer:debug",
+ "static",
+ "714afae5dcb45554ff1f05285501e3b7fcc656c8de51217e263b93dab25a9d2e"
+ ],
+ "debug-scripts/packages": [
+ "layer:debug",
+ "static",
+ "e8177102dc2ca853cb9272c1257cf2cfd5253d2a074e602d07c8bc4ea8e27c75"
+ ],
+ "debug-scripts/sysctl": [
+ "layer:debug",
+ "static",
+ "990035b320e09cc2228e1f2f880e795d51118b2959339eacddff9cbb74349c6a"
+ ],
+ "debug-scripts/systemd": [
+ "layer:debug",
+ "static",
+ "23ddf533198bf5b1ce723acde31ada806aab8539292b514c721d8ec08af74106"
+ ],
+ "docs/status.md": [
+ "layer:status",
+ "static",
+ "975dec9f8c938196e102e954a80226bda293407c4e5ae857c118bf692154702a"
+ ],
+ "hooks/config-changed": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/containerd-relation-broken": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/containerd-relation-changed": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/containerd-relation-created": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/containerd-relation-departed": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/containerd-relation-joined": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/hook.template": [
+ "layer:basic",
+ "static",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/install": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/leader-elected": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/leader-settings-changed": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/post-series-upgrade": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/pre-series-upgrade": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/relations/container-runtime/.gitignore": [
+ "interface:container-runtime",
+ "static",
+ "a2ebfecdb6c1b58267fbe97e6e2ac02c2b963df7673fc1047270f0f0cff16732"
+ ],
+ "hooks/relations/container-runtime/LICENSE": [
+ "interface:container-runtime",
+ "static",
+ "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4"
+ ],
+ "hooks/relations/container-runtime/README.md": [
+ "interface:container-runtime",
+ "static",
+ "44273265818229d2c858c3af0e0eee3a7df05aaa9ab20d28c3872190d4b48611"
+ ],
+ "hooks/relations/container-runtime/__init__.py": [
+ "interface:container-runtime",
+ "static",
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
+ ],
+ "hooks/relations/container-runtime/interface.yaml": [
+ "interface:container-runtime",
+ "static",
+ "e5343dcb11a6817a6050df4ea1c463eeaa0dd4777098566d4e27b056775426c6"
+ ],
+ "hooks/relations/container-runtime/provides.py": [
+ "interface:container-runtime",
+ "static",
+ "4e818da222f507604179a828629787a1250083c847277f6b5b8e028cfbbb6d06"
+ ],
+ "hooks/relations/container-runtime/requires.py": [
+ "interface:container-runtime",
+ "static",
+ "95285168b02f1f70be15c03098833a85e60fa1658ed72a46acd42e8e85ded761"
+ ],
+ "hooks/relations/untrusted-container-runtime/.gitignore": [
+ "interface:untrusted-container-runtime",
+ "static",
+ "a2ebfecdb6c1b58267fbe97e6e2ac02c2b963df7673fc1047270f0f0cff16732"
+ ],
+ "hooks/relations/untrusted-container-runtime/LICENSE": [
+ "interface:untrusted-container-runtime",
+ "static",
+ "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4"
+ ],
+ "hooks/relations/untrusted-container-runtime/README.md": [
+ "interface:untrusted-container-runtime",
+ "static",
+ "e3dc7db9ee98b716cb9a3a281fad88ca313bc11888a0da2f4b63c4306d91b64f"
+ ],
+ "hooks/relations/untrusted-container-runtime/__init__.py": [
+ "interface:untrusted-container-runtime",
+ "static",
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
+ ],
+ "hooks/relations/untrusted-container-runtime/interface.yaml": [
+ "interface:untrusted-container-runtime",
+ "static",
+ "1fcb0305295206dc2b9926bf1870cae2c6cd8eee6eef72b6060c85e4f2109a45"
+ ],
+ "hooks/relations/untrusted-container-runtime/provides.py": [
+ "interface:untrusted-container-runtime",
+ "static",
+ "05a52be7ad18df5cac9fb5dcc27c2ab24fe12e65fa809e0ea4d395dbcb36e6f2"
+ ],
+ "hooks/relations/untrusted-container-runtime/requires.py": [
+ "interface:untrusted-container-runtime",
+ "static",
+ "958e03e254ee27bee761a6af3e032a273204b356dc51438489cde726b1a6e060"
+ ],
+ "hooks/start": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/stop": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/untrusted-relation-broken": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/untrusted-relation-changed": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/untrusted-relation-created": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/untrusted-relation-departed": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/untrusted-relation-joined": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/update-status": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "hooks/upgrade-charm": [
+ "layer:basic",
+ "dynamic",
+ "2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
+ ],
+ "icon.svg": [
+ "kata",
+ "static",
+ "d20624e9389af6506a8d8a69ac9bba4d41709601b624c0875fd7d6717b395088"
+ ],
+ "layer.yaml": [
+ "kata",
+ "dynamic",
+ "599574e1d3dda3bf1d63047ac0b152caffcf22058e2f61370a37c8bb89317e4c"
+ ],
+ "lib/charms/layer/__init__.py": [
+ "layer:basic",
+ "static",
+ "dfe0d26c6bf409767de6e2546bc648f150e1b396243619bad3aa0553ab7e0e6f"
+ ],
+ "lib/charms/layer/basic.py": [
+ "layer:basic",
+ "static",
+ "98b47134770ed6e4c0b2d4aad73cd5bc200bec84aa9c1c4e075fd70c3222a0c9"
+ ],
+ "lib/charms/layer/execd.py": [
+ "layer:basic",
+ "static",
+ "fda8bd491032db1db8ddaf4e99e7cc878c6fb5432efe1f91cadb5b34765d076d"
+ ],
+ "lib/charms/layer/options.py": [
+ "layer:options",
+ "static",
+ "8ae7a07d22542fc964f2d2bee8219d1c78a68dace70a1b38d36d4aea47b1c3b2"
+ ],
+ "lib/charms/layer/status.py": [
+ "layer:status",
+ "static",
+ "d560a5e07b2e5f2b0f25f30e1f0278b06f3f90c01e4dbad5c83d71efc79018c6"
+ ],
+ "lib/debug_script.py": [
+ "layer:debug",
+ "static",
+ "a4d56f2d3e712b1b5cadb657c7195c6268d0aac6d228991049fd769e0ddaf453"
+ ],
+ "make_docs": [
+ "layer:status",
+ "static",
+ "c990f55c8e879793a62ed8464ee3d7e0d7d2225fdecaf17af24b0df0e2daa8c1"
+ ],
+ "metadata.yaml": [
+ "kata",
+ "dynamic",
+ "883f95d6180166d507365b3374b733fde27e0eb988d9532e88bb66e002c3fd68"
+ ],
+ "pydocmd.yml": [
+ "layer:status",
+ "static",
+ "11d9293901f32f75f4256ae4ac2073b92ce1d7ef7b6c892ba9fbb98690a0b330"
+ ],
+ "reactive/__init__.py": [
+ "layer:basic",
+ "static",
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
+ ],
+ "reactive/kata.py": [
+ "kata",
+ "static",
+ "7863484c83034271ea1f7a645c9f904405047db1be0fd7857f80008f47e073bf"
+ ],
+ "reactive/status.py": [
+ "layer:status",
+ "static",
+ "30207fc206f24e91def5252f1c7f7c8e23c0aed0e93076babf5e03c05296d207"
+ ],
+ "requirements.txt": [
+ "layer:basic",
+ "static",
+ "a00f75d80849e5b4fc5ad2e7536f947c25b1a4044b341caa8ee87a92d3a4c804"
+ ],
+ "tests/conftest.py": [
+ "kata",
+ "static",
+ "fd53e0c38b4dda0c18096167889cd0d85b98b0a13225f9f8853261241e94078c"
+ ],
+ "tests/test_kata_reactive.py": [
+ "kata",
+ "static",
+ "24d714d03b6f2c2faa67ecdbd7d102f700087973eb5c98d7b9c8e5542d61541c"
+ ],
+ "tox.ini": [
+ "kata",
+ "static",
+ "b04898a3c4de3bf48ca4363751048ec83ed185bc27af7d956ae799d88d3827ab"
+ ],
+ "version": [
+ "kata",
+ "dynamic",
+ "f6c325fd13ee5c726bc2e631996963198f2cfbaa50599b4962b151630fa86cf4"
+ ],
+ "wheelhouse.txt": [
+ "kata",
+ "dynamic",
+ "425000e4406bf00f663cf41789c409e7980e4bd4a1b557b0470770502f71ed09"
+ ],
+ "wheelhouse/Jinja2-2.10.1.tar.gz": [
+ "layer:basic",
+ "dynamic",
+ "065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013"
+ ],
+ "wheelhouse/MarkupSafe-1.1.1.tar.gz": [
+ "layer:basic",
+ "dynamic",
+ "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"
+ ],
+ "wheelhouse/PyYAML-5.2.tar.gz": [
+ "layer:basic",
+ "dynamic",
+ "c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c"
+ ],
+ "wheelhouse/Tempita-0.5.2.tar.gz": [
+ "__pip__",
+ "dynamic",
+ "cacecf0baa674d356641f1d406b8bff1d756d739c46b869a54de515d08e6fc9c"
+ ],
+ "wheelhouse/certifi-2021.10.8.tar.gz": [
+ "__pip__",
+ "dynamic",
+ "78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"
+ ],
+ "wheelhouse/charmhelpers-0.20.23.tar.gz": [
+ "layer:basic",
+ "dynamic",
+ "59a9776594e91cd3e3e000043f8668b4d7b279422dbb17e320f01dc16385b80e"
+ ],
+ "wheelhouse/charms.reactive-1.4.1.tar.gz": [
+ "layer:basic",
+ "dynamic",
+ "bba21b4fd40b26c240c9ef2aa10c6fdf73592031c68591da4e7ccc46ca9cb616"
+ ],
+ "wheelhouse/charset-normalizer-2.0.7.tar.gz": [
+ "__pip__",
+ "dynamic",
+ "e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"
+ ],
+ "wheelhouse/idna-3.3.tar.gz": [
+ "__pip__",
+ "dynamic",
+ "9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
+ ],
+ "wheelhouse/netaddr-0.7.19.tar.gz": [
+ "layer:basic",
+ "dynamic",
+ "38aeec7cdd035081d3a4c306394b19d677623bf76fa0913f6695127c7753aefd"
+ ],
+ "wheelhouse/pbr-5.6.0.tar.gz": [
+ "__pip__",
+ "dynamic",
+ "42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd"
+ ],
+ "wheelhouse/pip-18.1.tar.gz": [
+ "layer:basic",
+ "dynamic",
+ "c0a292bd977ef590379a3f05d7b7f65135487b67470f6281289a94e015650ea1"
+ ],
+ "wheelhouse/pyaml-21.10.1.tar.gz": [
+ "__pip__",
+ "dynamic",
+ "c6519fee13bf06e3bb3f20cacdea8eba9140385a7c2546df5dbae4887f768383"
+ ],
+ "wheelhouse/requests-2.26.0.tar.gz": [
+ "kata",
+ "dynamic",
+ "b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
+ ],
+ "wheelhouse/setuptools-41.6.0.zip": [
+ "layer:basic",
+ "dynamic",
+ "6afa61b391dcd16cb8890ec9f66cc4015a8a31a6e1c2b4e0c464514be1a3d722"
+ ],
+ "wheelhouse/setuptools_scm-1.17.0.tar.gz": [
+ "layer:basic",
+ "dynamic",
+ "70a4cf5584e966ae92f54a764e6437af992ba42ac4bca7eb37cc5d02b98ec40a"
+ ],
+ "wheelhouse/six-1.16.0.tar.gz": [
+ "__pip__",
+ "dynamic",
+ "1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"
+ ],
+ "wheelhouse/urllib3-1.26.7.tar.gz": [
+ "__pip__",
+ "dynamic",
+ "4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"
+ ],
+ "wheelhouse/wheel-0.33.6.tar.gz": [
+ "layer:basic",
+ "dynamic",
+ "10c9da68765315ed98850f8e048347c3eb06dd81822dc2ab1d4fde9dc9702646"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/kata/.github/workflows/main.yml b/kata/.github/workflows/main.yml
new file mode 100644
index 0000000..565bfaf
--- /dev/null
+++ b/kata/.github/workflows/main.yml
@@ -0,0 +1,50 @@
+name: Test Suite
+on: [pull_request]
+
+jobs:
+ lint:
+ name: Lint
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ python: [3.5, 3.6, 3.7, 3.8, 3.9]
+ steps:
+ - name: Check out code
+ uses: actions/checkout@v2
+ - name: Setup Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python }}
+ - name: Install Dependencies
+ run: |
+ pip install tox
+ - name: Run lint
+ run: tox -e flake8
+ functional-test:
+ name: Functional test with LXD
+ runs-on: ubuntu-latest
+ timeout-minutes: 360
+ steps:
+ - name: Check out code
+ uses: actions/checkout@v2
+ - name: Setup Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.8
+ - name: Install Dependencies
+ run: |
+ pip install tox
+ - name: Setup operator environment
+ uses: charmed-kubernetes/actions-operator@master
+ - name: Run test
+ run: tox -e func
+ - name: Show Status
+ if: ${{ always() }}
+ run: |
+ model=$(juju models --format yaml|grep "^- name:.*zaza"|cut -f2 -d/);
+ juju status -m "$model"
+ - name: Show Error Logs
+ if: ${{ always() }}
+ run: |
+ model=$(juju models --format yaml|grep "^- name:.*zaza"|cut -f2 -d/);
+ juju debug-log -m "$model" --replay --no-tail --level ERROR
diff --git a/kata/.gitignore b/kata/.gitignore
new file mode 100644
index 0000000..8003a31
--- /dev/null
+++ b/kata/.gitignore
@@ -0,0 +1,4 @@
+__pycache__/
+.coverage
+.tox/
+.venv/
diff --git a/kata/.travis.yml b/kata/.travis.yml
new file mode 100644
index 0000000..694ddcb
--- /dev/null
+++ b/kata/.travis.yml
@@ -0,0 +1,11 @@
+dist: bionic
+language: python
+python:
+ - "3.5"
+ - "3.6"
+ - "3.7"
+ - "3.8"
+install:
+ - pip install tox-travis
+script:
+ - tox
diff --git a/kata/CONTRIBUTING.md b/kata/CONTRIBUTING.md
new file mode 100644
index 0000000..7a8f252
--- /dev/null
+++ b/kata/CONTRIBUTING.md
@@ -0,0 +1,41 @@
+# Contributor Guide
+
+This Juju charm is open source ([Apache License 2.0](./LICENSE)) and we actively seek any community contibutions
+for code, suggestions and documentation.
+This page details a few notes, workflows and suggestions for how to make contributions most effective and help us
+all build a better charm - please give them a read before working on any contributions.
+
+## Licensing
+
+This charm has been created under the [Apache License 2.0](./LICENSE), which will cover any contributions you may
+make to this project. Please familiarise yourself with the terms of the license.
+
+Additionally, this charm uses the Harmony CLA agreement. It’s the easiest way for you to give us permission to
+use your contributions.
+In effect, you’re giving us a license, but you still own the copyright — so you retain the right to modify your
+code and use it in other projects. Please [sign the CLA here](https://ubuntu.com/legal/contributors/agreement) before
+making any contributions.
+
+## Code of conduct
+
+We have adopted the Ubuntu code of Conduct. You can read this in full [here](https://ubuntu.com/community/code-of-conduct).
+
+## Contributing code
+
+To contribute code to this project, please use the following workflow:
+
+1. [Submit a bug][bug] to explain the need for and track the change.
+2. Create a branch on your fork of the repo with your changes, including a unit test covering the new or modified code.
+3. Submit a PR. The PR description should include a link to the bug on Launchpad.
+4. Update the Launchpad bug to include a link to the PR and the `review-needed` tag.
+5. Once reviewed and merged, the change will become available on the edge channel and assigned to an appropriate milestone
+ for further release according to priority.
+
+## Documentation
+
+Documentation for this charm is currently maintained as part of the Charmed Kubernetes docs.
+See [this page][docs]
+
+
+[bug]: https://bugs.launchpad.net/charm-kata/+filebug
+[docs]: https://github.com/charmed-kubernetes/kubernetes-docs/blob/master/pages/k8s/charm-kata.md
\ No newline at end of file
diff --git a/kata/LICENSE b/kata/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/kata/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/kata/Makefile b/kata/Makefile
new file mode 100644
index 0000000..a1ad3a5
--- /dev/null
+++ b/kata/Makefile
@@ -0,0 +1,24 @@
+#!/usr/bin/make
+
+all: lint unit_test
+
+
+.PHONY: clean
+clean:
+ @rm -rf .tox
+
+.PHONY: apt_prereqs
+apt_prereqs:
+ @# Need tox, but don't install the apt version unless we have to (don't want to conflict with pip)
+ @which tox >/dev/null || (sudo apt-get install -y python-pip && sudo pip install tox)
+
+.PHONY: lint
+lint: apt_prereqs
+ @tox --notest
+ @PATH=.tox/py34/bin:.tox/py35/bin flake8 $(wildcard hooks reactive lib unit_tests tests)
+ @charm proof
+
+.PHONY: unit_test
+unit_test: apt_prereqs
+ @echo Starting tests...
+ tox
diff --git a/kata/README.md b/kata/README.md
new file mode 100644
index 0000000..39cb53b
--- /dev/null
+++ b/kata/README.md
@@ -0,0 +1,7 @@
+# Charm for Kata Containers
+
+This subordinate charm deploys the [Kata](https://katacontainers.io/)
+untrusted container runtime within a running Juju charm model.
+
+This charm is maintained along with the components of Charmed Kubernetes.
+For full information, please visit the official [Charmed Kubernetes docs](https://ubuntu.com/kubernetes/docs/charm-kata).
diff --git a/kata/actions.yaml b/kata/actions.yaml
new file mode 100644
index 0000000..8712b6b
--- /dev/null
+++ b/kata/actions.yaml
@@ -0,0 +1,2 @@
+"debug":
+ "description": "Collect debug data"
diff --git a/kata/actions/debug b/kata/actions/debug
new file mode 100755
index 0000000..8ba160e
--- /dev/null
+++ b/kata/actions/debug
@@ -0,0 +1,102 @@
+#!/usr/local/sbin/charm-env python3
+
+import os
+import subprocess
+import tarfile
+import tempfile
+import traceback
+from contextlib import contextmanager
+from datetime import datetime
+from charmhelpers.core.hookenv import action_set, local_unit
+
+archive_dir = None
+log_file = None
+
+
+@contextmanager
+def archive_context():
+ """ Open a context with a new temporary directory.
+
+ When the context closes, the directory is archived, and the archive
+ location is added to Juju action output. """
+ global archive_dir
+ global log_file
+ with tempfile.TemporaryDirectory() as temp_dir:
+ name = "debug-" + datetime.now().strftime("%Y%m%d%H%M%S")
+ archive_dir = os.path.join(temp_dir, name)
+ os.makedirs(archive_dir)
+ with open("%s/debug.log" % archive_dir, "w") as log_file:
+ yield
+ os.chdir(temp_dir)
+ tar_path = "/home/ubuntu/%s.tar.gz" % name
+ with tarfile.open(tar_path, "w:gz") as f:
+ f.add(name)
+ action_set({
+ "path": tar_path,
+ "command": "juju scp %s:%s ." % (local_unit(), tar_path),
+ "message": " ".join([
+ "Archive has been created on unit %s." % local_unit(),
+ "Use the juju scp command to copy it to your local machine."
+ ])
+ })
+
+
+def log(msg):
+ """ Log a message that will be included in the debug archive.
+
+ Must be run within archive_context """
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ for line in str(msg).splitlines():
+ log_file.write(timestamp + " | " + line.rstrip() + "\n")
+
+
+def run_script(script):
+ """ Run a single script. Must be run within archive_context """
+ log("Running script: " + script)
+ script_dir = os.path.join(archive_dir, script)
+ os.makedirs(script_dir)
+ env = os.environ.copy()
+ env["PYTHONPATH"] = "lib" # allow same imports as reactive code
+ env["DEBUG_SCRIPT_DIR"] = script_dir
+ with open(script_dir + "/stdout", "w") as stdout:
+ with open(script_dir + "/stderr", "w") as stderr:
+ process = subprocess.Popen(
+ "debug-scripts/" + script,
+ stdout=stdout, stderr=stderr, env=env
+ )
+ try:
+ exit_code = process.wait(timeout=300)
+ except subprocess.TimeoutExpired:
+ log("ERROR: still running, terminating")
+ process.terminate()
+ try:
+ exit_code = process.wait(timeout=10)
+ except subprocess.TimeoutExpired:
+ log("ERROR: still running, killing")
+ process.kill()
+ exit_code = process.wait(timeout=10)
+ if exit_code != 0:
+ log("ERROR: %s failed with exit code %d" % (script, exit_code))
+
+
+def run_all_scripts():
+ """ Run all scripts. For the sake of robustness, log and ignore any
+ exceptions that occur.
+
+ Must be run within archive_context """
+ scripts = os.listdir("debug-scripts")
+ for script in scripts:
+ try:
+ run_script(script)
+ except:
+ log(traceback.format_exc())
+
+
+def main():
+ """ Open an archive context and run all scripts. """
+ with archive_context():
+ run_all_scripts()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/kata/bin/charm-env b/kata/bin/charm-env
new file mode 100755
index 0000000..d211ce9
--- /dev/null
+++ b/kata/bin/charm-env
@@ -0,0 +1,107 @@
+#!/bin/bash
+
+VERSION="1.0.0"
+
+
+find_charm_dirs() {
+ # Hopefully, $JUJU_CHARM_DIR is set so which venv to use in unambiguous.
+ if [[ -n "$JUJU_CHARM_DIR" || -n "$CHARM_DIR" ]]; then
+ if [[ -z "$JUJU_CHARM_DIR" ]]; then
+ # accept $CHARM_DIR to be more forgiving
+ export JUJU_CHARM_DIR="$CHARM_DIR"
+ fi
+ if [[ -z "$CHARM_DIR" ]]; then
+ # set CHARM_DIR as well to help with backwards compatibility
+ export CHARM_DIR="$JUJU_CHARM_DIR"
+ fi
+ return
+ fi
+ # Try to guess the value for JUJU_CHARM_DIR by looking for a non-subordinate
+ # (because there's got to be at least one principle) charm directory;
+ # if there are several, pick the first by alpha order.
+ agents_dir="/var/lib/juju/agents"
+ if [[ -d "$agents_dir" ]]; then
+ desired_charm="$1"
+ found_charm_dir=""
+ if [[ -n "$desired_charm" ]]; then
+ for charm_dir in $(/bin/ls -d "$agents_dir"/unit-*/charm); do
+ charm_name="$(grep -o '^['\''"]\?name['\''"]\?:.*' $charm_dir/metadata.yaml 2> /dev/null | sed -e 's/.*: *//' -e 's/['\''"]//g')"
+ if [[ "$charm_name" == "$desired_charm" ]]; then
+ if [[ -n "$found_charm_dir" ]]; then
+ >&2 echo "Ambiguous possibilities for JUJU_CHARM_DIR matching '$desired_charm'; please run within a Juju hook context"
+ exit 1
+ fi
+ found_charm_dir="$charm_dir"
+ fi
+ done
+ if [[ -z "$found_charm_dir" ]]; then
+ >&2 echo "Unable to determine JUJU_CHARM_DIR matching '$desired_charm'; please run within a Juju hook context"
+ exit 1
+ fi
+ export JUJU_CHARM_DIR="$found_charm_dir"
+ export CHARM_DIR="$found_charm_dir"
+ return
+ fi
+ # shellcheck disable=SC2126
+ non_subordinates="$(grep -L 'subordinate"\?:.*true' "$agents_dir"/unit-*/charm/metadata.yaml | wc -l)"
+ if [[ "$non_subordinates" -gt 1 ]]; then
+ >&2 echo 'Ambiguous possibilities for JUJU_CHARM_DIR; please use --charm or run within a Juju hook context'
+ exit 1
+ elif [[ "$non_subordinates" -eq 1 ]]; then
+ for charm_dir in $(/bin/ls -d "$agents_dir"/unit-*/charm); do
+ if grep -q 'subordinate"\?:.*true' "$charm_dir/metadata.yaml"; then
+ continue
+ fi
+ export JUJU_CHARM_DIR="$charm_dir"
+ export CHARM_DIR="$charm_dir"
+ return
+ done
+ fi
+ fi
+ >&2 echo 'Unable to determine JUJU_CHARM_DIR; please run within a Juju hook context'
+ exit 1
+}
+
+try_activate_venv() {
+ if [[ -d "$JUJU_CHARM_DIR/../.venv" ]]; then
+ . "$JUJU_CHARM_DIR/../.venv/bin/activate"
+ fi
+}
+
+find_wrapped() {
+ PATH="${PATH/\/usr\/local\/sbin:}" which "$(basename "$0")"
+}
+
+
+if [[ "$1" == "--version" || "$1" == "-v" ]]; then
+ echo "$VERSION"
+ exit 0
+fi
+
+
+# allow --charm option to hint which JUJU_CHARM_DIR to choose when ambiguous
+# NB: --charm option must come first
+# NB: option must be processed outside find_charm_dirs to modify $@
+charm_name=""
+if [[ "$1" == "--charm" ]]; then
+ charm_name="$2"
+ shift; shift
+fi
+
+find_charm_dirs "$charm_name"
+try_activate_venv
+export PYTHONPATH="$JUJU_CHARM_DIR/lib:$PYTHONPATH"
+
+if [[ "$(basename "$0")" == "charm-env" ]]; then
+ # being used as a shebang
+ exec "$@"
+elif [[ "$0" == "$BASH_SOURCE" ]]; then
+ # being invoked as a symlink wrapping something to find in the venv
+ exec "$(find_wrapped)" "$@"
+elif [[ "$(basename "$BASH_SOURCE")" == "charm-env" ]]; then
+ # being sourced directly; do nothing
+ /bin/true
+else
+ # being sourced for wrapped bash helpers
+ . "$(find_wrapped)"
+fi
diff --git a/kata/bin/layer_option b/kata/bin/layer_option
new file mode 100755
index 0000000..3253ef8
--- /dev/null
+++ b/kata/bin/layer_option
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+import sys
+import argparse
+from charms import layer
+
+
+parser = argparse.ArgumentParser(description='Access layer options.')
+parser.add_argument('section',
+ help='the section, or layer, the option is from')
+parser.add_argument('option',
+ help='the option to access')
+
+args = parser.parse_args()
+value = layer.options.get(args.section, args.option)
+if isinstance(value, bool):
+ sys.exit(0 if value else 1)
+elif isinstance(value, list):
+ for val in value:
+ print(val)
+else:
+ print(value)
diff --git a/kata/copyright b/kata/copyright
new file mode 100644
index 0000000..a91bdf1
--- /dev/null
+++ b/kata/copyright
@@ -0,0 +1,16 @@
+Format: http://dep.debian.net/deps/dep5/
+
+Files: *
+Copyright: Copyright 2018, Canonical Ltd., All Rights Reserved.
+License: Apache License 2.0
+ 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.
diff --git a/kata/copyright.layer-basic b/kata/copyright.layer-basic
new file mode 100644
index 0000000..d4fdd18
--- /dev/null
+++ b/kata/copyright.layer-basic
@@ -0,0 +1,16 @@
+Format: http://dep.debian.net/deps/dep5/
+
+Files: *
+Copyright: Copyright 2015-2017, Canonical Ltd., All Rights Reserved.
+License: Apache License 2.0
+ 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.
diff --git a/kata/copyright.layer-options b/kata/copyright.layer-options
new file mode 100644
index 0000000..d4fdd18
--- /dev/null
+++ b/kata/copyright.layer-options
@@ -0,0 +1,16 @@
+Format: http://dep.debian.net/deps/dep5/
+
+Files: *
+Copyright: Copyright 2015-2017, Canonical Ltd., All Rights Reserved.
+License: Apache License 2.0
+ 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.
diff --git a/kata/debug-scripts/charm-unitdata b/kata/debug-scripts/charm-unitdata
new file mode 100755
index 0000000..d2aac60
--- /dev/null
+++ b/kata/debug-scripts/charm-unitdata
@@ -0,0 +1,12 @@
+#!/usr/local/sbin/charm-env python3
+
+import debug_script
+import json
+from charmhelpers.core import unitdata
+
+kv = unitdata.kv()
+data = kv.getrange("")
+
+with debug_script.open_file("unitdata.json", "w") as f:
+ json.dump(data, f, indent=2)
+ f.write("\n")
diff --git a/kata/debug-scripts/filesystem b/kata/debug-scripts/filesystem
new file mode 100755
index 0000000..c5ec6d8
--- /dev/null
+++ b/kata/debug-scripts/filesystem
@@ -0,0 +1,17 @@
+#!/bin/sh
+set -ux
+
+# report file system disk space usage
+df -hT > $DEBUG_SCRIPT_DIR/df-hT
+# estimate file space usage
+du -h / 2>&1 > $DEBUG_SCRIPT_DIR/du-h
+# list the mounted filesystems
+mount > $DEBUG_SCRIPT_DIR/mount
+# list the mounted systems with ascii trees
+findmnt -A > $DEBUG_SCRIPT_DIR/findmnt
+# list block devices
+lsblk > $DEBUG_SCRIPT_DIR/lsblk
+# list open files
+lsof 2>&1 > $DEBUG_SCRIPT_DIR/lsof
+# list local system locks
+lslocks > $DEBUG_SCRIPT_DIR/lslocks
diff --git a/kata/debug-scripts/juju-logs b/kata/debug-scripts/juju-logs
new file mode 100755
index 0000000..d27c458
--- /dev/null
+++ b/kata/debug-scripts/juju-logs
@@ -0,0 +1,4 @@
+#!/bin/sh
+set -ux
+
+cp -v /var/log/juju/* $DEBUG_SCRIPT_DIR
diff --git a/kata/debug-scripts/juju-network-get b/kata/debug-scripts/juju-network-get
new file mode 100755
index 0000000..983c8c4
--- /dev/null
+++ b/kata/debug-scripts/juju-network-get
@@ -0,0 +1,21 @@
+#!/usr/local/sbin/charm-env python3
+
+import os
+import subprocess
+import yaml
+import debug_script
+
+with open('metadata.yaml') as f:
+ metadata = yaml.load(f)
+
+relations = []
+for key in ['requires', 'provides', 'peers']:
+ relations += list(metadata.get(key, {}).keys())
+
+os.mkdir(os.path.join(debug_script.dir, 'relations'))
+
+for relation in relations:
+ path = 'relations/' + relation
+ with debug_script.open_file(path, 'w') as f:
+ cmd = ['network-get', relation]
+ subprocess.call(cmd, stdout=f, stderr=subprocess.STDOUT)
diff --git a/kata/debug-scripts/network b/kata/debug-scripts/network
new file mode 100755
index 0000000..944a355
--- /dev/null
+++ b/kata/debug-scripts/network
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -ux
+
+ifconfig -a > $DEBUG_SCRIPT_DIR/ifconfig
+cp -v /etc/resolv.conf $DEBUG_SCRIPT_DIR/resolv.conf
+cp -v /etc/network/interfaces $DEBUG_SCRIPT_DIR/interfaces
+netstat -planut > $DEBUG_SCRIPT_DIR/netstat
+route -n > $DEBUG_SCRIPT_DIR/route
+iptables-save > $DEBUG_SCRIPT_DIR/iptables-save
+dig google.com > $DEBUG_SCRIPT_DIR/dig-google
+ping -w 2 -i 0.1 google.com > $DEBUG_SCRIPT_DIR/ping-google
diff --git a/kata/debug-scripts/packages b/kata/debug-scripts/packages
new file mode 100755
index 0000000..b60a9cf
--- /dev/null
+++ b/kata/debug-scripts/packages
@@ -0,0 +1,7 @@
+#!/bin/sh
+set -ux
+
+dpkg --list > $DEBUG_SCRIPT_DIR/dpkg-list
+snap list > $DEBUG_SCRIPT_DIR/snap-list
+pip2 list > $DEBUG_SCRIPT_DIR/pip2-list
+pip3 list > $DEBUG_SCRIPT_DIR/pip3-list
diff --git a/kata/debug-scripts/sysctl b/kata/debug-scripts/sysctl
new file mode 100755
index 0000000..a86a6c8
--- /dev/null
+++ b/kata/debug-scripts/sysctl
@@ -0,0 +1,4 @@
+#!/bin/sh
+set -ux
+
+sysctl -a > $DEBUG_SCRIPT_DIR/sysctl
diff --git a/kata/debug-scripts/systemd b/kata/debug-scripts/systemd
new file mode 100755
index 0000000..8bb9b6f
--- /dev/null
+++ b/kata/debug-scripts/systemd
@@ -0,0 +1,9 @@
+#!/bin/sh
+set -ux
+
+systemctl --all > $DEBUG_SCRIPT_DIR/systemctl
+journalctl > $DEBUG_SCRIPT_DIR/journalctl
+systemd-analyze time > $DEBUG_SCRIPT_DIR/systemd-analyze-time
+systemd-analyze blame > $DEBUG_SCRIPT_DIR/systemd-analyze-blame
+systemd-analyze critical-chain > $DEBUG_SCRIPT_DIR/systemd-analyze-critical-chain
+systemd-analyze dump > $DEBUG_SCRIPT_DIR/systemd-analyze-dump
diff --git a/kata/docs/status.md b/kata/docs/status.md
new file mode 100644
index 0000000..c6cceab
--- /dev/null
+++ b/kata/docs/status.md
@@ -0,0 +1,91 @@
+
+
+```python
+maintenance(message)
+```
+
+Set the status to the `MAINTENANCE` state with the given operator message.
+
+__Parameters__
+
+- __`message` (str)__: Message to convey to the operator.
+
+
maint
+
+```python
+maint(message)
+```
+
+Shorthand alias for
+[maintenance](status.md#charms.layer.status.maintenance).
+
+__Parameters__
+
+- __`message` (str)__: Message to convey to the operator.
+
+
blocked
+
+```python
+blocked(message)
+```
+
+Set the status to the `BLOCKED` state with the given operator message.
+
+__Parameters__
+
+- __`message` (str)__: Message to convey to the operator.
+
+
waiting
+
+```python
+waiting(message)
+```
+
+Set the status to the `WAITING` state with the given operator message.
+
+__Parameters__
+
+- __`message` (str)__: Message to convey to the operator.
+
+
active
+
+```python
+active(message)
+```
+
+Set the status to the `ACTIVE` state with the given operator message.
+
+__Parameters__
+
+- __`message` (str)__: Message to convey to the operator.
+
+
status_set
+
+```python
+status_set(workload_state, message)
+```
+
+Set the status to the given workload state with a message.
+
+__Parameters__
+
+- __`workload_state` (WorkloadState or str)__: State of the workload. Should be
+ a [WorkloadState](status.md#charms.layer.status.WorkloadState) enum
+ member, or the string value of one of those members.
+- __`message` (str)__: Message to convey to the operator.
+
diff --git a/kata/hooks/config-changed b/kata/hooks/config-changed
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/config-changed
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/containerd-relation-broken b/kata/hooks/containerd-relation-broken
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/containerd-relation-broken
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/containerd-relation-changed b/kata/hooks/containerd-relation-changed
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/containerd-relation-changed
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/containerd-relation-created b/kata/hooks/containerd-relation-created
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/containerd-relation-created
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/containerd-relation-departed b/kata/hooks/containerd-relation-departed
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/containerd-relation-departed
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/containerd-relation-joined b/kata/hooks/containerd-relation-joined
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/containerd-relation-joined
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/hook.template b/kata/hooks/hook.template
new file mode 100644
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/hook.template
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/install b/kata/hooks/install
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/install
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/leader-elected b/kata/hooks/leader-elected
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/leader-elected
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/leader-settings-changed b/kata/hooks/leader-settings-changed
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/leader-settings-changed
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/post-series-upgrade b/kata/hooks/post-series-upgrade
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/post-series-upgrade
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/pre-series-upgrade b/kata/hooks/pre-series-upgrade
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/pre-series-upgrade
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/relations/container-runtime/.gitignore b/kata/hooks/relations/container-runtime/.gitignore
new file mode 100644
index 0000000..894a44c
--- /dev/null
+++ b/kata/hooks/relations/container-runtime/.gitignore
@@ -0,0 +1,104 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
diff --git a/kata/hooks/relations/container-runtime/LICENSE b/kata/hooks/relations/container-runtime/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/kata/hooks/relations/container-runtime/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/kata/hooks/relations/container-runtime/README.md b/kata/hooks/relations/container-runtime/README.md
new file mode 100644
index 0000000..4620013
--- /dev/null
+++ b/kata/hooks/relations/container-runtime/README.md
@@ -0,0 +1,45 @@
+# interface-container-runtime
+
+## Overview
+
+This interface handles communication between subordinate charms, that provide a container runtime and charms requiring a container runtime.
+
+## Usage
+
+### Provides
+
+The providing side of the container interface provides a place for a container runtime to connect to.
+
+Your charm should respond to the `endpoint.{endpoint_name}.available` state,
+which indicates that there is a container runtime connected.
+
+A trivial example of handling this interface would be:
+
+```python
+@when('endpoint.containerd.joined')
+def update_kubelet_config(containerd):
+ endpoint = endpoint_from_flag('endpoint.containerd.joined')
+ config = endpoint.get_config()
+ kubelet.config['container-runtime'] = \
+ config['runtime']
+```
+
+### Requires
+
+The requiring side of the container interface requires a place for a container runtime to connect to.
+
+Your charm should set `{endpoint_name}.available` state,
+which indicates that the container is runtime connected.
+
+A trivial example of handling this interface would be:
+
+```python
+@when('endpoint.containerd.joined')
+def pubish_config():
+ endpoint = endpoint_from_flag('endpoint.containerd.joined')
+ endpoint.set_config(
+ socket='unix:///var/run/containerd/containerd.sock',
+ runtime='remote',
+ nvidia_enabled=False
+ )
+```
diff --git a/calico/calico-node-image.tgz b/kata/hooks/relations/container-runtime/__init__.py
similarity index 100%
rename from calico/calico-node-image.tgz
rename to kata/hooks/relations/container-runtime/__init__.py
diff --git a/kata/hooks/relations/container-runtime/interface.yaml b/kata/hooks/relations/container-runtime/interface.yaml
new file mode 100644
index 0000000..294be1e
--- /dev/null
+++ b/kata/hooks/relations/container-runtime/interface.yaml
@@ -0,0 +1,4 @@
+name: container-runtime
+summary: Interface for relating to container runtimes
+version: 1
+maintainer: "Joe Borg "
diff --git a/kata/hooks/relations/container-runtime/provides.py b/kata/hooks/relations/container-runtime/provides.py
new file mode 100644
index 0000000..a9768a8
--- /dev/null
+++ b/kata/hooks/relations/container-runtime/provides.py
@@ -0,0 +1,55 @@
+from charms.reactive import (
+ Endpoint,
+ toggle_flag
+)
+
+
+class ContainerRuntimeProvides(Endpoint):
+ def manage_flags(self):
+ toggle_flag(self.expand_name('endpoint.{endpoint_name}.available'),
+ self.is_joined)
+
+ def _get_config(self, key):
+ """
+ Get the published configuration for a given key.
+
+ :param key: String dict key
+ :return: String value for given key
+ """
+ return self.all_joined_units.received.get(key)
+
+ def get_nvidia_enabled(self):
+ """
+ Get the published nvidia config.
+
+ :return: String
+ """
+ return self._get_config(key='nvidia_enabled')
+
+ def get_runtime(self):
+ """
+ Get the published runtime config.
+
+ :return: String
+ """
+ return self._get_config(key='runtime')
+
+ def get_socket(self):
+ """
+ Get the published socket config.
+
+ :return: String
+ """
+ return self._get_config(key='socket')
+
+ def set_config(self, sandbox_image=None):
+ """
+ Set the configuration to be published.
+
+ :param sandbox_image: String to optionally override the sandbox image
+ :return: None
+ """
+ for relation in self.relations:
+ relation.to_publish.update({
+ 'sandbox_image': sandbox_image
+ })
diff --git a/kata/hooks/relations/container-runtime/requires.py b/kata/hooks/relations/container-runtime/requires.py
new file mode 100644
index 0000000..c461b68
--- /dev/null
+++ b/kata/hooks/relations/container-runtime/requires.py
@@ -0,0 +1,61 @@
+from charms.reactive import (
+ Endpoint,
+ clear_flag,
+ data_changed,
+ is_data_changed,
+ toggle_flag
+)
+
+
+class ContainerRuntimeRequires(Endpoint):
+ def manage_flags(self):
+ toggle_flag(self.expand_name('endpoint.{endpoint_name}.available'),
+ self.is_joined)
+ toggle_flag(self.expand_name('endpoint.{endpoint_name}.reconfigure'),
+ self.is_joined and self._config_changed())
+
+ def _config_changed(self):
+ """
+ Determine if our received data has changed.
+
+ :return: Boolean
+ """
+ # NB: this call should match whatever we're tracking in handle_remote_config
+ return is_data_changed('containerd.remote_config',
+ [self.get_sandbox_image()])
+
+ def handle_remote_config(self):
+ """
+ Keep track of received data so we can know if it changes.
+
+ :return: None
+ """
+ clear_flag(self.expand_name('endpoint.{endpoint_name}.reconfigure'))
+ # Presently, we only care about one piece of remote config. Expand
+ # the list as needed.
+ data_changed('containerd.remote_config',
+ [self.get_sandbox_image()])
+
+ def get_sandbox_image(self):
+ """
+ Get the sandbox image URI if a remote has published one.
+
+ :return: String: remotely configured sandbox image
+ """
+ return self.all_joined_units.received.get('sandbox_image')
+
+ def set_config(self, socket, runtime, nvidia_enabled):
+ """
+ Set the configuration to be published.
+
+ :param socket: String uri to runtime socket
+ :param runtime: String runtime executable
+ :param nvidia_enabled: Boolean nvidia runtime enabled
+ :return: None
+ """
+ for relation in self.relations:
+ relation.to_publish.update({
+ 'socket': socket,
+ 'runtime': runtime,
+ 'nvidia_enabled': nvidia_enabled
+ })
diff --git a/kata/hooks/relations/untrusted-container-runtime/.gitignore b/kata/hooks/relations/untrusted-container-runtime/.gitignore
new file mode 100644
index 0000000..894a44c
--- /dev/null
+++ b/kata/hooks/relations/untrusted-container-runtime/.gitignore
@@ -0,0 +1,104 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
diff --git a/kata/hooks/relations/untrusted-container-runtime/LICENSE b/kata/hooks/relations/untrusted-container-runtime/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/kata/hooks/relations/untrusted-container-runtime/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/kata/hooks/relations/untrusted-container-runtime/README.md b/kata/hooks/relations/untrusted-container-runtime/README.md
new file mode 100644
index 0000000..135dca5
--- /dev/null
+++ b/kata/hooks/relations/untrusted-container-runtime/README.md
@@ -0,0 +1,54 @@
+# interface-untrusted-container-runtime
+
+## Overview
+
+This interface handles communication between subordinate container runtimes
+and this subordinate untrusted container runtime, such as `containerd` and
+`kata-containers`.
+
+## Usage
+
+### Provides
+
+The providing side of the container interface provides a place for an
+untrusted container runtime to connect to.
+
+Your charm should respond to the `endpoint.{endpoint_name}.available` state,
+which indicates that there is an untrusted container runtime connected.
+
+A trivial example of handling this interface would be:
+
+```python
+@when('endpoint.containerd.joined')
+def update_kubelet_config(containerd):
+ endpoint = endpoint_from_flag('endpoint.containerd.joined')
+ config = endpoint.get_config()
+
+ render(
+ 'config.toml',
+ {
+ 'runtime_name': config['name'],
+ 'runtime_binary': config['binary_path']
+ }
+ )
+```
+
+### Requires
+
+The requiring side of the untrusted container interface requires a place for
+an untrusted container runtime to connect to.
+
+Your charm should set `{endpoint_name}.available` state,
+which indicates that the container is runtime connected.
+
+A trivial example of handling this interface would be:
+
+```python
+@when('endpoint.containerd.joined')
+def pubish_config():
+ endpoint = endpoint_from_flag('endpoint.containerd.joined')
+ endpoint.set_config(
+ 'name': 'kata',
+ 'binary_path': '/usr/bin/kata-runtime'
+ )
+```
diff --git a/kata/hooks/relations/untrusted-container-runtime/__init__.py b/kata/hooks/relations/untrusted-container-runtime/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/kata/hooks/relations/untrusted-container-runtime/interface.yaml b/kata/hooks/relations/untrusted-container-runtime/interface.yaml
new file mode 100644
index 0000000..d0d7dbc
--- /dev/null
+++ b/kata/hooks/relations/untrusted-container-runtime/interface.yaml
@@ -0,0 +1,4 @@
+name: untrusted-container-runtime
+summary: Interface for relating to untrusted container runtimes
+version: 1
+maintainer: "Joe Borg "
diff --git a/kata/hooks/relations/untrusted-container-runtime/provides.py b/kata/hooks/relations/untrusted-container-runtime/provides.py
new file mode 100644
index 0000000..09deb26
--- /dev/null
+++ b/kata/hooks/relations/untrusted-container-runtime/provides.py
@@ -0,0 +1,28 @@
+from charms.reactive import (
+ Endpoint,
+ set_flag,
+ clear_flag
+)
+
+from charms.reactive import (
+ when,
+ when_not
+)
+
+
+class ContainerRuntimeProvides(Endpoint):
+ @when('endpoint.{endpoint_name}.joined')
+ def joined(self):
+ set_flag(self.expand_name('endpoint.{endpoint_name}.available'))
+
+ @when_not('endpoint.{endpoint_name}.joined')
+ def broken(self):
+ clear_flag(self.expand_name('endpoint.{endpoint_name}.available'))
+
+ def get_config(self):
+ """
+ Get the configuration published.
+
+ :return: Dictionary configuration
+ """
+ return self.all_joined_units.received
diff --git a/kata/hooks/relations/untrusted-container-runtime/requires.py b/kata/hooks/relations/untrusted-container-runtime/requires.py
new file mode 100644
index 0000000..f717ba6
--- /dev/null
+++ b/kata/hooks/relations/untrusted-container-runtime/requires.py
@@ -0,0 +1,34 @@
+from charms.reactive import (
+ Endpoint,
+ set_flag,
+ clear_flag
+)
+
+from charms.reactive import (
+ when,
+ when_not
+)
+
+
+class ContainerRuntimeRequires(Endpoint):
+ @when('endpoint.{endpoint_name}.changed')
+ def changed(self):
+ set_flag(self.expand_name('endpoint.{endpoint_name}.available'))
+
+ @when_not('endpoint.{endpoint_name}.joined')
+ def broken(self):
+ clear_flag(self.expand_name('endpoint.{endpoint_name}.available'))
+
+ def set_config(self, name, binary_path):
+ """
+ Set the configuration to be published.
+
+ :param name: String name of runtime
+ :param binary_path: String runtime executable
+ :return: None
+ """
+ for relation in self.relations:
+ relation.to_publish.update({
+ 'name': name,
+ 'binary_path': binary_path
+ })
diff --git a/kata/hooks/start b/kata/hooks/start
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/start
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/stop b/kata/hooks/stop
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/stop
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/untrusted-relation-broken b/kata/hooks/untrusted-relation-broken
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/untrusted-relation-broken
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/untrusted-relation-changed b/kata/hooks/untrusted-relation-changed
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/untrusted-relation-changed
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/untrusted-relation-created b/kata/hooks/untrusted-relation-created
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/untrusted-relation-created
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/untrusted-relation-departed b/kata/hooks/untrusted-relation-departed
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/untrusted-relation-departed
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/untrusted-relation-joined b/kata/hooks/untrusted-relation-joined
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/untrusted-relation-joined
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/update-status b/kata/hooks/update-status
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/update-status
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/hooks/upgrade-charm b/kata/hooks/upgrade-charm
new file mode 100755
index 0000000..9858c6b
--- /dev/null
+++ b/kata/hooks/upgrade-charm
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+# Load modules from $JUJU_CHARM_DIR/lib
+import sys
+sys.path.append('lib')
+
+from charms.layer import basic # noqa
+basic.bootstrap_charm_deps()
+
+from charmhelpers.core import hookenv # noqa
+hookenv.atstart(basic.init_config_states)
+hookenv.atexit(basic.clear_config_states)
+
+
+# This will load and run the appropriate @hook and other decorated
+# handlers from $JUJU_CHARM_DIR/reactive, $JUJU_CHARM_DIR/hooks/reactive,
+# and $JUJU_CHARM_DIR/hooks/relations.
+#
+# See https://jujucharms.com/docs/stable/authors-charm-building
+# for more information on this pattern.
+from charms.reactive import main # noqa
+main()
diff --git a/kata/icon.svg b/kata/icon.svg
new file mode 100644
index 0000000..96a5d0c
--- /dev/null
+++ b/kata/icon.svg
@@ -0,0 +1,279 @@
+
+
+
+
diff --git a/kata/layer.yaml b/kata/layer.yaml
new file mode 100644
index 0000000..7dfb411
--- /dev/null
+++ b/kata/layer.yaml
@@ -0,0 +1,20 @@
+"includes":
+- "layer:options"
+- "layer:basic"
+- "layer:debug"
+- "interface:container-runtime"
+- "interface:untrusted-container-runtime"
+- "layer:status"
+"exclude": [".travis.yml", "tests", "tox.ini", "test-requirements.txt", "unit_tests"]
+"options":
+ "basic":
+ "packages": []
+ "python_packages": []
+ "use_venv": !!bool "true"
+ "include_system_packages": !!bool "false"
+ "debug": {}
+ "status":
+ "patch-hookenv": !!bool "true"
+ "kata": {}
+"repo": "https://github.com/charmed-kubernetes/charm-kata"
+"is": "kata"
diff --git a/kata/lib/charms/layer/__init__.py b/kata/lib/charms/layer/__init__.py
new file mode 100644
index 0000000..a8e0c64
--- /dev/null
+++ b/kata/lib/charms/layer/__init__.py
@@ -0,0 +1,60 @@
+import sys
+from importlib import import_module
+from pathlib import Path
+
+
+def import_layer_libs():
+ """
+ Ensure that all layer libraries are imported.
+
+ This makes it possible to do the following:
+
+ from charms import layer
+
+ layer.foo.do_foo_thing()
+
+ Note: This function must be called after bootstrap.
+ """
+ for module_file in Path('lib/charms/layer').glob('*'):
+ module_name = module_file.stem
+ if module_name in ('__init__', 'basic', 'execd') or not (
+ module_file.suffix == '.py' or module_file.is_dir()
+ ):
+ continue
+ import_module('charms.layer.{}'.format(module_name))
+
+
+# Terrible hack to support the old terrible interface.
+# Try to get people to call layer.options.get() instead so
+# that we can remove this garbage.
+# Cribbed from https://stackoverfLow.com/a/48100440/4941864
+class OptionsBackwardsCompatibilityHack(sys.modules[__name__].__class__):
+ def __call__(self, section=None, layer_file=None):
+ if layer_file is None:
+ return self.get(section=section)
+ else:
+ return self.get(section=section,
+ layer_file=Path(layer_file))
+
+
+def patch_options_interface():
+ from charms.layer import options
+ if sys.version_info.minor >= 5:
+ options.__class__ = OptionsBackwardsCompatibilityHack
+ else:
+ # Py 3.4 doesn't support changing the __class__, so we have to do it
+ # another way. The last line is needed because we already have a
+ # reference that doesn't get updated with sys.modules.
+ name = options.__name__
+ hack = OptionsBackwardsCompatibilityHack(name)
+ hack.get = options.get
+ sys.modules[name] = hack
+ sys.modules[__name__].options = hack
+
+
+try:
+ patch_options_interface()
+except ImportError:
+ # This may fail if pyyaml hasn't been installed yet. But in that
+ # case, the bootstrap logic will try it again once it has.
+ pass
diff --git a/kata/lib/charms/layer/basic.py b/kata/lib/charms/layer/basic.py
new file mode 100644
index 0000000..bbdd074
--- /dev/null
+++ b/kata/lib/charms/layer/basic.py
@@ -0,0 +1,501 @@
+import os
+import sys
+import re
+import shutil
+from distutils.version import LooseVersion
+from pkg_resources import Requirement
+from glob import glob
+from subprocess import check_call, check_output, CalledProcessError
+from time import sleep
+
+from charms import layer
+from charms.layer.execd import execd_preinstall
+
+
+def _get_subprocess_env():
+ env = os.environ.copy()
+ env['LANG'] = env.get('LANG', 'C.UTF-8')
+ return env
+
+
+def get_series():
+ """
+ Return series for a few known OS:es.
+ Tested as of 2019 november:
+ * centos6, centos7, rhel6.
+ * bionic
+ """
+ series = ""
+
+ # Looking for content in /etc/os-release
+ # works for ubuntu + some centos
+ if os.path.isfile('/etc/os-release'):
+ d = {}
+ with open('/etc/os-release', 'r') as rel:
+ for l in rel:
+ if not re.match(r'^\s*$', l):
+ k, v = l.split('=')
+ d[k.strip()] = v.strip().replace('"', '')
+ series = "{ID}{VERSION_ID}".format(**d)
+
+ # Looking for content in /etc/redhat-release
+ # works for redhat enterprise systems
+ elif os.path.isfile('/etc/redhat-release'):
+ with open('/etc/redhat-release', 'r') as redhatlsb:
+ # CentOS Linux release 7.7.1908 (Core)
+ line = redhatlsb.readline()
+ release = int(line.split("release")[1].split()[0][0])
+ series = "centos" + str(release)
+
+ # Looking for content in /etc/lsb-release
+ # works for ubuntu
+ elif os.path.isfile('/etc/lsb-release'):
+ d = {}
+ with open('/etc/lsb-release', 'r') as lsb:
+ for l in lsb:
+ k, v = l.split('=')
+ d[k.strip()] = v.strip()
+ series = d['DISTRIB_CODENAME']
+
+ # This is what happens if we cant figure out the OS.
+ else:
+ series = "unknown"
+ return series
+
+
+def bootstrap_charm_deps():
+ """
+ Set up the base charm dependencies so that the reactive system can run.
+ """
+ # execd must happen first, before any attempt to install packages or
+ # access the network, because sites use this hook to do bespoke
+ # configuration and install secrets so the rest of this bootstrap
+ # and the charm itself can actually succeed. This call does nothing
+ # unless the operator has created and populated $JUJU_CHARM_DIR/exec.d.
+ execd_preinstall()
+ # ensure that $JUJU_CHARM_DIR/bin is on the path, for helper scripts
+
+ series = get_series()
+
+ # OMG?! is build-essentials needed?
+ ubuntu_packages = ['python3-pip',
+ 'python3-setuptools',
+ 'python3-yaml',
+ 'python3-dev',
+ 'python3-wheel',
+ 'build-essential']
+
+ # I'm not going to "yum group info "Development Tools"
+ # omitting above madness
+ centos_packages = ['python3-pip',
+ 'python3-setuptools',
+ 'python3-devel',
+ 'python3-wheel']
+
+ packages_needed = []
+ if 'centos' in series:
+ packages_needed = centos_packages
+ else:
+ packages_needed = ubuntu_packages
+
+ charm_dir = os.environ['JUJU_CHARM_DIR']
+ os.environ['PATH'] += ':%s' % os.path.join(charm_dir, 'bin')
+ venv = os.path.abspath('../.venv')
+ vbin = os.path.join(venv, 'bin')
+ vpip = os.path.join(vbin, 'pip')
+ vpy = os.path.join(vbin, 'python')
+ hook_name = os.path.basename(sys.argv[0])
+ is_bootstrapped = os.path.exists('wheelhouse/.bootstrapped')
+ is_charm_upgrade = hook_name == 'upgrade-charm'
+ is_series_upgrade = hook_name == 'post-series-upgrade'
+ is_post_upgrade = os.path.exists('wheelhouse/.upgraded')
+ is_upgrade = (not is_post_upgrade and
+ (is_charm_upgrade or is_series_upgrade))
+ if is_bootstrapped and not is_upgrade:
+ # older subordinates might have downgraded charm-env, so we should
+ # restore it if necessary
+ install_or_update_charm_env()
+ activate_venv()
+ # the .upgrade file prevents us from getting stuck in a loop
+ # when re-execing to activate the venv; at this point, we've
+ # activated the venv, so it's safe to clear it
+ if is_post_upgrade:
+ os.unlink('wheelhouse/.upgraded')
+ return
+ if os.path.exists(venv):
+ try:
+ # focal installs or upgrades prior to PR 160 could leave the venv
+ # in a broken state which would prevent subsequent charm upgrades
+ _load_installed_versions(vpip)
+ except CalledProcessError:
+ is_broken_venv = True
+ else:
+ is_broken_venv = False
+ if is_upgrade or is_broken_venv:
+ # All upgrades should do a full clear of the venv, rather than
+ # just updating it, to bring in updates to Python itself
+ shutil.rmtree(venv)
+ if is_upgrade:
+ if os.path.exists('wheelhouse/.bootstrapped'):
+ os.unlink('wheelhouse/.bootstrapped')
+ # bootstrap wheelhouse
+ if os.path.exists('wheelhouse'):
+ pre_eoan = series in ('ubuntu12.04', 'precise',
+ 'ubuntu14.04', 'trusty',
+ 'ubuntu16.04', 'xenial',
+ 'ubuntu18.04', 'bionic')
+ pydistutils_lines = [
+ "[easy_install]\n",
+ "find_links = file://{}/wheelhouse/\n".format(charm_dir),
+ "no_index=True\n",
+ "index_url=\n", # deliberately nothing here; disables it.
+ ]
+ if pre_eoan:
+ pydistutils_lines.append("allow_hosts = ''\n")
+ with open('/root/.pydistutils.cfg', 'w') as fp:
+ # make sure that easy_install also only uses the wheelhouse
+ # (see https://github.com/pypa/pip/issues/410)
+ fp.writelines(pydistutils_lines)
+ if 'centos' in series:
+ yum_install(packages_needed)
+ else:
+ apt_install(packages_needed)
+ from charms.layer import options
+ cfg = options.get('basic')
+ # include packages defined in layer.yaml
+ if 'centos' in series:
+ yum_install(cfg.get('packages', []))
+ else:
+ apt_install(cfg.get('packages', []))
+ # if we're using a venv, set it up
+ if cfg.get('use_venv'):
+ if not os.path.exists(venv):
+ series = get_series()
+ if series in ('ubuntu12.04', 'precise',
+ 'ubuntu14.04', 'trusty'):
+ apt_install(['python-virtualenv'])
+ elif 'centos' in series:
+ yum_install(['python-virtualenv'])
+ else:
+ apt_install(['virtualenv'])
+ cmd = ['virtualenv', '-ppython3', '--never-download', venv]
+ if cfg.get('include_system_packages'):
+ cmd.append('--system-site-packages')
+ check_call(cmd, env=_get_subprocess_env())
+ os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
+ pip = vpip
+ else:
+ pip = 'pip3'
+ # save a copy of system pip to prevent `pip3 install -U pip`
+ # from changing it
+ if os.path.exists('/usr/bin/pip'):
+ shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save')
+ pre_install_pkgs = ['pip', 'setuptools', 'setuptools-scm']
+ # we bundle these packages to work around bugs in older versions (such
+ # as https://github.com/pypa/pip/issues/56), but if the system already
+ # provided a newer version, downgrading it can cause other problems
+ _update_if_newer(pip, pre_install_pkgs)
+ # install the rest of the wheelhouse deps (extract the pkg names into
+ # a set so that we can ignore the pre-install packages and let pip
+ # choose the best version in case there are multiple from layer
+ # conflicts)
+ _versions = _load_wheelhouse_versions()
+ _pkgs = _versions.keys() - set(pre_install_pkgs)
+ # add back the versions such that each package in pkgs is
+ # ==.
+ # This ensures that pip 20.3.4+ will install the packages from the
+ # wheelhouse without (erroneously) flagging an error.
+ pkgs = _add_back_versions(_pkgs, _versions)
+ reinstall_flag = '--force-reinstall'
+ if not cfg.get('use_venv', True) and pre_eoan:
+ reinstall_flag = '--ignore-installed'
+ check_call([pip, 'install', '-U', reinstall_flag, '--no-index',
+ '--no-cache-dir', '-f', 'wheelhouse'] + list(pkgs),
+ env=_get_subprocess_env())
+ # re-enable installation from pypi
+ os.remove('/root/.pydistutils.cfg')
+
+ # install pyyaml for centos7, since, unlike the ubuntu image, the
+ # default image for centos doesn't include pyyaml; see the discussion:
+ # https://discourse.jujucharms.com/t/charms-for-centos-lets-begin
+ if 'centos' in series:
+ check_call([pip, 'install', '-U', 'pyyaml'],
+ env=_get_subprocess_env())
+
+ # install python packages from layer options
+ if cfg.get('python_packages'):
+ check_call([pip, 'install', '-U'] + cfg.get('python_packages'),
+ env=_get_subprocess_env())
+ if not cfg.get('use_venv'):
+ # restore system pip to prevent `pip3 install -U pip`
+ # from changing it
+ if os.path.exists('/usr/bin/pip.save'):
+ shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip')
+ os.remove('/usr/bin/pip.save')
+ # setup wrappers to ensure envs are used for scripts
+ install_or_update_charm_env()
+ for wrapper in ('charms.reactive', 'charms.reactive.sh',
+ 'chlp', 'layer_option'):
+ src = os.path.join('/usr/local/sbin', 'charm-env')
+ dst = os.path.join('/usr/local/sbin', wrapper)
+ if not os.path.exists(dst):
+ os.symlink(src, dst)
+ if cfg.get('use_venv'):
+ shutil.copy2('bin/layer_option', vbin)
+ else:
+ shutil.copy2('bin/layer_option', '/usr/local/bin/')
+ # re-link the charm copy to the wrapper in case charms
+ # call bin/layer_option directly (as was the old pattern)
+ os.remove('bin/layer_option')
+ os.symlink('/usr/local/sbin/layer_option', 'bin/layer_option')
+ # flag us as having already bootstrapped so we don't do it again
+ open('wheelhouse/.bootstrapped', 'w').close()
+ if is_upgrade:
+ # flag us as having already upgraded so we don't do it again
+ open('wheelhouse/.upgraded', 'w').close()
+ # Ensure that the newly bootstrapped libs are available.
+ # Note: this only seems to be an issue with namespace packages.
+ # Non-namespace-package libs (e.g., charmhelpers) are available
+ # without having to reload the interpreter. :/
+ reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0])
+
+
+def _load_installed_versions(pip):
+ pip_freeze = check_output([pip, 'freeze']).decode('utf8')
+ versions = {}
+ for pkg_ver in pip_freeze.splitlines():
+ try:
+ req = Requirement.parse(pkg_ver)
+ except ValueError:
+ continue
+ versions.update({
+ req.project_name: LooseVersion(ver)
+ for op, ver in req.specs if op == '=='
+ })
+ return versions
+
+
+def _load_wheelhouse_versions():
+ versions = {}
+ for wheel in glob('wheelhouse/*'):
+ pkg, ver = os.path.basename(wheel).rsplit('-', 1)
+ # nb: LooseVersion ignores the file extension
+ versions[pkg.replace('_', '-')] = LooseVersion(ver)
+ return versions
+
+
+def _add_back_versions(pkgs, versions):
+ """Add back the version strings to each of the packages.
+
+ The versions are LooseVersion() from _load_wheelhouse_versions(). This
+ function strips the ".zip" or ".tar.gz" from the end of the version string
+ and adds it back to the package in the form of ==
+
+ If a package name is not a key in the versions dictionary, then it is
+ returned in the list unchanged.
+
+ :param pkgs: A list of package names
+ :type pkgs: List[str]
+ :param versions: A map of package to LooseVersion
+ :type versions: Dict[str, LooseVersion]
+ :returns: A list of (maybe) versioned packages
+ :rtype: List[str]
+ """
+ def _strip_ext(s):
+ """Strip an extension (if it exists) from the string
+
+ :param s: the string to strip an extension off if it exists
+ :type s: str
+ :returns: string without an extension of .zip or .tar.gz
+ :rtype: str
+ """
+ for ending in [".zip", ".tar.gz"]:
+ if s.endswith(ending):
+ return s[:-len(ending)]
+ return s
+
+ def _maybe_add_version(pkg):
+ """Maybe add back the version number to a package if it exists.
+
+ Adds the version number, if the package exists in the lexically
+ captured `versions` dictionary, in the form ==. Strips
+ the extension if it exists.
+
+ :param pkg: the package name to (maybe) add the version number to.
+ :type pkg: str
+ """
+ try:
+ return "{}=={}".format(pkg, _strip_ext(str(versions[pkg])))
+ except KeyError:
+ pass
+ return pkg
+
+ return [_maybe_add_version(pkg) for pkg in pkgs]
+
+
+def _update_if_newer(pip, pkgs):
+ installed = _load_installed_versions(pip)
+ wheelhouse = _load_wheelhouse_versions()
+ for pkg in pkgs:
+ if pkg not in installed or wheelhouse[pkg] > installed[pkg]:
+ check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse',
+ pkg], env=_get_subprocess_env())
+
+
+def install_or_update_charm_env():
+ # On Trusty python3-pkg-resources is not installed
+ try:
+ from pkg_resources import parse_version
+ except ImportError:
+ apt_install(['python3-pkg-resources'])
+ from pkg_resources import parse_version
+
+ try:
+ installed_version = parse_version(
+ check_output(['/usr/local/sbin/charm-env',
+ '--version']).decode('utf8'))
+ except (CalledProcessError, FileNotFoundError):
+ installed_version = parse_version('0.0.0')
+ try:
+ bundled_version = parse_version(
+ check_output(['bin/charm-env',
+ '--version']).decode('utf8'))
+ except (CalledProcessError, FileNotFoundError):
+ bundled_version = parse_version('0.0.0')
+ if installed_version < bundled_version:
+ shutil.copy2('bin/charm-env', '/usr/local/sbin/')
+
+
+def activate_venv():
+ """
+ Activate the venv if enabled in ``layer.yaml``.
+
+ This is handled automatically for normal hooks, but actions might
+ need to invoke this manually, using something like:
+
+ # Load modules from $JUJU_CHARM_DIR/lib
+ import sys
+ sys.path.append('lib')
+
+ from charms.layer.basic import activate_venv
+ activate_venv()
+
+ This will ensure that modules installed in the charm's
+ virtual environment are available to the action.
+ """
+ from charms.layer import options
+ venv = os.path.abspath('../.venv')
+ vbin = os.path.join(venv, 'bin')
+ vpy = os.path.join(vbin, 'python')
+ use_venv = options.get('basic', 'use_venv')
+ if use_venv and '.venv' not in sys.executable:
+ # activate the venv
+ os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
+ reload_interpreter(vpy)
+ layer.patch_options_interface()
+ layer.import_layer_libs()
+
+
+def reload_interpreter(python):
+ """
+ Reload the python interpreter to ensure that all deps are available.
+
+ Newly installed modules in namespace packages sometimes seemt to
+ not be picked up by Python 3.
+ """
+ os.execve(python, [python] + list(sys.argv), os.environ)
+
+
+def apt_install(packages):
+ """
+ Install apt packages.
+
+ This ensures a consistent set of options that are often missed but
+ should really be set.
+ """
+ if isinstance(packages, (str, bytes)):
+ packages = [packages]
+
+ env = _get_subprocess_env()
+
+ if 'DEBIAN_FRONTEND' not in env:
+ env['DEBIAN_FRONTEND'] = 'noninteractive'
+
+ cmd = ['apt-get',
+ '--option=Dpkg::Options::=--force-confold',
+ '--assume-yes',
+ 'install']
+ for attempt in range(3):
+ try:
+ check_call(cmd + packages, env=env)
+ except CalledProcessError:
+ if attempt == 2: # third attempt
+ raise
+ try:
+ # sometimes apt-get update needs to be run
+ check_call(['apt-get', 'update'], env=env)
+ except CalledProcessError:
+ # sometimes it's a dpkg lock issue
+ pass
+ sleep(5)
+ else:
+ break
+
+
+def yum_install(packages):
+ """ Installs packages with yum.
+ This function largely mimics the apt_install function for consistency.
+ """
+ if packages:
+ env = os.environ.copy()
+ cmd = ['yum', '-y', 'install']
+ for attempt in range(3):
+ try:
+ check_call(cmd + packages, env=env)
+ except CalledProcessError:
+ if attempt == 2:
+ raise
+ try:
+ check_call(['yum', 'update'], env=env)
+ except CalledProcessError:
+ pass
+ sleep(5)
+ else:
+ break
+ else:
+ pass
+
+
+def init_config_states():
+ import yaml
+ from charmhelpers.core import hookenv
+ from charms.reactive import set_state
+ from charms.reactive import toggle_state
+ config = hookenv.config()
+ config_defaults = {}
+ config_defs = {}
+ config_yaml = os.path.join(hookenv.charm_dir(), 'config.yaml')
+ if os.path.exists(config_yaml):
+ with open(config_yaml) as fp:
+ config_defs = yaml.safe_load(fp).get('options', {})
+ config_defaults = {key: value.get('default')
+ for key, value in config_defs.items()}
+ for opt in config_defs.keys():
+ if config.changed(opt):
+ set_state('config.changed')
+ set_state('config.changed.{}'.format(opt))
+ toggle_state('config.set.{}'.format(opt), config.get(opt))
+ toggle_state('config.default.{}'.format(opt),
+ config.get(opt) == config_defaults[opt])
+
+
+def clear_config_states():
+ from charmhelpers.core import hookenv, unitdata
+ from charms.reactive import remove_state
+ config = hookenv.config()
+ remove_state('config.changed')
+ for opt in config.keys():
+ remove_state('config.changed.{}'.format(opt))
+ remove_state('config.set.{}'.format(opt))
+ remove_state('config.default.{}'.format(opt))
+ unitdata.kv().flush()
diff --git a/kata/lib/charms/layer/execd.py b/kata/lib/charms/layer/execd.py
new file mode 100644
index 0000000..438d9a1
--- /dev/null
+++ b/kata/lib/charms/layer/execd.py
@@ -0,0 +1,114 @@
+# Copyright 2014-2016 Canonical Limited.
+#
+# This file is part of layer-basic, the reactive base layer for Juju.
+#
+# charm-helpers is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3 as
+# published by the Free Software Foundation.
+#
+# charm-helpers is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with charm-helpers. If not, see .
+
+# This module may only import from the Python standard library.
+import os
+import sys
+import subprocess
+import time
+
+'''
+execd/preinstall
+
+Read the layer-basic docs for more info on how to use this feature.
+https://charmsreactive.readthedocs.io/en/latest/layer-basic.html#exec-d-support
+'''
+
+
+def default_execd_dir():
+ return os.path.join(os.environ['JUJU_CHARM_DIR'], 'exec.d')
+
+
+def execd_module_paths(execd_dir=None):
+ """Generate a list of full paths to modules within execd_dir."""
+ if not execd_dir:
+ execd_dir = default_execd_dir()
+
+ if not os.path.exists(execd_dir):
+ return
+
+ for subpath in os.listdir(execd_dir):
+ module = os.path.join(execd_dir, subpath)
+ if os.path.isdir(module):
+ yield module
+
+
+def execd_submodule_paths(command, execd_dir=None):
+ """Generate a list of full paths to the specified command within exec_dir.
+ """
+ for module_path in execd_module_paths(execd_dir):
+ path = os.path.join(module_path, command)
+ if os.access(path, os.X_OK) and os.path.isfile(path):
+ yield path
+
+
+def execd_sentinel_path(submodule_path):
+ module_path = os.path.dirname(submodule_path)
+ execd_path = os.path.dirname(module_path)
+ module_name = os.path.basename(module_path)
+ submodule_name = os.path.basename(submodule_path)
+ return os.path.join(execd_path,
+ '.{}_{}.done'.format(module_name, submodule_name))
+
+
+def execd_run(command, execd_dir=None, stop_on_error=True, stderr=None):
+ """Run command for each module within execd_dir which defines it."""
+ if stderr is None:
+ stderr = sys.stdout
+ for submodule_path in execd_submodule_paths(command, execd_dir):
+ # Only run each execd once. We cannot simply run them in the
+ # install hook, as potentially storage hooks are run before that.
+ # We cannot rely on them being idempotent.
+ sentinel = execd_sentinel_path(submodule_path)
+ if os.path.exists(sentinel):
+ continue
+
+ try:
+ subprocess.check_call([submodule_path], stderr=stderr,
+ universal_newlines=True)
+ with open(sentinel, 'w') as f:
+ f.write('{} ran successfully {}\n'.format(submodule_path,
+ time.ctime()))
+ f.write('Removing this file will cause it to be run again\n')
+ except subprocess.CalledProcessError as e:
+ # Logs get the details. We can't use juju-log, as the
+ # output may be substantial and exceed command line
+ # length limits.
+ print("ERROR ({}) running {}".format(e.returncode, e.cmd),
+ file=stderr)
+ print("STDOUT<"
+"description": |
+ Kata Containers is an open source community working to build a secure
+ container runtime with lightweight virtual machines that feel and perform
+ like containers, but provide stronger workload isolation using hardware
+ virtualization technology as a second layer of defense.
+"tags":
+- "misc"
+- "containers"
+"series":
+- "focal"
+- "bionic"
+"requires":
+ "containerd":
+ "interface": "container-runtime"
+ "scope": "container"
+ "untrusted":
+ "interface": "untrusted-container-runtime"
+ "scope": "container"
+"resources":
+ "kata-archive":
+ "type": "file"
+ "filename": "kata-archive.tar.gz"
+ "description": "Offline archive of kata"
+
+"subordinate": !!bool "true"
diff --git a/kata/pydocmd.yml b/kata/pydocmd.yml
new file mode 100644
index 0000000..ab3b2ef
--- /dev/null
+++ b/kata/pydocmd.yml
@@ -0,0 +1,16 @@
+site_name: 'Status Management Layer'
+
+generate:
+ - status.md:
+ - charms.layer.status.WorkloadState
+ - charms.layer.status.maintenance
+ - charms.layer.status.maint
+ - charms.layer.status.blocked
+ - charms.layer.status.waiting
+ - charms.layer.status.active
+ - charms.layer.status.status_set
+
+pages:
+ - Status Management Layer: status.md
+
+gens_dir: docs
diff --git a/kata/reactive/__init__.py b/kata/reactive/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/kata/reactive/kata.py b/kata/reactive/kata.py
new file mode 100644
index 0000000..e11c45f
--- /dev/null
+++ b/kata/reactive/kata.py
@@ -0,0 +1,136 @@
+import os
+import requests
+
+from subprocess import (
+ check_call,
+ check_output
+)
+
+from charmhelpers.core import host
+
+from charms.reactive import (
+ when,
+ when_not,
+ set_state,
+ remove_state,
+ endpoint_from_flag,
+ hook,
+)
+
+from charmhelpers.fetch import (
+ apt_install,
+ apt_update,
+ apt_purge,
+ apt_autoremove,
+ import_key
+)
+
+from charmhelpers.core.hookenv import (
+ resource_get
+)
+
+from charms.layer import status
+
+
+KATA_PACKAGES = [
+ 'kata-runtime',
+ 'kata-proxy',
+ 'kata-shim'
+]
+
+
+@when_not('kata.installed')
+@when_not('endpoint.untrusted.departed')
+def install_kata():
+ """
+ Install the Kata container runtime.
+
+ :returns: None
+ """
+ dist = host.lsb_release()
+ release = '{}_{}'.format(
+ dist['DISTRIB_ID'],
+ dist['DISTRIB_RELEASE']
+ )
+
+ arch = check_output(['arch']).decode().strip()
+
+ archive = resource_get('kata-archive')
+
+ if not archive or os.path.getsize(archive) == 0:
+ status.maintenance('Installing Kata via apt')
+ gpg_key = requests.get(
+ 'http://download.opensuse.org/repositories/home:/katacontainers:/'
+ 'releases:/{}:/master/x{}/Release.key'.format(arch, release)).text
+ import_key(gpg_key)
+
+ with open('/etc/apt/sources.list.d/kata-containers.list', 'w') as f:
+ f.write(
+ 'deb http://download.opensuse.org/repositories/home:/'
+ 'katacontainers:/releases:/{}:/master/x{}/ /'
+ .format(arch, release)
+ )
+
+ apt_update()
+ apt_install(KATA_PACKAGES)
+
+ else:
+ status.maintenance('Installing Kata via resource')
+ unpack = '/tmp/kata-debs'
+
+ if not os.path.isdir(unpack):
+ os.makedirs(unpack, exist_ok=True)
+
+ check_call(['tar', '-xvf', archive, '-C', unpack])
+ check_call('apt-get install -y {}/*.deb'.format(unpack), shell=True)
+
+ status.active('Kata runtime available')
+ set_state('kata.installed')
+
+
+@when('endpoint.untrusted.departed')
+def purge_kata():
+ """
+ Purge Kata containers.
+
+ :return: None
+ """
+ status.maintenance('Purging Kata')
+
+ apt_purge(KATA_PACKAGES, fatal=False)
+
+ source = '/etc/apt/sources.list.d/kata-containers.list'
+ if os.path.isfile(source):
+ os.remove(source)
+
+ apt_autoremove()
+
+ remove_state('kata.installed')
+
+
+@when('kata.installed')
+@when('endpoint.untrusted.joined')
+@when_not('endpoint.untrusted.departed')
+def publish_config():
+ """
+ Pass configuration over the interface.
+
+ :return: None
+ """
+ endpoint = endpoint_from_flag('endpoint.untrusted.joined')
+ endpoint.set_config(
+ name='kata',
+ binary_path='/usr/bin/kata-runtime'
+ )
+
+
+@hook('pre-series-upgrade')
+def pre_series_upgrade():
+ """Set status during series upgrade."""
+ status.blocked('Series upgrade in progress')
+
+
+@hook('post-series-upgrade')
+def post_series_upgrade():
+ """Reset status to active after series upgrade."""
+ status.active('Kata runtime available')
diff --git a/kata/reactive/status.py b/kata/reactive/status.py
new file mode 100644
index 0000000..2f33f3f
--- /dev/null
+++ b/kata/reactive/status.py
@@ -0,0 +1,4 @@
+from charms import layer
+
+
+layer.status._initialize()
diff --git a/kata/requirements.txt b/kata/requirements.txt
new file mode 100644
index 0000000..55543d9
--- /dev/null
+++ b/kata/requirements.txt
@@ -0,0 +1,3 @@
+mock
+flake8
+pytest
diff --git a/kata/revision b/kata/revision
new file mode 100644
index 0000000..c227083
--- /dev/null
+++ b/kata/revision
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/kata/tests/conftest.py b/kata/tests/conftest.py
new file mode 100644
index 0000000..a92e249
--- /dev/null
+++ b/kata/tests/conftest.py
@@ -0,0 +1,4 @@
+import charms.unit_test
+
+
+charms.unit_test.patch_reactive()
diff --git a/kata/tests/test_kata_reactive.py b/kata/tests/test_kata_reactive.py
new file mode 100644
index 0000000..421b0dd
--- /dev/null
+++ b/kata/tests/test_kata_reactive.py
@@ -0,0 +1,35 @@
+from reactive import kata
+
+
+def test_packages_list():
+ """Assert KATA_PACKAGES is a list of strings."""
+ assert isinstance(kata.KATA_PACKAGES, list)
+ for item in kata.KATA_PACKAGES:
+ assert isinstance(item, str)
+
+
+def test_install_kata():
+ """Assert install_kata is a method."""
+ assert callable(kata.install_kata)
+
+
+def test_purge_kata():
+ """Assert purge_kata is a method."""
+ assert callable(kata.purge_kata)
+
+
+def test_publist_config():
+ """Assert publish_config is a method."""
+ assert callable(kata.publish_config)
+
+
+def test_series_upgrade():
+ """Assert status is set during series upgrade."""
+ assert kata.status.blocked.call_count == 0
+ assert kata.status.active.call_count == 0
+ kata.pre_series_upgrade()
+ assert kata.status.blocked.call_count == 1
+ assert kata.status.active.call_count == 0
+ kata.post_series_upgrade()
+ assert kata.status.blocked.call_count == 1
+ assert kata.status.active.call_count == 1
diff --git a/kata/tox.ini b/kata/tox.ini
new file mode 100644
index 0000000..0c9eaec
--- /dev/null
+++ b/kata/tox.ini
@@ -0,0 +1,34 @@
+[flake8]
+max-line-length = 120
+ignore = D100
+
+[tox]
+skipsdist = True
+envlist = lint,py3
+
+[tox:travis]
+3.5: lint,py3
+3.6: lint, py3
+3.7: lint, py3
+3.8: lint, py3
+
+[testenv]
+basepython = python3
+setenv =
+ PYTHONPATH={toxinidir}
+deps =
+ pyyaml
+ pytest
+ pytest-cov
+ flake8
+ flake8-docstrings
+ requests
+ git+https://github.com/juju-solutions/charms.unit_test/#egg=charms.unit_test
+commands =
+ pytest --cov-report term-missing \
+ --cov reactive.kata --cov-fail-under 30 \
+ --tb native -s {posargs}
+
+[testenv:lint]
+envdir = {toxworkdir}/py3
+commands = flake8 {toxinidir}/reactive {toxinidir}/tests
diff --git a/kata/version b/kata/version
new file mode 100644
index 0000000..20817dd
--- /dev/null
+++ b/kata/version
@@ -0,0 +1 @@
+ccfa68be
\ No newline at end of file
diff --git a/kata/wheelhouse.txt b/kata/wheelhouse.txt
new file mode 100644
index 0000000..4413da7
--- /dev/null
+++ b/kata/wheelhouse.txt
@@ -0,0 +1,21 @@
+# layer:basic
+# pip is pinned to <19.0 to avoid https://github.com/pypa/pip/issues/6164
+# even with installing setuptools before upgrading pip ends up with pip seeing
+# the older setuptools at the system level if include_system_packages is true
+pip>=18.1,<19.0
+# pin Jinja2, PyYAML and MarkupSafe to the last versions supporting python 3.5
+# for trusty
+Jinja2<=2.10.1
+PyYAML<=5.2
+MarkupSafe<2.0.0
+setuptools<42
+setuptools-scm<=1.17.0
+charmhelpers>=0.4.0,<1.0.0
+charms.reactive>=0.1.0,<2.0.0
+wheel<0.34
+# pin netaddr to avoid pulling importlib-resources
+netaddr<=0.7.19
+
+# kata
+requests
+
diff --git a/kata/wheelhouse/Jinja2-2.10.1.tar.gz b/kata/wheelhouse/Jinja2-2.10.1.tar.gz
new file mode 100644
index 0000000..ffd1054
Binary files /dev/null and b/kata/wheelhouse/Jinja2-2.10.1.tar.gz differ
diff --git a/kata/wheelhouse/MarkupSafe-1.1.1.tar.gz b/kata/wheelhouse/MarkupSafe-1.1.1.tar.gz
new file mode 100644
index 0000000..a6dad8e
Binary files /dev/null and b/kata/wheelhouse/MarkupSafe-1.1.1.tar.gz differ
diff --git a/kata/wheelhouse/PyYAML-5.2.tar.gz b/kata/wheelhouse/PyYAML-5.2.tar.gz
new file mode 100644
index 0000000..666d12a
Binary files /dev/null and b/kata/wheelhouse/PyYAML-5.2.tar.gz differ
diff --git a/kata/wheelhouse/Tempita-0.5.2.tar.gz b/kata/wheelhouse/Tempita-0.5.2.tar.gz
new file mode 100644
index 0000000..755befc
Binary files /dev/null and b/kata/wheelhouse/Tempita-0.5.2.tar.gz differ
diff --git a/kata/wheelhouse/certifi-2021.10.8.tar.gz b/kata/wheelhouse/certifi-2021.10.8.tar.gz
new file mode 100644
index 0000000..9e1581b
Binary files /dev/null and b/kata/wheelhouse/certifi-2021.10.8.tar.gz differ
diff --git a/kata/wheelhouse/charmhelpers-0.20.23.tar.gz b/kata/wheelhouse/charmhelpers-0.20.23.tar.gz
new file mode 100644
index 0000000..8fbc8ec
Binary files /dev/null and b/kata/wheelhouse/charmhelpers-0.20.23.tar.gz differ
diff --git a/kata/wheelhouse/charms.reactive-1.4.1.tar.gz b/kata/wheelhouse/charms.reactive-1.4.1.tar.gz
new file mode 100644
index 0000000..03bc1fe
Binary files /dev/null and b/kata/wheelhouse/charms.reactive-1.4.1.tar.gz differ
diff --git a/kata/wheelhouse/charset-normalizer-2.0.7.tar.gz b/kata/wheelhouse/charset-normalizer-2.0.7.tar.gz
new file mode 100644
index 0000000..61df022
Binary files /dev/null and b/kata/wheelhouse/charset-normalizer-2.0.7.tar.gz differ
diff --git a/kata/wheelhouse/idna-3.3.tar.gz b/kata/wheelhouse/idna-3.3.tar.gz
new file mode 100644
index 0000000..ff2bcbf
Binary files /dev/null and b/kata/wheelhouse/idna-3.3.tar.gz differ
diff --git a/kata/wheelhouse/netaddr-0.7.19.tar.gz b/kata/wheelhouse/netaddr-0.7.19.tar.gz
new file mode 100644
index 0000000..cc31d9d
Binary files /dev/null and b/kata/wheelhouse/netaddr-0.7.19.tar.gz differ
diff --git a/kata/wheelhouse/pbr-5.6.0.tar.gz b/kata/wheelhouse/pbr-5.6.0.tar.gz
new file mode 100644
index 0000000..0d5c965
Binary files /dev/null and b/kata/wheelhouse/pbr-5.6.0.tar.gz differ
diff --git a/kata/wheelhouse/pip-18.1.tar.gz b/kata/wheelhouse/pip-18.1.tar.gz
new file mode 100644
index 0000000..a18192d
Binary files /dev/null and b/kata/wheelhouse/pip-18.1.tar.gz differ
diff --git a/kata/wheelhouse/pyaml-21.10.1.tar.gz b/kata/wheelhouse/pyaml-21.10.1.tar.gz
new file mode 100644
index 0000000..b19aad3
Binary files /dev/null and b/kata/wheelhouse/pyaml-21.10.1.tar.gz differ
diff --git a/kata/wheelhouse/requests-2.26.0.tar.gz b/kata/wheelhouse/requests-2.26.0.tar.gz
new file mode 100644
index 0000000..101dc79
Binary files /dev/null and b/kata/wheelhouse/requests-2.26.0.tar.gz differ
diff --git a/kata/wheelhouse/setuptools-41.6.0.zip b/kata/wheelhouse/setuptools-41.6.0.zip
new file mode 100644
index 0000000..3345759
Binary files /dev/null and b/kata/wheelhouse/setuptools-41.6.0.zip differ
diff --git a/kata/wheelhouse/setuptools_scm-1.17.0.tar.gz b/kata/wheelhouse/setuptools_scm-1.17.0.tar.gz
new file mode 100644
index 0000000..43b16c7
Binary files /dev/null and b/kata/wheelhouse/setuptools_scm-1.17.0.tar.gz differ
diff --git a/kata/wheelhouse/six-1.16.0.tar.gz b/kata/wheelhouse/six-1.16.0.tar.gz
new file mode 100644
index 0000000..5bf3a27
Binary files /dev/null and b/kata/wheelhouse/six-1.16.0.tar.gz differ
diff --git a/kata/wheelhouse/urllib3-1.26.7.tar.gz b/kata/wheelhouse/urllib3-1.26.7.tar.gz
new file mode 100644
index 0000000..990abe6
Binary files /dev/null and b/kata/wheelhouse/urllib3-1.26.7.tar.gz differ
diff --git a/kata/wheelhouse/wheel-0.33.6.tar.gz b/kata/wheelhouse/wheel-0.33.6.tar.gz
new file mode 100644
index 0000000..c922c4e
Binary files /dev/null and b/kata/wheelhouse/wheel-0.33.6.tar.gz differ
diff --git a/kubernetes-master/cni-amd64.tar.gz b/kubernetes-master/cni-amd64.tar.gz
deleted file mode 100644
index d144b41..0000000
Binary files a/kubernetes-master/cni-amd64.tar.gz and /dev/null differ
diff --git a/kubernetes-master/cni-arm64.tar.gz b/kubernetes-master/cni-arm64.tar.gz
deleted file mode 100644
index 3d3844c..0000000
Binary files a/kubernetes-master/cni-arm64.tar.gz and /dev/null differ
diff --git a/kubernetes-master/cni-s390x.tar.gz b/kubernetes-master/cni-s390x.tar.gz
deleted file mode 100644
index 5607993..0000000
Binary files a/kubernetes-master/cni-s390x.tar.gz and /dev/null differ