2 # Copyright 2020 Canonical Ltd.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
20 sys
.path
.append("lib")
22 from ops
.charm
import CharmBase
23 from ops
.framework
import StoredState
, Object
24 from ops
.main
import main
25 from ops
.model
import (
34 from pathlib
import Path
35 from string
import Template
37 logger
= logging
.getLogger(__name__
)
40 class NGUICharm(CharmBase
):
43 def __init__(self
, framework
, key
):
44 super().__init
__(framework
, key
)
45 self
.state
.set_default(spec
=None)
46 self
.state
.set_default(nbi_host
=None)
47 self
.state
.set_default(nbi_port
=None)
49 # Observe Charm related events
50 self
.framework
.observe(self
.on
.config_changed
, self
.on_config_changed
)
51 self
.framework
.observe(self
.on
.start
, self
.on_start
)
52 self
.framework
.observe(self
.on
.upgrade_charm
, self
.on_upgrade_charm
)
53 self
.framework
.observe(
54 self
.on
.nbi_relation_changed
, self
.on_nbi_relation_changed
57 # SSL Certificate path
58 self
.ssl_folder
= "/certs"
59 self
.ssl_crt_name
= "ssl_certificate.crt"
60 self
.ssl_key_name
= "ssl_certificate.key"
62 def _apply_spec(self
):
63 # Only apply the spec if this unit is a leader.
64 unit
= self
.model
.unit
65 if not unit
.is_leader():
66 unit
.status
= ActiveStatus("ready")
68 if not self
.state
.nbi_host
or not self
.state
.nbi_port
:
69 unit
.status
= WaitingStatus("Waiting for NBI")
71 unit
.status
= MaintenanceStatus("Applying new pod spec")
73 new_spec
= self
.make_pod_spec()
74 if new_spec
== self
.state
.spec
:
75 unit
.status
= ActiveStatus("ready")
77 self
.framework
.model
.pod
.set_spec(new_spec
)
78 self
.state
.spec
= new_spec
79 unit
.status
= ActiveStatus("ready")
81 def make_pod_spec(self
):
82 config
= self
.framework
.model
.config
85 "http_port": config
["port"],
86 "https_port": config
["https_port"],
87 "server_name": config
["server_name"],
88 "client_max_body_size": config
["client_max_body_size"],
89 "nbi_host": self
.state
.nbi_host
or config
["nbi_host"],
90 "nbi_port": self
.state
.nbi_port
or config
["nbi_port"],
95 ssl_certificate
= None
96 ssl_certificate_key
= None
99 if "ssl_certificate" in config
and "ssl_certificate_key" in config
:
100 # Get bytes of cert and key
101 cert_b
= base64
.b64decode(config
["ssl_certificate"])
102 key_b
= base64
.b64decode(config
["ssl_certificate_key"])
103 # Decode key and cert
104 ssl_certificate
= cert_b
.decode("utf-8")
105 ssl_certificate_key
= key_b
.decode("utf-8")
107 cert_path
= "{}/{}".format(self
.ssl_folder
, self
.ssl_crt_name
)
108 key_path
= "{}/{}".format(self
.ssl_folder
, self
.ssl_key_name
)
110 config_spec
["port"] = "{} ssl".format(config
["https_port"])
111 config_spec
["ssl_crt"] = "ssl_certificate {};".format(cert_path
)
112 config_spec
["ssl_crt_key"] = "ssl_certificate_key {};".format(key_path
)
115 config_spec
["ssl_crt"] = ""
116 config_spec
["ssl_crt_key"] = ""
120 "name": "configuration",
121 "mountPath": "/etc/nginx/sites-available/",
124 .name
: Template(Path(filename
).read_text())
125 .substitute(config_spec
)
126 for filename
in glob("files/*")
130 port
= config
["https_port"] if ssl_enabled
else config
["port"]
132 {"name": "port", "containerPort": port
, "protocol": "TCP", },
137 "tcpSocket": {"port": port
},
140 "initialDelaySeconds": 10,
143 "tcpSocket": {"port": port
},
145 "initialDelaySeconds": 45,
149 if ssl_certificate
and ssl_certificate_key
:
153 "mountPath": self
.ssl_folder
,
155 self
.ssl_crt_name
: ssl_certificate
,
156 self
.ssl_key_name
: ssl_certificate_key
,
165 "name": self
.framework
.model
.app
.name
,
166 "image": "{}".format(config
["image"]),
168 "kubernetes": kubernetes
,
176 def on_config_changed(self
, event
):
177 """Handle changes in configuration"""
180 def on_start(self
, event
):
181 """Called when the charm is being installed"""
184 def on_upgrade_charm(self
, event
):
185 """Upgrade the charm."""
186 unit
= self
.model
.unit
187 unit
.status
= MaintenanceStatus("Upgrading charm")
190 def on_nbi_relation_changed(self
, event
):
191 unit
= self
.model
.unit
192 if not unit
.is_leader():
194 self
.state
.nbi_host
= event
.relation
.data
[event
.unit
].get("host")
195 self
.state
.nbi_port
= event
.relation
.data
[event
.unit
].get("port")
199 if __name__
== "__main__":