2 # Copyright 2020 Canonical Ltd.
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # 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, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
16 # For those usages not covered by the Apache License, Version 2.0 please
17 # contact: legal@canonical.com
19 # To get in touch with the maintainers, please contact:
20 # osm-charmers@lists.launchpad.net
23 # pylint: disable=E0213,E0611
27 from pydantic
import (
34 from typing
import Any
, Dict
, List
, Optional
35 from urllib
.parse
import urlparse
36 from pathlib
import Path
37 from string
import Template
39 logger
= logging
.getLogger(__name__
)
42 class ConfigData(BaseModel
):
43 """Configuration data model."""
46 site_url
: Optional
[str]
47 max_file_size
: Optional
[conint(ge
=0)]
48 ingress_whitelist_source_range
: Optional
[IPvAnyNetwork
]
49 tls_secret_name
: Optional
[str]
51 @validator("max_file_size", pre
=True, always
=True)
52 def validate_max_file_size(cls
, value
, values
, **kwargs
):
53 site_url
= values
.get("site_url")
58 parsed
= urlparse(site_url
)
60 if not parsed
.scheme
.startswith("http"):
64 raise ValueError("max_file_size needs to be defined if site_url is defined")
68 @validator("ingress_whitelist_source_range", pre
=True, always
=True)
69 def validate_ingress_whitelist_source_range(cls
, value
, values
, **kwargs
):
76 class RelationData(BaseModel
):
77 """Relation data model."""
83 def _make_pod_ports(port
: int) -> List
[Dict
[str, Any
]]:
84 """Generate pod ports details.
87 port (int): Port to expose.
90 List[Dict[str, Any]]: pod port details.
93 {"name": "http", "containerPort": port
, "protocol": "TCP"},
97 def _make_pod_ingress_resources(
98 config
: Dict
[str, Any
], app_name
: str, port
: int
99 ) -> List
[Dict
[str, Any
]]:
100 """Generate pod ingress resources.
103 config (Dict[str, Any]): configuration information.
104 app_name (str): application name.
105 port (int): port to expose.
108 List[Dict[str, Any]]: pod ingress resources.
110 site_url
= config
.get("site_url")
115 parsed
= urlparse(site_url
)
117 if not parsed
.scheme
.startswith("http"):
120 max_file_size
= config
["max_file_size"]
121 ingress_whitelist_source_range
= config
["ingress_whitelist_source_range"]
124 "nginx.ingress.kubernetes.io/proxy-body-size": "{}".format(
125 str(max_file_size
) + "m" if max_file_size
> 0 else max_file_size
129 if ingress_whitelist_source_range
:
131 "nginx.ingress.kubernetes.io/whitelist-source-range"
132 ] = ingress_whitelist_source_range
134 ingress_spec_tls
= None
136 if parsed
.scheme
== "https":
137 ingress_spec_tls
= [{"hosts": [parsed
.hostname
]}]
138 tls_secret_name
= config
["tls_secret_name"]
140 ingress_spec_tls
[0]["secretName"] = tls_secret_name
142 annotations
["nginx.ingress.kubernetes.io/ssl-redirect"] = "false"
145 "name": "{}-ingress".format(app_name
),
146 "annotations": annotations
,
150 "host": parsed
.hostname
,
156 "serviceName": app_name
,
167 ingress
["spec"]["tls"] = ingress_spec_tls
172 def _make_startup_probe() -> Dict
[str, Any
]:
173 """Generate startup probe.
176 Dict[str, Any]: startup probe.
179 "exec": {"command": ["/usr/bin/pgrep python3"]},
180 "initialDelaySeconds": 60,
185 def _make_readiness_probe(port
: int) -> Dict
[str, Any
]:
186 """Generate readiness probe.
189 port (int): [description]
192 Dict[str, Any]: readiness probe.
198 "initialDelaySeconds": 45,
203 def _make_liveness_probe(port
: int) -> Dict
[str, Any
]:
204 """Generate liveness probe.
207 port (int): [description]
210 Dict[str, Any]: liveness probe.
216 "initialDelaySeconds": 45,
221 def _make_pod_volume_config(
222 config
: Dict
[str, Any
],
223 relation_state
: Dict
[str, Any
],
224 ) -> List
[Dict
[str, Any
]]:
225 """Generate volume config with files.
228 config (Dict[str, Any]): configuration information.
231 Dict[str, Any]: volume config.
233 template_data
= {**config
, **relation_state
}
234 template_data
["max_file_size"] = f
'{template_data["max_file_size"]}M'
237 "name": "configuration",
238 "mountPath": "/etc/nginx/sites-available/",
242 "content": Template(Path("files/default").read_text()).substitute(
252 image_info
: Dict
[str, str],
253 config
: Dict
[str, Any
],
254 relation_state
: Dict
[str, Any
],
255 app_name
: str = "ng-ui",
257 """Generate the pod spec information.
260 image_info (Dict[str, str]): Object provided by
261 OCIImageResource("image").fetch().
262 config (Dict[str, Any]): Configuration information.
263 relation_state (Dict[str, Any]): Relation state information.
264 app_name (str, optional): Application name. Defaults to "ng-ui".
265 port (int, optional): Port for the container. Defaults to 80.
268 Dict[str, Any]: Pod spec dictionary for the charm.
273 ConfigData(**(config
))
274 RelationData(**(relation_state
))
276 ports
= _make_pod_ports(config
["port"])
277 ingress_resources
= _make_pod_ingress_resources(config
, app_name
, config
["port"])
279 # "startupProbe": _make_startup_probe(),
280 "readinessProbe": _make_readiness_probe(config
["port"]),
281 "livenessProbe": _make_liveness_probe(config
["port"]),
283 volume_config
= _make_pod_volume_config(config
, relation_state
)
289 "imageDetails": image_info
,
290 "imagePullPolicy": "Always",
292 "kubernetes": kubernetes
,
293 "volumeConfig": volume_config
,
296 "kubernetesResources": {
297 "ingressResources": ingress_resources
or [],