dc214537bca14d562b0dc60167370647d4369c99
[osm/devops.git] / installers / charm / lcm / src / pod_spec.py
1 #!/usr/bin/env python3
2 # Copyright 2020 Canonical Ltd.
3 #
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
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
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
14 # under the License.
15 #
16 # For those usages not covered by the Apache License, Version 2.0 please
17 # contact: legal@canonical.com
18 #
19 # To get in touch with the maintainers, please contact:
20 # osm-charmers@lists.launchpad.net
21 ##
22
23 import logging
24 from typing import Any, Dict, List, NoReturn
25
26 logger = logging.getLogger(__name__)
27
28
29 def _validate_data(
30 config_data: Dict[str, Any], relation_data: Dict[str, Any]
31 ) -> NoReturn:
32 """Validate input data.
33
34 Args:
35 config_data (Dict[str, Any]): configuration data.
36 relation_data (Dict[str, Any]): relation data.
37 """
38 config_validators = {
39 "database_commonkey": lambda value, _: isinstance(value, str)
40 and len(value) > 1,
41 "log_level": lambda value, _: isinstance(value, str)
42 and value in ("INFO", "DEBUG"),
43 "vca_host": lambda value, _: isinstance(value, str) and len(value) > 1,
44 "vca_port": lambda value, _: isinstance(value, int) and value > 0,
45 "vca_user": lambda value, _: isinstance(value, str) and len(value) > 1,
46 "vca_pubkey": lambda value, _: isinstance(value, str) and len(value) > 1,
47 "vca_password": lambda value, _: isinstance(value, str) and len(value) > 1,
48 "vca_cacert": lambda value, _: isinstance(value, str),
49 "vca_cloud": lambda value, _: isinstance(value, str) and len(value) > 1,
50 "vca_k8s_cloud": lambda value, _: isinstance(value, str) and len(value) > 1,
51 "vca_apiproxy": lambda value, _: (isinstance(value, str) and len(value) > 1)
52 if value
53 else True,
54 }
55 relation_validators = {
56 "ro_host": lambda value, _: isinstance(value, str) and len(value) > 1,
57 "ro_port": lambda value, _: isinstance(value, int) and value > 0,
58 "message_host": lambda value, _: isinstance(value, str) and len(value) > 1,
59 "message_port": lambda value, _: isinstance(value, int) and value > 0,
60 "database_uri": lambda value, _: isinstance(value, str) and len(value) > 1,
61 }
62 problems = []
63
64 for key, validator in config_validators.items():
65 valid = validator(config_data.get(key), config_data)
66
67 if not valid:
68 problems.append(key)
69
70 for key, validator in relation_validators.items():
71 valid = validator(relation_data.get(key), relation_data)
72
73 if not valid:
74 problems.append(key)
75
76 if len(problems) > 0:
77 raise ValueError("Errors found in: {}".format(", ".join(problems)))
78
79
80 def _make_pod_ports(port: int) -> List[Dict[str, Any]]:
81 """Generate pod ports details.
82
83 Args:
84 port (int): port to expose.
85
86 Returns:
87 List[Dict[str, Any]]: pod port details.
88 """
89 return [{"name": "lcm", "containerPort": port, "protocol": "TCP"}]
90
91
92 def _make_pod_envconfig(
93 config: Dict[str, Any], relation_state: Dict[str, Any]
94 ) -> Dict[str, Any]:
95 """Generate pod environment configuration.
96
97 Args:
98 config (Dict[str, Any]): configuration information.
99 relation_state (Dict[str, Any]): relation state information.
100
101 Returns:
102 Dict[str, Any]: pod environment configuration.
103 """
104 envconfig = {
105 # General configuration
106 "ALLOW_ANONYMOUS_LOGIN": "yes",
107 "OSMLCM_GLOBAL_LOGLEVEL": config["log_level"],
108 # RO configuration
109 "OSMLCM_RO_HOST": relation_state["ro_host"],
110 "OSMLCM_RO_PORT": relation_state["ro_port"],
111 "OSMLCM_RO_TENANT": "osm",
112 # Kafka configuration
113 "OSMLCM_MESSAGE_DRIVER": "kafka",
114 "OSMLCM_MESSAGE_HOST": relation_state["message_host"],
115 "OSMLCM_MESSAGE_PORT": relation_state["message_port"],
116 # Database configuration
117 "OSMLCM_DATABASE_DRIVER": "mongo",
118 "OSMLCM_DATABASE_URI": relation_state["database_uri"],
119 "OSMLCM_DATABASE_COMMONKEY": config["database_commonkey"],
120 # Storage configuration
121 "OSMLCM_STORAGE_DRIVER": "mongo",
122 "OSMLCM_STORAGE_PATH": "/app/storage",
123 "OSMLCM_STORAGE_COLLECTION": "files",
124 "OSMLCM_STORAGE_URI": relation_state["database_uri"],
125 # VCA configuration
126 "OSMLCM_VCA_HOST": config["vca_host"],
127 "OSMLCM_VCA_PORT": config["vca_port"],
128 "OSMLCM_VCA_USER": config["vca_user"],
129 "OSMLCM_VCA_PUBKEY": config["vca_pubkey"],
130 "OSMLCM_VCA_SECRET": config["vca_password"],
131 "OSMLCM_VCA_CACERT": config["vca_cacert"],
132 "OSMLCM_VCA_CLOUD": config["vca_cloud"],
133 "OSMLCM_VCA_K8S_CLOUD": config["vca_k8s_cloud"],
134 }
135
136 if "vca_apiproxy" in config and config["vca_apiproxy"]:
137 envconfig["OSMLCM_VCA_APIPROXY"] = config["vca_apiproxy"]
138
139 return envconfig
140
141
142 def _make_startup_probe() -> Dict[str, Any]:
143 """Generate startup probe.
144
145 Returns:
146 Dict[str, Any]: startup probe.
147 """
148 return {
149 "exec": {"command": ["/usr/bin/pgrep python3"]},
150 "initialDelaySeconds": 60,
151 "timeoutSeconds": 5,
152 }
153
154
155 def _make_readiness_probe(port: int) -> Dict[str, Any]:
156 """Generate readiness probe.
157
158 Args:
159 port (int): [description]
160
161 Returns:
162 Dict[str, Any]: readiness probe.
163 """
164 return {
165 "httpGet": {
166 "path": "/osm/",
167 "port": port,
168 },
169 "initialDelaySeconds": 45,
170 "timeoutSeconds": 5,
171 }
172
173
174 def _make_liveness_probe(port: int) -> Dict[str, Any]:
175 """Generate liveness probe.
176
177 Args:
178 port (int): [description]
179
180 Returns:
181 Dict[str, Any]: liveness probe.
182 """
183 return {
184 "httpGet": {
185 "path": "/osm/",
186 "port": port,
187 },
188 "initialDelaySeconds": 45,
189 "timeoutSeconds": 5,
190 }
191
192
193 def make_pod_spec(
194 image_info: Dict[str, str],
195 config: Dict[str, Any],
196 relation_state: Dict[str, Any],
197 app_name: str = "lcm",
198 port: int = 9999,
199 ) -> Dict[str, Any]:
200 """Generate the pod spec information.
201
202 Args:
203 image_info (Dict[str, str]): Object provided by
204 OCIImageResource("image").fetch().
205 config (Dict[str, Any]): Configuration information.
206 relation_state (Dict[str, Any]): Relation state information.
207 app_name (str, optional): Application name. Defaults to "lcm".
208 port (int, optional): Port for the container. Defaults to 9999.
209
210 Returns:
211 Dict[str, Any]: Pod spec dictionary for the charm.
212 """
213 if not image_info:
214 return None
215
216 _validate_data(config, relation_state)
217
218 ports = _make_pod_ports(port)
219 env_config = _make_pod_envconfig(config, relation_state)
220
221 return {
222 "version": 3,
223 "containers": [
224 {
225 "name": app_name,
226 "imageDetails": image_info,
227 "imagePullPolicy": "Always",
228 "ports": ports,
229 "envConfig": env_config,
230 }
231 ],
232 "kubernetesResources": {
233 "ingressResources": [],
234 },
235 }