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
24 from pydantic
import (
31 from typing
import Any
, Dict
, List
, Optional
32 from urllib
.parse
import urlparse
33 from pathlib
import Path
34 from string
import Template
36 logger
= logging
.getLogger(__name__
)
39 class ConfigData(BaseModel
):
40 """Configuration data model."""
43 site_url
: Optional
[str]
44 max_file_size
: Optional
[conint(ge
=0)]
45 ingress_whitelist_source_range
: Optional
[IPvAnyNetwork
]
46 tls_secret_name
: Optional
[str]
48 @validator("max_file_size", pre
=True, always
=True)
49 def validate_max_file_size(cls
, value
, values
, **kwargs
):
50 site_url
= values
.get("site_url")
55 parsed
= urlparse(site_url
)
57 if not parsed
.scheme
.startswith("http"):
61 raise ValueError("max_file_size needs to be defined if site_url is defined")
65 @validator("ingress_whitelist_source_range", pre
=True, always
=True)
66 def validate_ingress_whitelist_source_range(cls
, value
, values
, **kwargs
):
73 class RelationData(BaseModel
):
74 """Relation data model."""
80 def _make_pod_ports(port
: int) -> List
[Dict
[str, Any
]]:
81 """Generate pod ports details.
84 port (int): Port to expose.
87 List[Dict[str, Any]]: pod port details.
90 {"name": "http", "containerPort": port
, "protocol": "TCP"},
94 def _make_pod_ingress_resources(
95 config
: Dict
[str, Any
], app_name
: str, port
: int
96 ) -> List
[Dict
[str, Any
]]:
97 """Generate pod ingress resources.
100 config (Dict[str, Any]): configuration information.
101 app_name (str): application name.
102 port (int): port to expose.
105 List[Dict[str, Any]]: pod ingress resources.
107 site_url
= config
.get("site_url")
112 parsed
= urlparse(site_url
)
114 if not parsed
.scheme
.startswith("http"):
117 max_file_size
= config
["max_file_size"]
118 ingress_whitelist_source_range
= config
["ingress_whitelist_source_range"]
121 "nginx.ingress.kubernetes.io/proxy-body-size": "{}".format(
122 str(max_file_size
) + "m" if max_file_size
> 0 else max_file_size
126 if ingress_whitelist_source_range
:
128 "nginx.ingress.kubernetes.io/whitelist-source-range"
129 ] = ingress_whitelist_source_range
131 ingress_spec_tls
= None
133 if parsed
.scheme
== "https":
134 ingress_spec_tls
= [{"hosts": [parsed
.hostname
]}]
135 tls_secret_name
= config
["tls_secret_name"]
137 ingress_spec_tls
[0]["secretName"] = tls_secret_name
139 annotations
["nginx.ingress.kubernetes.io/ssl-redirect"] = "false"
142 "name": "{}-ingress".format(app_name
),
143 "annotations": annotations
,
147 "host": parsed
.hostname
,
153 "serviceName": app_name
,
164 ingress
["spec"]["tls"] = ingress_spec_tls
169 def _make_startup_probe() -> Dict
[str, Any
]:
170 """Generate startup probe.
173 Dict[str, Any]: startup probe.
176 "exec": {"command": ["/usr/bin/pgrep python3"]},
177 "initialDelaySeconds": 60,
182 def _make_readiness_probe(port
: int) -> Dict
[str, Any
]:
183 """Generate readiness probe.
186 port (int): [description]
189 Dict[str, Any]: readiness probe.
195 "initialDelaySeconds": 45,
200 def _make_liveness_probe(port
: int) -> Dict
[str, Any
]:
201 """Generate liveness probe.
204 port (int): [description]
207 Dict[str, Any]: liveness probe.
213 "initialDelaySeconds": 45,
218 def _make_pod_volume_config(
219 config
: Dict
[str, Any
],
220 relation_state
: Dict
[str, Any
],
221 ) -> List
[Dict
[str, Any
]]:
222 """Generate volume config with files.
225 config (Dict[str, Any]): configuration information.
228 Dict[str, Any]: volume config.
230 template_data
= {**config
, **relation_state
}
231 template_data
["max_file_size"] = f
'{template_data["max_file_size"]}M'
234 "name": "configuration",
235 "mountPath": "/etc/nginx/sites-available/",
239 "content": Template(Path("files/default").read_text()).substitute(
249 image_info
: Dict
[str, str],
250 config
: Dict
[str, Any
],
251 relation_state
: Dict
[str, Any
],
252 app_name
: str = "ng-ui",
254 """Generate the pod spec information.
257 image_info (Dict[str, str]): Object provided by
258 OCIImageResource("image").fetch().
259 config (Dict[str, Any]): Configuration information.
260 relation_state (Dict[str, Any]): Relation state information.
261 app_name (str, optional): Application name. Defaults to "ng-ui".
262 port (int, optional): Port for the container. Defaults to 80.
265 Dict[str, Any]: Pod spec dictionary for the charm.
270 ConfigData(**(config
))
271 RelationData(**(relation_state
))
273 ports
= _make_pod_ports(config
["port"])
274 ingress_resources
= _make_pod_ingress_resources(config
, app_name
, config
["port"])
276 # "startupProbe": _make_startup_probe(),
277 "readinessProbe": _make_readiness_probe(config
["port"]),
278 "livenessProbe": _make_liveness_probe(config
["port"]),
280 volume_config
= _make_pod_volume_config(config
, relation_state
)
286 "imageDetails": image_info
,
287 "imagePullPolicy": "Always",
289 "kubernetes": kubernetes
,
290 "volumeConfig": volume_config
,
293 "kubernetesResources": {
294 "ingressResources": ingress_resources
or [],