Compare commits

..

29 Commits

Author SHA1 Message Date
Ares 47ae841bd2 update bugfix/1.27-GA 2024-03-14 17:00:56 +08:00
Ares f3a6aeb459 update bugfix/1.27-GA 2024-03-14 06:48:53 +00:00
Ares 00c1cfa456 update bugfix/1.27-GA 2024-03-13 23:36:24 +08:00
Ares 60a6ae7e7b update bugfix/1.27-GA 2024-03-13 15:07:05 +00:00
Ares 4e2e48becd Delete resources/kubelet_3185.snap 2024-03-13 14:46:39 +00:00
Ares e275ee7b2e Delete resources/kubelet_3185.assert 2024-03-13 14:46:34 +00:00
Ares 8b18cbf2b8 Delete resources/kubectl_3178.snap 2024-03-13 14:46:30 +00:00
Ares 3a632f5710 Delete resources/kubectl_3178.assert 2024-03-13 14:46:25 +00:00
Ares 506211eb69 Delete resources/kube-scheduler_3238.snap 2024-03-13 14:46:21 +00:00
Ares 1868559bba Delete resources/kube-scheduler_3238.assert 2024-03-13 14:46:16 +00:00
Ares 594c0c4ef5 Delete resources/kube-proxy_3266.snap 2024-03-13 14:46:12 +00:00
Ares caad29860c Delete resources/kube-proxy_3266.assert 2024-03-13 14:46:06 +00:00
Ares 4555bd1d76 Delete resources/kube-controller-manager_3355.snap 2024-03-13 14:45:58 +00:00
Ares 43395845c1 Delete resources/kube-controller-manager_3355.assert 2024-03-13 14:45:53 +00:00
Ares 2a76fcf0dc Delete resources/kube-apiserver_3579.snap 2024-03-13 14:45:48 +00:00
Ares 7647272267 Delete resources/kube-apiserver_3579.assert 2024-03-13 14:45:43 +00:00
Ares fbaaad8bc8 Delete resources/cdk-addons_23650.snap 2024-03-13 14:45:38 +00:00
Ares 4543ac033c Delete resources/cdk-addons_23650.assert 2024-03-13 14:45:33 +00:00
Ares a31bd77eb6 update bugfix/1.27-GA 2024-03-13 14:42:11 +00:00
Ares af4be39756 update bugfix/1.27-GA 2024-03-13 14:35:54 +00:00
Ares a7af3112cc Delete coredns.charm 2024-03-13 11:31:55 +00:00
Ares 827cedf08b Delete kata.charm 2024-03-13 11:31:50 +00:00
Ares a859235384 Delete kubernetes-worker.charm 2024-03-13 11:13:10 +00:00
Ares c54d3f159e Delete kubernetes-control-plane.charm 2024-03-13 11:13:04 +00:00
Ares 8589aa0b5f Delete kubeapi-load-balancer.charm 2024-03-13 11:12:58 +00:00
Ares 61f480b0b8 update bugfix/1.27-GA 2024-03-13 11:07:46 +00:00
Ares f5db1950ea Update ReadME.MD 2024-03-13 11:04:21 +00:00
Ares c872025853 update 2023-07-10 12:05:11 +08:00
AnonSaber 8f7368231b 1.27 GA Release 2023-07-10 04:03:04 +00:00
1039 changed files with 77755 additions and 180 deletions

View File

@ -1,64 +1,31 @@
## 安装 Charm 2.x
# Kubernetes 1.27
## Important Change
### Support for Juju 3.1
With this release, Charmed Kubernetes can be deployed with a juju 2.9, 3.0 or 3.1 controller. This release is intended to serve as a migration point away from juju 2.9 deployments which is why we offer a tested strategy of our charms on both 2.9 and 3.1 releases.
### Cilium CNI
We are excited to announce the inclusion of Cilium in our portfolio of Container Network Interface solutions for Charmed Kubernetes. Cilium is a powerful CNI, network security and observability solution which provides enhanced performance and improved security for containerised applications. The current version of Cilium shipped within the charm is 1.12.5. It also comes bundled with Hubble: a networking and security observability solution that offers real time insights of the network and the security state of the cluster with little performance impact.
### Cloud Providers and Cloud Storage
Operator charms for external cloud providers have been expanded and now include AWS, Azure, GCP, and vSphere. Previously, cloud-specific features such as load balancing and storage were managed by Kubernetes in-tree solutions. Today, cloud provider charms offer flexible management of these features decoupled from any specific Kubernetes release.
### NVIDIA Network Operator
The NVIDIA network operator charm is a new addition to the Charmed Kubernetes ecosystem. It simplifies the deployment, operation, and management of NVIDIA networking resources for Kubernetes.
```Bash
# 如果是使用 1.24 版本以及以后, 此步骤可以跳过
snap install charm --channel=2.x --classic
```
## 获取特定版本的 Bundle
```Bash
wget https://raw.githubusercontent.com/charmed-kubernetes/bundle/main/releases/1.21/bundle.yaml
```
或是
```Bash
# 对于 1.23 版本以及以前
charm pull cs:~containers/charmed-kubernetes-657
# 对于 1.24 版本以及以后
juju download ch:charmed-kubernetes --channel 1.24/stable --series focal
```
## 拉取 Bundle 中的相关组件
```Bash
# 对于 1.23 版本以及以前
charm pull cs:~containers/etcd-607
# 对于 1.24 版本以及以后
juju download ch:etcd --channel 1.24/stable --series focal
```
## 拉取相关组件的资源包
```Bash
# 对于 1.23 版本以及以前
charm list-resources cs:~containers/calico-812
wget https://api.jujucharms.com/charmstore/v5/~containers/calico-812/resource/calico/1027 -O calico.tgz
wget https://api.jujucharms.com/charmstore/v5/~containers/calico-812/resource/calico-arm64/1026 -O calico-arm64.tgz
wget https://api.jujucharms.com/charmstore/v5/~containers/calico-812/resource/calico-node-image/709 -O calico-node-image.tgz
wget https://api.jujucharms.com/charmstore/v5/~containers/calico-812/resource/calico-upgrade/854 -O calico-upgrade.tgz
wget https://api.jujucharms.com/charmstore/v5/~containers/calico-812/resource/calico-upgrade-arm64/854 -O calico-upgrade-arm64.tgz
# 对于 1.24 版本以及以后
unzip xxx.charm
build-xxx-resource.sh
# 或
前往 https://charmhub.io/calico/resources/calico?channel=1.24/stable 下载对应的 Revision
```
## 向 Controller 上传资源
```Bash
# ex: juju attach-resource <charm-name> resource-name=<filepath>
juju attach-resource calico calico=/home/sa/charm/calico/calico-amd64.tar.gz
juju attach-resource calico calico-upgrade=/home/sa/charm/calico/calico-upgrade-amd64.tar.gz
```
## 回收节点
```None
https://git.motofans.club/Motofans/Charmed-Kubernetes/src/branch/Guide/cleanup-k8s-node.py
```
juju download ch:etcd --channel 1.27/stable --series focal
juju download ch:easyrsa --channel 1.27/stable --series focal
juju download ch:kubernetes-control-plane --channel 1.27/stable --series focal
juju download ch:kubernetes-worker --channel 1.27/stable --series focal
juju download ch:calico --channel 1.27/stable --series focal
juju download ch:containerd --channel 1.27/stable --series focal
# Extend
juju download ch:kubeapi-load-balancer --channel 1.27/stable --series focal
juju download ch:keepalived --channel 1.27/stable --series focal
```

BIN
calico.charm Normal file

Binary file not shown.

View File

@ -1,50 +0,0 @@
#!/usr/bin/env python
import subprocess
# Purge snap packages
snap_packages = [
"cdk-addons",
"kube-apiserver",
"kube-controller-manager",
"kube-proxy",
"kube-scheduler",
"kubelet",
"kubectl",
"etcd"
]
for package in snap_packages:
subprocess.run(["snap", "remove", package, "--purge"])
# Purge Debian packages
packages_to_purge = [
"nginx-full",
"containerd",
"keepalived"
]
subprocess.run(["apt", "purge", "-y"] + packages_to_purge + ["--allow-change-held-packages"])
subprocess.run(["apt", "autoremove", "-y", "--purge"])
subprocess.run(["/sbin/remove-juju-services"])
# Remove directories and files
directories = [
"/var/log/juju",
"/var/lib/juju",
"/home/ubuntu/config*",
"/root/cdk",
"/etc/juju-proxy*",
"/var/run/calico",
"/var/lib/calico",
"/var/log/calico",
"/etc/containerd",
"/var/lib/containerd",
"/opt/calicoctl",
"/opt/cni",
"/opt/containerd",
"/etc/keepalived"
]
for directory in directories:
subprocess.run(["rm", "-rf", directory])

BIN
containerd.charm Normal file

Binary file not shown.

BIN
easyrsa.charm Normal file

Binary file not shown.

BIN
etcd.charm Normal file

Binary file not shown.

BIN
keepalived.charm Normal file

Binary file not shown.

View File

@ -0,0 +1,991 @@
{
"layers": [
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "f491ebe32b503c9712d2f8cd602dcce18f4aab46",
"url": "layer:metrics"
},
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "fcdcea4e5de3e1556c24e6704607862d0ba00a56",
"url": "layer:options"
},
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "a41b3559d629993d960af65104108f66ab9795a8",
"url": "layer:basic"
},
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "47dfcd4920ef6317850a4837ef0057ab0092a18e",
"url": "layer:nagios"
},
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "a7d7b6423db37a47611310039e6ed1929c0a2eab",
"url": "layer:status"
},
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "76bddfb640ab8767fc7e4a4b73a4a4e781948f34",
"url": "layer:apt"
},
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "672d27695b512e50f51777b1eb63c5ff157b3d9e",
"url": "layer:nginx"
},
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "527dd64fc4b9a6b0f8d80a3c2c0b865155050275",
"url": "layer:debug"
},
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "ed989b244333a2e18b7db609b9d516222e54703f",
"url": "layer:tls-client"
},
{
"branch": "refs/heads/release_1.27",
"rev": "982db815ad2e4720ec53d9e51f1139f0a8590a75",
"url": "layer:kubernetes-common"
},
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "5b0926cdc45f511a0040b0b26f89bd174d5c81eb",
"url": "layer:hacluster"
},
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "cc5bd3f49b2fa5e6c3ab2336763c313ec8bf083f",
"url": "layer:leadership"
},
{
"branch": "refs/heads/release_1.27",
"rev": "7c5d69b3dd4b5a241e325f3e9ffabfb21509d062",
"url": "kubeapi-load-balancer"
},
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "95d744d1dbc4d86fb0462283c9371619bf5bbc24",
"url": "interface:nrpe-external-master"
},
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "632131b1f122daf6fb601fd4c9f1e4dbb1a92e09",
"url": "interface:http"
},
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "61e9f278fc8c8119b1d4810ac39e2275be58e9ce",
"url": "interface:tls-certificates"
},
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "5fc5216f51dcf98530d45e137d55fd94b39d150a",
"url": "interface:hacluster"
},
{
"branch": "refs/heads/main\nrefs/heads/release_1.27",
"rev": "5021f8a23f6e6e4cc449d2d02f2d8cb99763ec27",
"url": "interface:public-address"
}
],
"signatures": {
".build.manifest": [
"build",
"dynamic",
"unchecked"
],
".github/workflows/main.yml": [
"kubeapi-load-balancer",
"static",
"42f320fc534f333ec46b25325d6ff4d25cee2964cffaa8121e3056a342a41f01"
],
".gitignore": [
"kubeapi-load-balancer",
"static",
"58e67f82f991b0c2d359d93622964c7c4f963aff3f8e2b7224b69810606c6c42"
],
"AUTHORS": [
"layer:nginx",
"static",
"5e460cc5d7fe5ce6dc5c4e8eefc13159ee58874667baf9af3b5fa9b597a10fa2"
],
"CONTRIBUTING.md": [
"kubeapi-load-balancer",
"static",
"7155516596ae597b0b7065f0463ff69031d689c0fc565998b51c06d999129d5a"
],
"LICENSE": [
"kubeapi-load-balancer",
"static",
"58d1e17ffe5109a7ae296caafcadfdbe6a7d176f0bc4ab01e12a689b0499d8bd"
],
"Makefile": [
"layer:basic",
"static",
"b7ab3a34e5faf79b96a8632039a0ad0aa87f2a9b5f0ba604e007cafb22190301"
],
"README.md": [
"kubeapi-load-balancer",
"static",
"9efc40856c08af5871a051144d8c3bb518983a3bca118defbb81ad849b3f3c8d"
],
"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"
],
"config.yaml": [
"kubeapi-load-balancer",
"dynamic",
"586a155cd5fb93090f379e3c1ec9d350b89d73c58ebad447b03e36a886010ba7"
],
"copyright": [
"kubeapi-load-balancer",
"static",
"badd4492d214890abd07b615f9e1a7a5ff3339b6c44655a826c746a9263ff00d"
],
"copyright.layer-apt": [
"layer:apt",
"static",
"5123b2d0220fefb4424a463216fb41a6dd7cfad49c9799ba7037f1e74a2fd6bc"
],
"copyright.layer-basic": [
"layer:basic",
"static",
"f6740d66fd60b60f2533d9fcb53907078d1e20920a0219afce7182e2a1c97629"
],
"copyright.layer-leadership": [
"layer:leadership",
"static",
"8ce407829378fc0f72ce44c7f624e4951c7ccb3db1cfb949bee026b701728cc9"
],
"copyright.layer-metrics": [
"layer:metrics",
"static",
"08509dcbade4c20761ba4382ef23c831744dbab1d4a8dd94a1c2b4d4e913334c"
],
"copyright.layer-nagios": [
"layer:nagios",
"static",
"47b2363574909e748bcc471d9004780ac084b301c154905654b5b6f088474749"
],
"copyright.layer-nginx": [
"layer:nginx",
"static",
"66b7d69f452f9203cbf702c57c58b16b359be9970781deb0e21893620dd52516"
],
"copyright.layer-options": [
"layer:options",
"static",
"f6740d66fd60b60f2533d9fcb53907078d1e20920a0219afce7182e2a1c97629"
],
"copyright.layer-status": [
"layer:status",
"static",
"7c0e36e618a8544faaaa3f8e0533c2f1f4a18bcacbdd8b99b537742e6b587d58"
],
"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"
],
"debug-scripts/tls-certs": [
"layer:tls-client",
"static",
"ebf7f23ef6e39fb8e664bac2e9429e32aaeb673b4a51751724b835c007e85d3b"
],
"docs/status.md": [
"layer:status",
"static",
"975dec9f8c938196e102e954a80226bda293407c4e5ae857c118bf692154702a"
],
"hooks/apiserver-relation-broken": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/apiserver-relation-changed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/apiserver-relation-departed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/apiserver-relation-joined": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/certificates-relation-broken": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/certificates-relation-changed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/certificates-relation-departed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/certificates-relation-joined": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/collect-metrics": [
"layer:metrics",
"static",
"139fe18ce4cf2bed2155d3d0fce1c3b4cf1bc2598242cda42b3d772ec9bf8558"
],
"hooks/config-changed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/ha-relation-broken": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/ha-relation-changed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/ha-relation-departed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/ha-relation-joined": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/hook.template": [
"layer:basic",
"static",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/install": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/lb-consumers-relation-broken": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/lb-consumers-relation-changed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/lb-consumers-relation-departed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/lb-consumers-relation-joined": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/leader-elected": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/leader-settings-changed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/loadbalancer-relation-broken": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/loadbalancer-relation-changed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/loadbalancer-relation-departed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/loadbalancer-relation-joined": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/nrpe-external-master-relation-broken": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/nrpe-external-master-relation-changed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/nrpe-external-master-relation-departed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/nrpe-external-master-relation-joined": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/post-series-upgrade": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/pre-series-upgrade": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/relations/hacluster/.stestr.conf": [
"interface:hacluster",
"static",
"46965969e6df6ac729b7dac68d57bc4e677e9f4d79d445be77f54ca3b9e58774"
],
"hooks/relations/hacluster/README.md": [
"interface:hacluster",
"static",
"7fad91e409c6e559cdb76d11c89c325531adc25679049a629a28c4f890755f1f"
],
"hooks/relations/hacluster/__init__.py": [
"interface:hacluster",
"static",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
],
"hooks/relations/hacluster/copyright": [
"interface:hacluster",
"static",
"7a296596102da98cecee289a195e00d6af44241911321699b3d4d4af93f11893"
],
"hooks/relations/hacluster/interface.yaml": [
"interface:hacluster",
"static",
"5f4e6c8d7b2884bdceeee422821f4db7163dbfa7994d86cb405ffef2c3dea43c"
],
"hooks/relations/hacluster/interface_hacluster/__init__.py": [
"interface:hacluster",
"static",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
],
"hooks/relations/hacluster/interface_hacluster/common.py": [
"interface:hacluster",
"static",
"07dace0447e9c0c62a4829f0fa02f364fe8e9ff59c4489eb39fd9602ae1f32cb"
],
"hooks/relations/hacluster/requires.py": [
"interface:hacluster",
"static",
"68cf3ed22af30e42f34fc70ca484e8e4eeaedac6410bd3f228677cc791e6f46c"
],
"hooks/relations/hacluster/test-requirements.txt": [
"interface:hacluster",
"static",
"63756e4b1c67bc161cee0d30d460dbb83911b2c064dc1c55454a30c1ab877616"
],
"hooks/relations/http/.gitignore": [
"interface:http",
"static",
"83b4ca18cc39800b1d260b5633cd0252e21501b21e7c33e718db44f1a68a09b8"
],
"hooks/relations/http/README.md": [
"interface:http",
"static",
"9c95320ad040745374fc03e972077f52c27e07eb0386ec93ae19bd50dca24c0d"
],
"hooks/relations/http/__init__.py": [
"interface:http",
"static",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
],
"hooks/relations/http/interface.yaml": [
"interface:http",
"static",
"d0b64038b85b7791ee4f3a42d73ffc8c208f206f73f899cbf33a519d12f9ad13"
],
"hooks/relations/http/provides.py": [
"interface:http",
"static",
"8c72cd8a5a6ea24f53b6dba11f4353c75265bfa7d3ecc2dd096c8963eab8c877"
],
"hooks/relations/http/requires.py": [
"interface:http",
"static",
"76cc886368eaf9c2403a6dc46b40531c3f4eaf67b08829f890c57cb645430abd"
],
"hooks/relations/nrpe-external-master/README.md": [
"interface:nrpe-external-master",
"static",
"d8ed3bc7334f6581b12b6091923f58e6f5ef62075a095a4e78fb8f434a948636"
],
"hooks/relations/nrpe-external-master/__init__.py": [
"interface:nrpe-external-master",
"static",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
],
"hooks/relations/nrpe-external-master/interface.yaml": [
"interface:nrpe-external-master",
"static",
"894f24ba56148044dae5b7febf874b427d199239bcbe1f2f55c3db06bb77b5f0"
],
"hooks/relations/nrpe-external-master/provides.py": [
"interface:nrpe-external-master",
"static",
"54e5400de99c051ecf6453776ad416b1cb8c6b73b34cbe2f41b617a8ed7b9daa"
],
"hooks/relations/public-address/README.md": [
"interface:public-address",
"static",
"7225effe61bfd8571447b8b685a2ecb52be17431b3066a5306330954c4cb064d"
],
"hooks/relations/public-address/__init__.py": [
"interface:public-address",
"static",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
],
"hooks/relations/public-address/interface.yaml": [
"interface:public-address",
"static",
"49d6777a54aa84c7d3be8d531be237564e90f2e4cb2be05ef5617a372a382340"
],
"hooks/relations/public-address/provides.py": [
"interface:public-address",
"static",
"7c99b0fe987d38773ed3e67c0378fdb78748c04d6895489cd4bca40aaeb051b2"
],
"hooks/relations/public-address/requires.py": [
"interface:public-address",
"static",
"d6a7c6c0762d29a5db19afb4cf82af50812988d5e19a3a48fcbe8b0f6fec12a5"
],
"hooks/relations/tls-certificates/.gitignore": [
"interface:tls-certificates",
"static",
"5231045ee0941ac40f03b79792c2c573014b0f57bce723dd79299221240eb4b3"
],
"hooks/relations/tls-certificates/README.md": [
"interface:tls-certificates",
"static",
"6851227de8fcca7edfd504159dbe3e3af31080af64df46f3d3b345da7630827a"
],
"hooks/relations/tls-certificates/__init__.py": [
"interface:tls-certificates",
"static",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
],
"hooks/relations/tls-certificates/docs/common.md": [
"interface:tls-certificates",
"static",
"5e91d6637fc0ccc50af2776de9e59a0f8098244b627816b2e18fabb266e980ff"
],
"hooks/relations/tls-certificates/docs/provides.md": [
"interface:tls-certificates",
"static",
"5c12dfca99b5c15ba10b4e7f7cff4cb4c9b621b198deba5f2397d3c837d035fe"
],
"hooks/relations/tls-certificates/docs/requires.md": [
"interface:tls-certificates",
"static",
"148dd1de163d75253f0a9d3c35e108dcaacbc9bdf97e47186743e6c82a67b62e"
],
"hooks/relations/tls-certificates/interface.yaml": [
"interface:tls-certificates",
"static",
"6e4282769da2e1f586e2277274b50281bb296a15efc82eae2c8542fd6fd8e778"
],
"hooks/relations/tls-certificates/make_docs": [
"interface:tls-certificates",
"static",
"3671543bddc9d277171263310e404df3f11660429582cb27b39b7e7ec8757a37"
],
"hooks/relations/tls-certificates/provides.py": [
"interface:tls-certificates",
"static",
"be2a4b9a411c770989c529fd887070ad91649481a13f5239cfd8751f234b637c"
],
"hooks/relations/tls-certificates/pydocmd.yml": [
"interface:tls-certificates",
"static",
"48a233f60a89f87d56e9bc715e05766f5d39bbea2bc8741ed31f67b30c8cfcb8"
],
"hooks/relations/tls-certificates/requires.py": [
"interface:tls-certificates",
"static",
"442d773112079bc674d3e6be75b00323fcad7efd2f03613a1972b575dd438dba"
],
"hooks/relations/tls-certificates/tls_certificates_common.py": [
"interface:tls-certificates",
"static",
"068bd32ba69bfa514e1da386919d18b348ee678b40c372f275c9110f2cc4677c"
],
"hooks/relations/tls-certificates/tox.ini": [
"interface:tls-certificates",
"static",
"a523478f33476d331dfac7e9399b2d6c496936bdcb2ac59fd4f671e873df215f"
],
"hooks/start": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/stop": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/update-status": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/upgrade-charm": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/website-relation-broken": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/website-relation-changed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/website-relation-departed": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"hooks/website-relation-joined": [
"layer:basic",
"dynamic",
"2b693cb2a11594a80cc91235c2dc219a0a6303ae62bee8aa87eb35781f7158f7"
],
"icon.svg": [
"kubeapi-load-balancer",
"static",
"92271bf7063cc3a85a6d0fe2841250cf9bf8cd72697f3655f03ada39f8aee029"
],
"layer.yaml": [
"kubeapi-load-balancer",
"dynamic",
"d7bac049bb8874aaab83bbe0339f1c1a4e726f27e548fa9705a0c890db70d5b2"
],
"lib/.gitkeep": [
"layer:nginx",
"static",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
],
"lib/charms/apt.py": [
"layer:apt",
"static",
"c7613992eb33ac94d83fbf02f467b614ea5112eaf561c4715def90989cefa531"
],
"lib/charms/layer/__init__.py": [
"layer:basic",
"static",
"dfe0d26c6bf409767de6e2546bc648f150e1b396243619bad3aa0553ab7e0e6f"
],
"lib/charms/layer/basic.py": [
"layer:basic",
"static",
"d75a0ed4c174a7c4d0e38e4d74eac7abf79ce01331490b88251c72575f5954f8"
],
"lib/charms/layer/execd.py": [
"layer:basic",
"static",
"fda8bd491032db1db8ddaf4e99e7cc878c6fb5432efe1f91cadb5b34765d076d"
],
"lib/charms/layer/hacluster.py": [
"layer:hacluster",
"static",
"f58e0c1503187247f858ff3c9a1166d59107afd1557ba89e4878ec2e79304f8a"
],
"lib/charms/layer/kubernetes_common.py": [
"layer:kubernetes-common",
"static",
"f81163072f8d338da8b72e2362471ef6cc385330b0f3728bf5abff9f94bdd777"
],
"lib/charms/layer/nagios.py": [
"layer:nagios",
"static",
"0246710bdbea844356007a64409907d93e6e94a289d83266e8b7c5d921fb3a6c"
],
"lib/charms/layer/nginx.py": [
"layer:nginx",
"static",
"5fea9e756b8e9ad09d0256d9f2a1e8e2169a97741af256653ca85b4412e40174"
],
"lib/charms/layer/options.py": [
"layer:options",
"static",
"8ae7a07d22542fc964f2d2bee8219d1c78a68dace70a1b38d36d4aea47b1c3b2"
],
"lib/charms/layer/status.py": [
"layer:status",
"static",
"d560a5e07b2e5f2b0f25f30e1f0278b06f3f90c01e4dbad5c83d71efc79018c6"
],
"lib/charms/layer/tls_client.py": [
"layer:tls-client",
"static",
"34531c3980777b661b913d77c432fc371ed10425473c2eb365b1dd5540c2ec6e"
],
"lib/charms/leadership.py": [
"layer:leadership",
"static",
"20ffcbbc08147506759726ad51567420659ffb8a2e0121079240b8706658e332"
],
"lib/debug_script.py": [
"layer:debug",
"static",
"a4d56f2d3e712b1b5cadb657c7195c6268d0aac6d228991049fd769e0ddaf453"
],
"lib/nginxlib.py": [
"layer:nginx",
"static",
"bae474acba0fbf9da21f1372dcda1dba848757c5e7cebb6fb22c29f04a67c0aa"
],
"make_docs": [
"layer:status",
"static",
"c990f55c8e879793a62ed8464ee3d7e0d7d2225fdecaf17af24b0df0e2daa8c1"
],
"manifest.yaml": [
"kubeapi-load-balancer",
"static",
"292bbdf1e54d500ac298fcd578929d9c61fa98c59a65af314cfef20e7f9bbd66"
],
"metadata.yaml": [
"kubeapi-load-balancer",
"dynamic",
"42cd67e872b9788492467064508be6954f4917c4694c621eff624b3daf58d498"
],
"metrics.yaml": [
"kubeapi-load-balancer",
"static",
"94a5eb0b0966f8ba434d91ff1e9b99b1b4c3b3044657b236d4e742d3e0d57c47"
],
"pydocmd.yml": [
"layer:status",
"static",
"11d9293901f32f75f4256ae4ac2073b92ce1d7ef7b6c892ba9fbb98690a0b330"
],
"pyproject.toml": [
"layer:apt",
"static",
"19689509a5fb9bfc90ed1e873122ac0a90f22533b7f40055c38fdd587fe297de"
],
"reactive/__init__.py": [
"layer:leadership",
"static",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
],
"reactive/apt.py": [
"layer:apt",
"static",
"6fe40f18eb84a910a71a4acb7ec74856128de846de6029b4fc297a875692c837"
],
"reactive/hacluster.py": [
"layer:hacluster",
"static",
"7b56e9efc95ace190694e439eff210f0981811f89dc46a026a400e114f3f833d"
],
"reactive/leadership.py": [
"layer:leadership",
"static",
"e2b233cf861adc3b2d9e9c062134ce2f104953f03283cdddd88f49efee652e8f"
],
"reactive/load_balancer.py": [
"kubeapi-load-balancer",
"static",
"bca19a310482a2ebb5b5887341998b116a6fb6c63506bea25fcd3eabd3bc1574"
],
"reactive/nginx.py": [
"layer:nginx",
"static",
"046769111b72a5a5aa7bfd6362db988361719586bee4e9b40a472f33c0cf09a8"
],
"reactive/status.py": [
"layer:status",
"static",
"30207fc206f24e91def5252f1c7f7c8e23c0aed0e93076babf5e03c05296d207"
],
"reactive/tls_client.py": [
"layer:tls-client",
"static",
"1bde8287715bc0b633587c6fc0b1d169f6764343c19745e2de1769fffd0f7972"
],
"requirements.txt": [
"layer:basic",
"static",
"a00f75d80849e5b4fc5ad2e7536f947c25b1a4044b341caa8ee87a92d3a4c804"
],
"templates/.gitkeep": [
"layer:nginx",
"static",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
],
"templates/apilb.conf": [
"kubeapi-load-balancer",
"static",
"2f84c6592f300bba3e197d22c0b43c24320ca5510e6d53cc8d5750d0061e1de6"
],
"templates/cdk.auth-webhook-secret.yaml": [
"layer:kubernetes-common",
"static",
"efaf34c12a5c961fa7843199070945ba05717b3656a0f3acc3327f45334bcaec"
],
"templates/service-always-restart.systemd-229.conf": [
"layer:kubernetes-common",
"static",
"516958fbf8b9a05cc86f6700d0de7bdc6b2ba1847d69fbe1214e23b52e00b064"
],
"templates/service-always-restart.systemd-latest.conf": [
"layer:kubernetes-common",
"static",
"37de98817682363d48b3dd2b635f5cfb281533aaa9d3836d1af44f9d6a59984c"
],
"templates/vhost.conf.ex": [
"layer:nginx",
"static",
"f68c366c35a8487acb78da6f1086eeee33a3eccdbe5a524509039c0c41ad5d5a"
],
"tests/data/charm.yaml": [
"kubeapi-load-balancer",
"static",
"c20a3e6b0422cf2607ecbfcbf747e876e86abaa16381cbca3c987c4f65611bd4"
],
"tests/data/ip_addr_json": [
"layer:kubernetes-common",
"static",
"f129576a9e2c7738aca8669c642f123534eda63121ae450cec4cbda787b1eb06"
],
"tests/functional/conftest.py": [
"layer:kubernetes-common",
"static",
"fd53e0c38b4dda0c18096167889cd0d85b98b0a13225f9f8853261241e94078c"
],
"tests/functional/test_k8s_common.py": [
"layer:kubernetes-common",
"static",
"680a53724154771dd78422bbaf24b151788d86dd07960712c5d9e0d758499b50"
],
"tests/integration/test_kubeapi-load-balancer_integration.py": [
"kubeapi-load-balancer",
"static",
"ad9da65fe5b129dbf24adf0c2892f2405add076357273a8473602eb020540914"
],
"tests/unit/conftest.py": [
"kubeapi-load-balancer",
"static",
"b38bf2bc23b57be1345143e07e361e41f20cff55848e7ba3ce86400d61e16081"
],
"tests/unit/test_k8s_common.py": [
"layer:kubernetes-common",
"static",
"1c18cd3d4be58092f9c1e119c2a9c05ee85a37e1c2cad834eb0049842b954885"
],
"tests/unit/test_kubeapi_load_balancer.py": [
"kubeapi-load-balancer",
"static",
"8c31c2541800259eab3461d0295ed0c76d763596b2a99a5ecdd683d65402517f"
],
"tests/validate-wheelhouse.sh": [
"kubeapi-load-balancer",
"static",
"1c74bea041866cf4bd75763190d3c512e1d63a19b04e35178a64b8c517bb3231"
],
"tox.ini": [
"kubeapi-load-balancer",
"static",
"e88a6fa74f7e2551690b7e9e0794859329d34cfb11eb4bb4b574b70843580008"
],
"version": [
"kubeapi-load-balancer",
"dynamic",
"e67e3e0910d28b48e4b9fb8ff0f27994997b58f3151331ab9a1e00588b4e2c73"
],
"wheelhouse.txt": [
"kubeapi-load-balancer",
"dynamic",
"b4fb9735f80c8b9cf90ce276670757647f0293705cf17eb23c66b6590d6d0937"
],
"wheelhouse/Cython-0.29.35.tar.gz": [
"layer:basic",
"dynamic",
"6e381fa0bf08b3c26ec2f616b19ae852c06f5750f4290118bf986b6f85c8c527"
],
"wheelhouse/Jinja2-3.0.3.tar.gz": [
"layer:basic",
"dynamic",
"611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"
],
"wheelhouse/MarkupSafe-2.0.1.tar.gz": [
"layer:basic",
"dynamic",
"594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"
],
"wheelhouse/PyYAML-5.3.1.tar.gz": [
"layer:basic",
"dynamic",
"b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"
],
"wheelhouse/cached-property-1.5.2.tar.gz": [
"__pip__",
"dynamic",
"9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"
],
"wheelhouse/charmhelpers-1.2.1.tar.gz": [
"layer:basic",
"dynamic",
"298bb9e90d9392e2b66d10a5199b1b2d459dc8d5434b897913325904989dd2d7"
],
"wheelhouse/charms.reactive-1.5.2.tar.gz": [
"layer:basic",
"dynamic",
"4cb67e15402b95e766877666f985d157b7e917dc6170ec6d922d79928aefa6b8"
],
"wheelhouse/loadbalancer_interface-1.2.0.tar.gz": [
"kubeapi-load-balancer",
"dynamic",
"f2b31a5bf25b0435eee696685af78082c8a93fbe85336755bea5b17392a584bd"
],
"wheelhouse/marshmallow-3.14.1.tar.gz": [
"__pip__",
"dynamic",
"4c05c1684e0e97fe779c62b91878f173b937fe097b356cd82f793464f5bc6138"
],
"wheelhouse/marshmallow-enum-1.5.1.tar.gz": [
"__pip__",
"dynamic",
"38e697e11f45a8e64b4a1e664000897c659b60aa57bfa18d44e226a9920b6e58"
],
"wheelhouse/netaddr-0.7.19.tar.gz": [
"layer:basic",
"dynamic",
"38aeec7cdd035081d3a4c306394b19d677623bf76fa0913f6695127c7753aefd"
],
"wheelhouse/ops-1.5.5.tar.gz": [
"__pip__",
"dynamic",
"07210019819daf35693585619d01b620f89640f8a9b4d50061f4d84f9fcf31e5"
],
"wheelhouse/ops_reactive_interface-1.0.1.tar.gz": [
"__pip__",
"dynamic",
"9ed351c42fc187299c23125975aa3dfee9f6aaae0c9d49bce8904ac079255dba"
],
"wheelhouse/pbr-5.11.1.tar.gz": [
"__pip__",
"dynamic",
"aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3"
],
"wheelhouse/pip-18.1.tar.gz": [
"layer:basic",
"dynamic",
"c0a292bd977ef590379a3f05d7b7f65135487b67470f6281289a94e015650ea1"
],
"wheelhouse/pyaml-21.10.1.tar.gz": [
"__pip__",
"dynamic",
"c6519fee13bf06e3bb3f20cacdea8eba9140385a7c2546df5dbae4887f768383"
],
"wheelhouse/setuptools-41.6.0.zip": [
"layer:basic",
"dynamic",
"6afa61b391dcd16cb8890ec9f66cc4015a8a31a6e1c2b4e0c464514be1a3d722"
],
"wheelhouse/setuptools_scm-1.17.0.tar.gz": [
"layer:basic",
"dynamic",
"70a4cf5584e966ae92f54a764e6437af992ba42ac4bca7eb37cc5d02b98ec40a"
],
"wheelhouse/toml-0.10.2.tar.gz": [
"layer:nginx",
"dynamic",
"b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
"wheelhouse/wheel-0.33.6.tar.gz": [
"layer:basic",
"dynamic",
"10c9da68765315ed98850f8e048347c3eb06dd81822dc2ab1d4fde9dc9702646"
]
}
}

View File

@ -0,0 +1,44 @@
name: Test Suite
on: [pull_request]
jobs:
call-inclusive-naming-check:
name: Inclusive naming
uses: canonical-web-and-design/Inclusive-naming/.github/workflows/woke.yaml@main
with:
fail-on-error: "true"
validate-wheelhouse:
name: Validate Wheelhouse
uses: charmed-kubernetes/workflows/.github/workflows/validate-wheelhouse.yaml@main
lint-unit:
name: Lint Unit
uses: charmed-kubernetes/workflows/.github/workflows/lint-unit.yaml@main
integration-test:
name: Integration test with VMWare
runs-on: self-hosted
timeout-minutes: 360
needs:
- call-inclusive-naming-check
- validate-wheelhouse
- lint-unit
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Setup operator environment
uses: charmed-kubernetes/actions-operator@main
with:
provider: vsphere
credentials-yaml: ${{ secrets.CREDENTIALS_YAML }}
clouds-yaml: ${{ secrets.CLOUDS_YAML }}
juju-channel: 3.1/stable
bootstrap-constraints: "arch=amd64 cores=2 mem=4G"
bootstrap-options: "${{ secrets.JAMMY_BOOTSTRAP_OPTIONS }} --model-default datastore=vsanDatastore --model-default primary-network=VLAN_2764"
- name: Run test
run: tox -e integration -- --basetemp=/home/ubuntu/pytest

4
kubeapi-load-balancer/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.tox/
__pycache__/
*.pyc
*.charm

View File

@ -0,0 +1,2 @@
Adam Stokes <adam.stokes@ubuntu.com>
Marco Ceppi <marco@ceppi.net>

View File

@ -0,0 +1,37 @@
# 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. Its the easiest way for you to give us permission to
use your contributions.
In effect, youre 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](https://bugs.launchpad.net/charm-kubeapi-load-balancer/+filebug) 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](https://github.com/charmed-kubernetes/kubernetes-docs/blob/master/pages/k8s/charm-kubeapi-load-balancer.md)

View File

@ -0,0 +1,202 @@
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.

View File

@ -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

View File

@ -0,0 +1,15 @@
# kubeapi-load-balancer
Simple NGINX reverse proxy to lend a hand in HA kubernetes-control-plane deployments.
This charm is a component of Charmed Kubernetes. For full information,
please visit the [official Charmed Kubernetes docs](https://www.ubuntu.com/kubernetes/docs/charm-kubeapi-load-balancer).
# Developers
## Building the charm
```
make charm
```

View File

@ -0,0 +1,2 @@
"debug":
"description": "Collect debug data"

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -0,0 +1,91 @@
"options":
"nagios_context":
"default": "juju"
"type": "string"
"description": |
Used by the nrpe subordinate charms.
A string that will be prepended to instance name to set the host name
in nagios. So for instance the hostname would be something like:
juju-myservice-0
If you're running multiple environments with the same services in them
this allows you to differentiate between them.
"nagios_servicegroups":
"default": ""
"type": "string"
"description": |
A comma-separated list of nagios servicegroups.
If left empty, the nagios_context will be used as the servicegroup
"extra_packages":
"description": >
Space separated list of extra deb packages to install.
"type": "string"
"default": ""
"package_status":
"default": "install"
"type": "string"
"description": >
The status of service-affecting packages will be set to this
value in the dpkg database. Valid values are "install" and "hold".
"install_sources":
"description": >
List of extra apt sources, per charm-helpers standard
format (a yaml list of strings encoded as a string). Each source
may be either a line that can be added directly to
sources.list(5), or in the form ppa:<user>/<ppa-name> for adding
Personal Package Archives, or a distribution component to enable.
"type": "string"
"default": ""
"install_keys":
"description": >
List of signing keys for install_sources package sources, per
charmhelpers standard format (a yaml list of strings encoded as
a string). The keys should be the full ASCII armoured GPG public
keys. While GPG key ids are also supported and looked up on a
keyserver, operators should be aware that this mechanism is
insecure. null can be used if a standard package signing key is
used that will already be installed on the machine, and for PPA
sources where the package signing key is securely retrieved from
Launchpad.
"type": "string"
"default": ""
"port":
"type": "int"
"default": !!int "443"
"description": |-
The port to run the loadbalancer
"host":
"type": "string"
"default": "127.0.0.1"
"description": "listen address"
"ha-cluster-vip":
"type": "string"
"description": |
Virtual IP for the charm to use with the HA Cluster subordinate charm
Mutually exclusive with ha-cluster-dns. Multiple virtual IPs are
separated by spaces.
"default": ""
"ha-cluster-dns":
"type": "string"
"description": |
DNS entry to use with the HA Cluster subordinate charm.
Mutually exclusive with ha-cluster-vip.
"default": ""
"extra_sans":
"type": "string"
"default": ""
"description": |
Space-separated list of extra SAN entries to add to the x509 certificate
created for the load balancers.
"proxy_read_timeout":
"type": "int"
"default": !!int "600"
"description": "Timeout in seconds for reading a response from proxy server."
"loadbalancer-ips":
"type": "string"
"description": |
Space seperated list of IP addresses of loadbalancers in front of control plane.
A common case for this is virtual IP addresses that are floated in front of the
kubeapi-load-balancer charm. The workers will alternate IP addresses from this
list to distribute load. If you have 2 IPs and 4 workers, each IP will be used
by 2 workers.
"default": ""

View File

@ -0,0 +1,13 @@
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,15 @@
Copyright 2015-2016 Canonical Ltd.
This file is part of the Apt layer for Juju.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranties of
MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

View File

@ -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.

View File

@ -0,0 +1,15 @@
Copyright 2015-2016 Canonical Ltd.
This file is part of the Leadership Layer for Juju.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranties of
MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

View File

@ -0,0 +1,13 @@
Copyright 2016 Canonical Ltd
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.

View File

@ -0,0 +1,16 @@
Format: http://dep.debian.net/deps/dep5/
Files: *
Copyright: Copyright 2016, Canonical Ltd.
License: GPL-3
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranties of
MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Adam Stokes <adam.stokes@ubuntu.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -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.

View File

@ -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.

View File

@ -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")

View File

@ -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

View File

@ -0,0 +1,4 @@
#!/bin/sh
set -ux
cp -v /var/log/juju/* $DEBUG_SCRIPT_DIR

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,4 @@
#!/bin/sh
set -ux
sysctl -a > $DEBUG_SCRIPT_DIR/sysctl

View File

@ -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

View File

@ -0,0 +1,21 @@
#!/usr/local/sbin/charm-env python3
import os
import shutil
import traceback
import debug_script
from charms import layer
options = layer.options.get('tls-client')
def copy_cert(source_key, name):
try:
source = options[source_key]
dest = os.path.join(debug_script.dir, name)
shutil.copy(source, dest)
except Exception:
traceback.print_exc()
copy_cert('client_certificate_path', 'client.crt')
copy_cert('server_certificate_path', 'server.crt')
copy_cert('ca_certificate_path', 'ca.crt')

View File

@ -0,0 +1,91 @@
<h1 id="charms.layer.status.WorkloadState">WorkloadState</h1>
```python
WorkloadState(self, /, *args, **kwargs)
```
Enum of the valid workload states.
Valid options are:
* `WorkloadState.MAINTENANCE`
* `WorkloadState.BLOCKED`
* `WorkloadState.WAITING`
* `WorkloadState.ACTIVE`
<h1 id="charms.layer.status.maintenance">maintenance</h1>
```python
maintenance(message)
```
Set the status to the `MAINTENANCE` state with the given operator message.
__Parameters__
- __`message` (str)__: Message to convey to the operator.
<h1 id="charms.layer.status.maint">maint</h1>
```python
maint(message)
```
Shorthand alias for
[maintenance](status.md#charms.layer.status.maintenance).
__Parameters__
- __`message` (str)__: Message to convey to the operator.
<h1 id="charms.layer.status.blocked">blocked</h1>
```python
blocked(message)
```
Set the status to the `BLOCKED` state with the given operator message.
__Parameters__
- __`message` (str)__: Message to convey to the operator.
<h1 id="charms.layer.status.waiting">waiting</h1>
```python
waiting(message)
```
Set the status to the `WAITING` state with the given operator message.
__Parameters__
- __`message` (str)__: Message to convey to the operator.
<h1 id="charms.layer.status.active">active</h1>
```python
active(message)
```
Set the status to the `ACTIVE` state with the given operator message.
__Parameters__
- __`message` (str)__: Message to convey to the operator.
<h1 id="charms.layer.status.status_set">status_set</h1>
```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.

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -0,0 +1,46 @@
#!/usr/bin/env python3
# Load modules from $CHARM_DIR/lib
import sys
sys.path.append('lib')
import yaml
import os
from subprocess import check_output, check_call, CalledProcessError
def build_command(doc):
values = {}
metrics = doc.get("metrics", {})
for metric, mdoc in metrics.items():
if not mdoc:
continue
cmd = mdoc.get("command")
if cmd:
try:
value = check_output(cmd, shell=True, universal_newlines=True)
except CalledProcessError as e:
check_call(['juju-log', '-lERROR',
'Error collecting metric {}:\n{}'.format(
metric, e.output)])
continue
value = value.strip()
if value:
values[metric] = value
if not values:
return None
command = ["add-metric"]
for metric, value in values.items():
command.append("%s=%s" % (metric, value))
return command
if __name__ == '__main__':
charm_dir = os.path.dirname(os.path.abspath(os.path.join(__file__, "..")))
metrics_yaml = os.path.join(charm_dir, "metrics.yaml")
with open(metrics_yaml) as f:
doc = yaml.load(f)
command = build_command(doc)
if command:
check_call(command)

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -0,0 +1,3 @@
[DEFAULT]
test_path=./unit_tests
top_dir=./

View File

@ -0,0 +1,90 @@
# Overview
This interface handles the communication with the hacluster subordinate
charm using the `ha` interface protocol.
# Usage
## Requires
The interface layer will set the following reactive states, as appropriate:
* `{relation_name}.connected` The relation is established and ready for
the local charm to configure the hacluster subordinate charm. The
configuration of the resources to manage for the hacluster charm
can be managed via one of the following methods:
* `manage_resources` method
* `bind_on` method
Configuration of the managed resources within the hacluster can be
managed by passing `common.CRM` object definitions to the
`manage_resources` method.
* `{relation_name}.available` The hacluster is up and ready.
For example:
```python
from charms.reactive import when, when_not
from charms.reactive import set_state, remove_state
from relations.hacluster.common import CRM
@when('ha.connected')
def cluster_connected(hacluster):
resources = CRM()
resources.primitive('res_vip', 'ocf:IPAddr2',
params='ip=10.0.3.100 nic=eth0',
op='monitor interval="10s"')
resources.clone('cl_res_vip', 'res_vip')
hacluster.bind_on(iface='eth0', mcastport=4430)
hacluster.manage_resources(resources)
```
Additionally, for more code clarity a custom object implements the interface
defined in common.ResourceDescriptor can be used to simplify the code for
reuse.
For example:
```python
import ipaddress
from relation.hacluster.common import CRM
from relation.hacluster.common import ResourceDescriptor
class VirtualIP(ResourceDescriptor):
def __init__(self, vip, nic='eth0'):
self.vip = vip
self.nic = 'eth0'
def configure_resource(self, crm):
ipaddr = ipaddress.ip_address(self.vip)
if isinstance(ipaddr, ipaddress.IPv4Address):
res_type = 'ocf:heartbeat:IPAddr2'
res_parms = 'ip={ip} nic={nic}'.format(ip=self.vip,
nic=self.nic)
else:
res_type = 'ocf:heartbeat:IPv6addr'
res_params = 'ipv6addr={ip} nic={nic}'.format(ip=self.vip,
nic=self.nic)
crm.primitive('res_vip', res_type, params=res_params,
op='monitor interval="10s"')
crm.clone('cl_res_vip', 'res_vip')
```
Once the VirtualIP class above has been defined in charm code, it can make
the code a bit cleaner. The example above can thusly be written as:
```python
@when('ha.connected')
def cluster_connected(hacluster):
resources = CRM()
resources.add(VirtualIP('10.0.3.100'))
hacluster.bind_on(iface='eth0', mcastport=4430)
hacluster.manage_resources(resources)
```

View File

@ -0,0 +1,21 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0
Files: *
Copyright: 2015, Canonical Ltd.
License: Apache-2.0
License: Apache-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.
.
On Debian-based systems the full text of the Apache version 2.0 license
can be found in `/usr/share/common-licenses/Apache-2.0'.

View File

@ -0,0 +1,16 @@
name: hacluster
summary: |
Provides the hacluster interface used for configuring Corosync
and Pacemaker services.
maintainer: OpenStack Charmers <openstack-charmers@lists.ubuntu.com>
ignore:
- '.gitignore'
- '.gitreview'
- '.testr.conf'
- 'test-requirements'
- 'tox.ini'
- 'unit_tests'
- '.zuul.yaml'
- 'setup.cfg'
- 'setup.py'
- '**/ops_ha_interface.py'

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
#!/usr/bin/python
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import relations.hacluster.interface_hacluster.common as common
from charms.reactive import hook
from charms.reactive import RelationBase
from charms.reactive import scopes
from charms.reactive.helpers import data_changed as rh_data_changed
from charmhelpers.core import hookenv
class HAClusterRequires(RelationBase, common.ResourceManagement):
# The hacluster charm is a subordinate charm and really only works
# for a single service to the HA Cluster relation, therefore set the
# expected scope to be GLOBAL.
scope = scopes.GLOBAL
@hook('{requires:hacluster}-relation-joined')
def joined(self):
self.set_state('{relation_name}.connected')
@hook('{requires:hacluster}-relation-changed')
def changed(self):
if self.is_clustered():
self.set_state('{relation_name}.available')
else:
self.remove_state('{relation_name}.available')
@hook('{requires:hacluster}-relation-{broken,departed}')
def departed(self):
self.remove_state('{relation_name}.available')
self.remove_state('{relation_name}.connected')
def data_changed(self, data_id, data, hash_type='md5'):
return rh_data_changed(data_id, data, hash_type)
def get_remote_all(self, key, default=None):
"""Return a list of all values presented by remote units for key"""
values = []
for conversation in self.conversations():
for relation_id in conversation.relation_ids:
for unit in hookenv.related_units(relation_id):
value = hookenv.relation_get(key,
unit,
relation_id) or default
if value:
values.append(value)
return list(set(values))

View File

@ -0,0 +1,7 @@
# Lint and unit test requirements
flake8
stestr>=2.2.0
charms.reactive
coverage>=3.6
netifaces
git+https://github.com/canonical/operator.git#egg=ops

View File

@ -0,0 +1,5 @@
# Emacs save files
*~
\#*\#
.\#*

View File

@ -0,0 +1,68 @@
# Overview
This interface layer implements the basic form of the `http` interface protocol,
which is used for things such as reverse-proxies, load-balanced servers, REST
service discovery, et cetera.
# Usage
## Provides
By providing the `http` interface, your charm is providing an HTTP server that
can be load-balanced, reverse-proxied, used as a REST endpoint, etc.
Your charm need only provide the port on which it is serving its content, as
soon as the `{relation_name}.available` state is set:
```python
@when('website.available')
def configure_website(website):
website.configure(port=hookenv.config('port'))
```
## Requires
By requiring the `http` interface, your charm is consuming one or more HTTP
servers, as a REST endpoint, to load-balance a set of servers, etc.
Your charm should respond to the `{relation_name}.available` state, which
indicates that there is at least one HTTP server connected.
The `services()` method returns a list of available HTTP services and their
associated hosts and ports.
The return value is a list of dicts of the following form:
```python
[
{
'service_name': name_of_service,
'hosts': [
{
'hostname': address_of_host,
'port': port_for_host,
},
# ...
],
},
# ...
]
```
A trivial example of handling this interface would be:
```python
from charms.reactive.helpers import data_changed
@when('reverseproxy.available')
def update_reverse_proxy_config(reverseproxy):
services = reverseproxy.services()
if not data_changed('reverseproxy.services', services):
return
for service in services:
for host in service['hosts']:
hookenv.log('{} has a unit {}:{}'.format(
services['service_name'],
host['hostname'],
host['port']))
```

View File

@ -0,0 +1,4 @@
name: http
summary: Basic HTTP interface
version: 1
repo: https://git.launchpad.net/~bcsaller/charms/+source/http

View File

@ -0,0 +1,67 @@
import json
from charmhelpers.core import hookenv
from charms.reactive import when, when_not
from charms.reactive import set_flag, clear_flag
from charms.reactive import Endpoint
class HttpProvides(Endpoint):
@when('endpoint.{endpoint_name}.joined')
def joined(self):
set_flag(self.expand_name('{endpoint_name}.available'))
@when_not('endpoint.{endpoint_name}.joined')
def broken(self):
clear_flag(self.expand_name('{endpoint_name}.available'))
def get_ingress_address(self, rel_id=None):
# If no rel_id is provided, we fallback to the first one
if rel_id is None:
rel_id = self.relations[0].relation_id
return hookenv.ingress_address(rel_id, hookenv.local_unit())
def configure(self, port, private_address=None, hostname=None):
''' configure the address(es). private_address and hostname can
be None, a single string address/hostname, or a list of addresses
and hostnames. Note that if a list is passed, it is assumed both
private_address and hostname are either lists or None '''
for relation in self.relations:
ingress_address = self.get_ingress_address(relation.relation_id)
if type(private_address) is list or type(hostname) is list:
# build 3 lists to zip together that are the same length
length = max(len(private_address), len(hostname))
p = [port] * length
a = private_address + [ingress_address] *\
(length - len(private_address))
h = hostname + [ingress_address] * (length - len(hostname))
zipped_list = zip(p, a, h)
# now build an array of dictionaries from that in the desired
# format for the interface
data_list = [{'hostname': h, 'port': p, 'private-address': a}
for p, a, h in zipped_list]
# for backwards compatibility, we just send a single entry
# and have an array of dictionaries in a field of that
# entry for the other entries.
data = data_list.pop(0)
data['extended_data'] = json.dumps(data_list)
relation.to_publish_raw.update(data)
else:
relation.to_publish_raw.update({
'hostname': hostname or ingress_address,
'private-address': private_address or ingress_address,
'port': port,
})
def set_remote(self, **kwargs):
# NB: This method provides backwards compatibility for charms that
# called RelationBase.set_remote. Most commonly, this was done by
# charms that needed to pass reverse proxy stanzas to http proxies.
# This type of interaction with base relation classes is discouraged,
# and should be handled with logic encapsulated in appropriate
# interfaces. Eventually, this method will be deprecated in favor of
# that behavior.
for relation in self.relations:
relation.to_publish_raw.update(kwargs)

View File

@ -0,0 +1,76 @@
import json
from charms.reactive import when, when_not
from charms.reactive import set_flag, clear_flag
from charms.reactive import Endpoint
class HttpRequires(Endpoint):
@when('endpoint.{endpoint_name}.changed')
def changed(self):
if any(unit.received_raw['port'] for unit in self.all_joined_units):
set_flag(self.expand_name('{endpoint_name}.available'))
@when_not('endpoint.{endpoint_name}.joined')
def broken(self):
clear_flag(self.expand_name('{endpoint_name}.available'))
def services(self):
"""
Returns a list of available HTTP services and their associated hosts
and ports.
The return value is a list of dicts of the following form::
[
{
'service_name': name_of_service,
'hosts': [
{
'hostname': address_of_host,
'private-address': private_address_of_host,
'port': port_for_host,
},
# ...
],
},
# ...
]
"""
def build_service_host(data):
private_address = data['private-address']
host = data['hostname'] or private_address
if host and data['port']:
return (host, private_address, data['port'])
else:
return None
services = {}
for relation in self.relations:
service_name = relation.application_name
service = services.setdefault(service_name, {
'service_name': service_name,
'hosts': [],
})
host_set = set()
for unit in relation.joined_units:
data = unit.received_raw
host = build_service_host(data)
if host:
host_set.add(host)
# if we have extended data, add it
if 'extended_data' in data:
for ed in json.loads(data['extended_data']):
host = build_service_host(ed)
if host:
host_set.add(host)
service['hosts'] = [
{'hostname': h, 'private-address': pa, 'port': p}
for h, pa, p in sorted(host_set)
]
ret = [s for s in services.values() if s['hosts']]
return ret

View File

@ -0,0 +1,66 @@
# nrpe-external-master interface
Use this interface to register nagios checks in your charm layers.
## Purpose
This interface is designed to interoperate with the
[nrpe-external-master](https://jujucharms.com/nrpe-external-master) subordinate charm.
## How to use in your layers
The event handler for `nrpe-external-master.available` is called with an object
through which you can register your own custom nagios checks, when a relation
is established with `nrpe-external-master:nrpe-external-master`.
This object provides a method,
_add_check_(args, name=_check_name_, description=_description_, context=_context_, unit=_unit_)
which is called to register a nagios plugin check for your service.
All arguments are required.
*args* is a list of nagios plugin command line arguments, starting with the path to the plugin executable.
*name* is the name of the check registered in nagios
*description* is some text that describes what the check is for and what it does
*context* is the nagios context name, something that identifies your application
*unit* is `hookenv.local_unit()`
The nrpe subordinate installs `check_http`, so you can use it like this:
```
@when('nrpe-external-master.available')
def setup_nagios(nagios):
config = hookenv.config()
unit_name = hookenv.local_unit()
nagios.add_check(['/usr/lib/nagios/plugins/check_http',
'-I', '127.0.0.1', '-p', str(config['port']),
'-e', " 200 OK", '-u', '/publickey'],
name="check_http",
description="Verify my awesome service is responding",
context=config["nagios_context"],
unit=unit_name,
)
```
If your `nagios.add_check` defines a custom plugin, you will also need to restart the `nagios-nrpe-server` service.
Consult the nagios documentation for more information on [how to write your own
plugins](https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/4/en/pluginapi.html)
or [find one](https://www.nagios.org/projects/nagios-plugins/) that does what you need.
## Example deployment
```
$ juju deploy your-awesome-charm
$ juju deploy nrpe-external-master --config site-nagios.yaml
$ juju add-relation your-awesome-charm nrpe-external-master
```
where `site-nagios.yaml` has the necessary configuration settings for the
subordinate to connect to nagios.

View File

@ -0,0 +1,3 @@
name: nrpe-external-master
summary: Nagios interface
version: 1

View File

@ -0,0 +1,62 @@
import datetime
from charms.reactive import hook
from charms.reactive import RelationBase
from charms.reactive import scopes
class NrpeExternalMasterProvides(RelationBase):
scope = scopes.GLOBAL
@hook('{provides:nrpe-external-master}-relation-{joined,changed}')
def changed_nrpe(self):
self.set_state('{relation_name}.available')
@hook('{provides:nrpe-external-master}-relation-{broken,departed}')
def broken_nrpe(self):
self.remove_state('{relation_name}.available')
def add_check(self, args, name=None, description=None, context=None,
servicegroups=None, unit=None):
unit = unit.replace('/', '-')
check_tmpl = """
#---------------------------------------------------
# This file is Juju managed
#---------------------------------------------------
command[%(check_name)s]=%(check_args)s
"""
service_tmpl = """
#---------------------------------------------------
# This file is Juju managed
#---------------------------------------------------
define service {
use active-service
host_name %(context)s-%(unit_name)s
service_description %(description)s
check_command check_nrpe!%(check_name)s
servicegroups %(servicegroups)s
}
"""
check_filename = "/etc/nagios/nrpe.d/check_%s.cfg" % (name)
with open(check_filename, "w") as fh:
fh.write(check_tmpl % {
'check_args': ' '.join(args),
'check_name': name,
})
service_filename = "/var/lib/nagios/export/service__%s_%s.cfg" % (
unit, name)
with open(service_filename, "w") as fh:
fh.write(service_tmpl % {
'servicegroups': servicegroups or context,
'context': context,
'description': description,
'check_name': name,
'unit_name': unit,
})
def updated(self):
relation_info = {
'timestamp': datetime.datetime.now().isoformat(),
}
self.set_remote(**relation_info)
self.remove_state('{relation_name}.available')

View File

@ -0,0 +1,59 @@
# Overview
This interface layer implements a public address protocol useful for load
balancers and their subordinates. The load balancers (providers) set their
own public address and port, which is then available to the subordinates
(requirers).
# Usage
## Provides
By providing the `public-address` interface, your charm is providing an HTTP
server that can load-balance for another HTTP based service.
Your charm need only provide the address and port on which it is serving its
content, as soon as the `{relation_name}.available` state is set:
```python
from charmhelpers.core import hookenv
@when('website.available')
def configure_website(website):
website.set_address_port(hookenv.unit_get('public-address'), hookenv.config('port'))
```
## Requires
By requiring the `public-address` interface, your charm is consuming one or
more HTTP servers, to load-balance a set of servers, etc.
Your charm should respond to the `{relation_name}.available` state, which
indicates that there is at least one HTTP server connected.
The `get_addresses_ports()` method returns a list of available addresses and
ports.
The return value is a list of dicts of the following form:
```python
[
{
'public-address': address_of_host,
'port': port_for_host,
},
# ...
]
```
A trivial example of handling this interface would be:
```python
from charmhelpers.core import hookenv
@when('loadbalancer.available')
def update_reverse_proxy_config(loadbalancer):
hosts = loadbalancer.get_addresses_ports()
for host in hosts:
hookenv.log('The loadbalancer for this unit is {}:{}'.format(
host['public-address'],
host['port']))
```

View File

@ -0,0 +1,4 @@
name: public-address
summary: A basic interface to provide the public address for load balancers.
version: 1
repo: https://githb.com/juju-solutions/interface-public-address.git

View File

@ -0,0 +1,60 @@
import json
from charms.reactive import toggle_flag
from charms.reactive import Endpoint
class PublicAdddressProvides(Endpoint):
def manage_flags(self):
toggle_flag(self.expand_name('{endpoint_name}.available'),
self.is_joined)
def set_address_port(self, address, port, relation=None):
if relation is None:
# no relation specified, so send the same data to everyone
relations = self.relations
else:
# specific relation given, so only send the data to that one
relations = [relation]
if type(address) is list:
# build 2 lists to zip together that are the same length
length = len(address)
p = [port] * length
combined = zip(address, p)
clients = [{'public-address': a, 'port': p}
for a, p in combined]
# for backwards compatibility, we just send a single entry
# and have an array of dictionaries in a field of that
# entry for the other entries.
first = clients.pop(0)
first['extended_data'] = json.dumps(clients)
for relation in relations:
relation.to_publish_raw.update(first)
else:
for relation in relations:
relation.to_publish_raw.update({'public-address': address,
'port': port})
@property
def requests(self):
return [Request(rel) for rel in self.relations]
class Request:
def __init__(self, rel):
self.rel = rel
@property
def application_name(self):
return self.rel.application_name
@property
def members(self):
return [(u.received_raw.get('ingress-address',
u.received_raw['private-address']),
u.received_raw.get('port', '6443'))
for u in self.rel.joined_units]
def set_address_port(self, address, port):
self.rel.endpoint.set_address_port(address, port, self.rel)

View File

@ -0,0 +1,44 @@
import json
from charms.reactive import toggle_flag, Endpoint
class PublicAddressRequires(Endpoint):
def manage_flags(self):
toggle_flag(self.expand_name('{endpoint_name}.available'),
len(self.get_addresses_ports()) > 0)
def set_backend_port(self, port):
"""
Set the port that the backend service is listening on.
Defaults to 6443 if not set.
"""
for rel in self.relations:
rel.to_publish_raw['port'] = str(port)
def get_addresses_ports(self):
'''Returns a list of available HTTP providers and their associated
public addresses and ports.
The return value is a list of dicts of the following form::
[
{
'public-address': address_for_frontend,
'port': port_for_frontend,
},
# ...
]
'''
hosts = set()
for relation in self.relations:
for unit in relation.joined_units:
data = unit.received_raw
hosts.add((data['public-address'], data['port']))
if 'extended_data' in data:
for ed in json.loads(data['extended_data']):
hosts.add((ed['public-address'], ed['port']))
return [{'public-address': pa, 'port': p}
for pa, p in sorted(host for host in hosts
if None not in host)]

View File

@ -0,0 +1,6 @@
.coverage
**/.tox
**/__pycache__
*.egg-info/
*.pyc
_build

View File

@ -0,0 +1,90 @@
# Interface tls-certificates
This is a [Juju][] interface layer that enables a charm which requires TLS
certificates to relate to a charm which can provide them, such as [Vault][] or
[EasyRSA][]
To get started please read the [Introduction to PKI][] which defines some PKI
terms, concepts and processes used in this document.
# Example Usage
Let's say you have a charm which needs a server certificate for a service it
provides to other charms and a client certificate for a database it consumes
from another charm. The charm provides its own service on the `clients`
relation endpoint, and it consumes the database on the `db` relation endpoint.
First, you must define the relation endpoint in your charm's `metadata.yaml`:
```yaml
requires:
cert-provider:
interface: tls-certificates
```
Next, you must ensure the interface layer is included in your `layer.yaml`:
```yaml
includes:
- interface:tls-certificates
```
Then, in your reactive code, add the following, changing `update_certs` to
handle the certificates however your charm needs:
```python
from charmhelpers.core import hookenv, host
from charms.reactive import endpoint_from_flag
@when('cert-provider.ca.changed')
def install_root_ca_cert():
cert_provider = endpoint_from_flag('cert-provider.ca.available')
host.install_ca_cert(cert_provider.root_ca_cert)
clear_flag('cert-provider.ca.changed')
@when('cert-provider.available')
def request_certificates():
cert_provider = endpoint_from_flag('cert-provider.available')
# get ingress info
ingress_for_clients = hookenv.network_get('clients')['ingress-addresses']
ingress_for_db = hookenv.network_get('db')['ingress-addresses']
# use first ingress address as primary and any additional as SANs
server_cn, server_sans = ingress_for_clients[0], ingress_for_clients[:1]
client_cn, client_sans = ingress_for_db[0], ingress_for_db[:1]
# request a single server and single client cert; note that multiple certs
# of either type can be requested as long as they have unique common names
cert_provider.request_server_cert(server_cn, server_sans)
cert_provider.request_client_cert(client_cn, client_sans)
@when('cert-provider.certs.changed')
def update_certs():
cert_provider = endpoint_from_flag('cert-provider.available')
server_cert = cert_provider.server_certs[0] # only requested one
myserver.update_server_cert(server_cert.cert, server_cert.key)
client_cert = cert_provider.client_certs[0] # only requested one
myclient.update_client_cert(client_cert.cert, client_cert.key)
clear_flag('cert-provider.certs.changed')
```
# Reference
* [Requires](docs/requires.md)
* [Provides](docs/provides.md)
# Contact Information
Maintainer: Cory Johns &lt;Cory.Johns@canonical.com&gt;
[Juju]: https://jujucharms.com
[Vault]: https://jujucharms.com/u/openstack-charmers/vault
[EasyRSA]: https://jujucharms.com/u/containers/easyrsa
[Introduction to PKI]: https://github.com/OpenVPN/easy-rsa/blob/master/doc/Intro-To-PKI.md

View File

@ -0,0 +1,51 @@
<h1 id="tls_certificates_common.CertificateRequest">CertificateRequest</h1>
```python
CertificateRequest(self, unit, cert_type, cert_name, common_name, sans)
```
<h2 id="tls_certificates_common.CertificateRequest.application_name">application_name</h2>
Name of the application which the request came from.
:returns: Name of application
:rtype: str
<h2 id="tls_certificates_common.CertificateRequest.cert">cert</h2>
The cert published for this request, if any.
<h2 id="tls_certificates_common.CertificateRequest.cert_type">cert_type</h2>
Type of certificate, 'server' or 'client', being requested.
<h2 id="tls_certificates_common.CertificateRequest.resolve_unit_name">resolve_unit_name</h2>
```python
CertificateRequest.resolve_unit_name(unit)
```
Return name of unit associated with this request.
unit_name should be provided in the relation data to ensure
compatability with cross-model relations. If the unit name
is absent then fall back to unit_name attribute of the
unit associated with this request.
:param unit: Unit to extract name from
:type unit: charms.reactive.endpoints.RelatedUnit
:returns: Name of unit
:rtype: str
<h1 id="tls_certificates_common.Certificate">Certificate</h1>
```python
Certificate(self, cert_type, common_name, cert, key)
```
Represents a created certificate and key.
The ``cert_type``, ``common_name``, ``cert``, and ``key`` values can
be accessed either as properties or as the contents of the dict.

View File

@ -0,0 +1,212 @@
<h1 id="provides">provides</h1>
<h1 id="provides.TlsProvides">TlsProvides</h1>
```python
TlsProvides(self, endpoint_name, relation_ids=None)
```
The provider's side of the interface protocol.
The following flags may be set:
* `{endpoint_name}.available`
Whenever any clients are joined.
* `{endpoint_name}.certs.requested`
When there are new certificate requests of any kind to be processed.
The requests can be accessed via [new_requests][].
* `{endpoint_name}.server.certs.requested`
When there are new server certificate requests to be processed.
The requests can be accessed via [new_server_requests][].
* `{endpoint_name}.client.certs.requested`
When there are new client certificate requests to be processed.
The requests can be accessed via [new_client_requests][].
[Certificate]: common.md#tls_certificates_common.Certificate
[CertificateRequest]: common.md#tls_certificates_common.CertificateRequest
[all_requests]: provides.md#provides.TlsProvides.all_requests
[new_requests]: provides.md#provides.TlsProvides.new_requests
[new_server_requests]: provides.md#provides.TlsProvides.new_server_requests
[new_client_requests]: provides.md#provides.TlsProvides.new_client_requests
<h2 id="provides.TlsProvides.all_published_certs">all_published_certs</h2>
List of all [Certificate][] instances that this provider has published
for all related applications.
<h2 id="provides.TlsProvides.all_requests">all_requests</h2>
List of all requests that have been made.
Each will be an instance of [CertificateRequest][].
Example usage:
```python
@when('certs.regen',
'tls.certs.available')
def regen_all_certs():
tls = endpoint_from_flag('tls.certs.available')
for request in tls.all_requests:
cert, key = generate_cert(request.cert_type,
request.common_name,
request.sans)
request.set_cert(cert, key)
```
<h2 id="provides.TlsProvides.new_application_requests">new_application_requests</h2>
Filtered view of [new_requests][] that only includes application cert
requests.
Each will be an instance of [ApplicationCertificateRequest][].
Example usage:
```python
@when('tls.application.certs.requested')
def gen_application_certs():
tls = endpoint_from_flag('tls.application.certs.requested')
for request in tls.new_application_requests:
cert, key = generate_application_cert(request.common_name,
request.sans)
request.set_cert(cert, key)
```
<h2 id="provides.TlsProvides.new_client_requests">new_client_requests</h2>
Filtered view of [new_requests][] that only includes client cert
requests.
Each will be an instance of [CertificateRequest][].
Example usage:
```python
@when('tls.client.certs.requested')
def gen_client_certs():
tls = endpoint_from_flag('tls.client.certs.requested')
for request in tls.new_client_requests:
cert, key = generate_client_cert(request.common_name,
request.sans)
request.set_cert(cert, key)
```
<h2 id="provides.TlsProvides.new_requests">new_requests</h2>
Filtered view of [all_requests][] that only includes requests that
haven't been handled.
Each will be an instance of [CertificateRequest][].
This collection can also be further filtered by request type using
[new_server_requests][] or [new_client_requests][].
Example usage:
```python
@when('tls.certs.requested')
def gen_certs():
tls = endpoint_from_flag('tls.certs.requested')
for request in tls.new_requests:
cert, key = generate_cert(request.cert_type,
request.common_name,
request.sans)
request.set_cert(cert, key)
```
<h2 id="provides.TlsProvides.new_server_requests">new_server_requests</h2>
Filtered view of [new_requests][] that only includes server cert
requests.
Each will be an instance of [CertificateRequest][].
Example usage:
```python
@when('tls.server.certs.requested')
def gen_server_certs():
tls = endpoint_from_flag('tls.server.certs.requested')
for request in tls.new_server_requests:
cert, key = generate_server_cert(request.common_name,
request.sans)
request.set_cert(cert, key)
```
<h2 id="provides.TlsProvides.set_ca">set_ca</h2>
```python
TlsProvides.set_ca(certificate_authority)
```
Publish the CA to all related applications.
<h2 id="provides.TlsProvides.set_chain">set_chain</h2>
```python
TlsProvides.set_chain(chain)
```
Publish the chain of trust to all related applications.
<h2 id="provides.TlsProvides.set_client_cert">set_client_cert</h2>
```python
TlsProvides.set_client_cert(cert, key)
```
Deprecated. This is only for backwards compatibility.
Publish a globally shared client cert and key.
<h2 id="provides.TlsProvides.set_server_cert">set_server_cert</h2>
```python
TlsProvides.set_server_cert(scope, cert, key)
```
Deprecated. Use one of the [new_requests][] collections and
`request.set_cert()` instead.
Set the server cert and key for the request identified by `scope`.
<h2 id="provides.TlsProvides.set_server_multicerts">set_server_multicerts</h2>
```python
TlsProvides.set_server_multicerts(scope)
```
Deprecated. Done automatically.
<h2 id="provides.TlsProvides.add_server_cert">add_server_cert</h2>
```python
TlsProvides.add_server_cert(scope, cn, cert, key)
```
Deprecated. Use `request.set_cert()` instead.
<h2 id="provides.TlsProvides.get_server_requests">get_server_requests</h2>
```python
TlsProvides.get_server_requests()
```
Deprecated. Use the [new_requests][] or [server_requests][]
collections instead.
One provider can have many requests to generate server certificates.
Return a map of all server request objects indexed by a unique
identifier.

Some files were not shown because too many files have changed in this diff Show More