Major improvement in OSM charms
[osm/devops.git] / installers / charm / mon / 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 "openstack_default_granularity": lambda value, _: (
40 isinstance(value, int) and value > 0
41 ),
42 "global_request_timeout": lambda value, _: isinstance(value, int) and value > 0,
43 "log_level": lambda value, _: (
44 isinstance(value, str) and value in ("INFO", "DEBUG")
45 ),
46 "collector_interval": lambda value, _: isinstance(value, int) and value > 0,
47 "evaluator_interval": lambda value, _: isinstance(value, int) and value > 0,
48 "database_commonkey": lambda value, _: (
49 isinstance(value, str) and len(value) > 0
50 ),
51 "vca_host": lambda value, _: isinstance(value, str) and len(value) > 0,
52 "vca_user": lambda value, _: isinstance(value, str) and len(value) > 0,
53 "vca_password": lambda value, _: isinstance(value, str) and len(value) > 0,
54 "vca_cacert": lambda value, _: isinstance(value, str),
55 }
56 relation_validators = {
57 "message_host": lambda value, _: isinstance(value, str) and len(value) > 0,
58 "message_port": lambda value, _: isinstance(value, int) and value > 0,
59 "database_uri": lambda value, _: (
60 isinstance(value, str) and value.startswith("mongodb://")
61 ),
62 "prometheus_host": lambda value, _: isinstance(value, str) and len(value) > 0,
63 "prometheus_port": lambda value, _: isinstance(value, int) and value > 0,
64 }
65 problems = []
66
67 for key, validator in config_validators.items():
68 valid = validator(config_data.get(key), config_data)
69
70 if not valid:
71 problems.append(key)
72
73 for key, validator in relation_validators.items():
74 valid = validator(relation_data.get(key), relation_data)
75
76 if not valid:
77 problems.append(key)
78
79 if len(problems) > 0:
80 raise ValueError("Errors found in: {}".format(", ".join(problems)))
81
82
83 def _make_pod_ports(port: int) -> List[Dict[str, Any]]:
84 """Generate pod ports details.
85
86 Args:
87 port (int): port to expose.
88
89 Returns:
90 List[Dict[str, Any]]: pod port details.
91 """
92 return [{"name": "mon", "containerPort": port, "protocol": "TCP"}]
93
94
95 def _make_pod_envconfig(
96 config: Dict[str, Any], relation_state: Dict[str, Any]
97 ) -> Dict[str, Any]:
98 """Generate pod environment configuration.
99
100 Args:
101 config (Dict[str, Any]): configuration information.
102 relation_state (Dict[str, Any]): relation state information.
103
104 Returns:
105 Dict[str, Any]: pod environment configuration.
106 """
107 envconfig = {
108 # General configuration
109 "ALLOW_ANONYMOUS_LOGIN": "yes",
110 "OSMMON_OPENSTACK_DEFAULT_GRANULARITY": config["openstack_default_granularity"],
111 "OSMMON_GLOBAL_REQUEST_TIMEOUT": config["global_request_timeout"],
112 "OSMMON_GLOBAL_LOGLEVEL": config["log_level"],
113 "OSMMON_COLLECTOR_INTERVAL": config["collector_interval"],
114 "OSMMON_EVALUATOR_INTERVAL": config["evaluator_interval"],
115 # Kafka configuration
116 "OSMMON_MESSAGE_DRIVER": "kafka",
117 "OSMMON_MESSAGE_HOST": relation_state["message_host"],
118 "OSMMON_MESSAGE_PORT": relation_state["message_port"],
119 # Database configuration
120 "OSMMON_DATABASE_DRIVER": "mongo",
121 "OSMMON_DATABASE_URI": relation_state["database_uri"],
122 "OSMMON_DATABASE_COMMONKEY": config["database_commonkey"],
123 # Prometheus configuration
124 "OSMMON_PROMETHEUS_URL": f"http://{relation_state['prometheus_host']}:{relation_state['prometheus_port']}",
125 # VCA configuration
126 "OSMMON_VCA_HOST": config["vca_host"],
127 "OSMMON_VCA_USER": config["vca_user"],
128 "OSMMON_VCA_SECRET": config["vca_password"],
129 "OSMMON_VCA_CACERT": config["vca_cacert"],
130 }
131
132 return envconfig
133
134
135 def _make_startup_probe() -> Dict[str, Any]:
136 """Generate startup probe.
137
138 Returns:
139 Dict[str, Any]: startup probe.
140 """
141 return {
142 "exec": {"command": ["/usr/bin/pgrep python3"]},
143 "initialDelaySeconds": 60,
144 "timeoutSeconds": 5,
145 }
146
147
148 def _make_readiness_probe(port: int) -> Dict[str, Any]:
149 """Generate readiness probe.
150
151 Args:
152 port (int): [description]
153
154 Returns:
155 Dict[str, Any]: readiness probe.
156 """
157 return {
158 "tcpSocket": {
159 "port": port,
160 },
161 "periodSeconds": 10,
162 "timeoutSeconds": 5,
163 "successThreshold": 1,
164 "failureThreshold": 3,
165 }
166
167
168 def _make_liveness_probe(port: int) -> Dict[str, Any]:
169 """Generate liveness probe.
170
171 Args:
172 port (int): [description]
173
174 Returns:
175 Dict[str, Any]: liveness probe.
176 """
177 return {
178 "tcpSocket": {
179 "port": port,
180 },
181 "initialDelaySeconds": 45,
182 "periodSeconds": 10,
183 "timeoutSeconds": 5,
184 "successThreshold": 1,
185 "failureThreshold": 3,
186 }
187
188
189 def make_pod_spec(
190 image_info: Dict[str, str],
191 config: Dict[str, Any],
192 relation_state: Dict[str, Any],
193 app_name: str = "mon",
194 port: int = 8000,
195 ) -> Dict[str, Any]:
196 """Generate the pod spec information.
197
198 Args:
199 image_info (Dict[str, str]): Object provided by
200 OCIImageResource("image").fetch().
201 config (Dict[str, Any]): Configuration information.
202 relation_state (Dict[str, Any]): Relation state information.
203 app_name (str, optional): Application name. Defaults to "mon".
204 port (int, optional): Port for the container. Defaults to 8000.
205
206 Returns:
207 Dict[str, Any]: Pod spec dictionary for the charm.
208 """
209 if not image_info:
210 return None
211
212 _validate_data(config, relation_state)
213
214 ports = _make_pod_ports(port)
215 env_config = _make_pod_envconfig(config, relation_state)
216
217 return {
218 "version": 3,
219 "containers": [
220 {
221 "name": app_name,
222 "imageDetails": image_info,
223 "imagePullPolicy": "Always",
224 "ports": ports,
225 "envConfig": env_config,
226 }
227 ],
228 "kubernetesResources": {
229 "ingressResources": [],
230 },
231 }