Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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 

23import logging 

24from typing import Any, Dict, List, NoReturn 

25 

26logger = logging.getLogger(__name__) 

27 

28 

29def _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, _: ( 

40 isinstance(value, str) and len(value) > 1 

41 ), 

42 "log_level": lambda value, _: ( 

43 isinstance(value, str) and value in ("INFO", "DEBUG") 

44 ), 

45 "vca_host": lambda value, _: isinstance(value, str) and len(value) > 1, 

46 "vca_port": lambda value, _: isinstance(value, int) and value > 0, 

47 "vca_user": lambda value, _: isinstance(value, str) and len(value) > 1, 

48 "vca_pubkey": lambda value, _: isinstance(value, str) and len(value) > 1, 

49 "vca_password": lambda value, _: isinstance(value, str) and len(value) > 1, 

50 "vca_cacert": lambda value, _: isinstance(value, str), 

51 "vca_cloud": lambda value, _: isinstance(value, str) and len(value) > 1, 

52 "vca_k8s_cloud": lambda value, _: isinstance(value, str) and len(value) > 1, 

53 "vca_apiproxy": lambda value, _: (isinstance(value, str) and len(value) > 1) 

54 if value 

55 else True, 

56 } 

57 relation_validators = { 

58 "ro_host": lambda value, _: isinstance(value, str) and len(value) > 1, 

59 "ro_port": lambda value, _: isinstance(value, int) and value > 0, 

60 "message_host": lambda value, _: isinstance(value, str) and len(value) > 1, 

61 "message_port": lambda value, _: isinstance(value, int) and value > 0, 

62 "database_uri": lambda value, _: isinstance(value, str) and len(value) > 1, 

63 } 

64 problems = [] 

65 

66 for key, validator in config_validators.items(): 

67 valid = validator(config_data.get(key), config_data) 

68 

69 if not valid: 

70 problems.append(key) 

71 

72 for key, validator in relation_validators.items(): 

73 valid = validator(relation_data.get(key), relation_data) 

74 

75 if not valid: 

76 problems.append(key) 

77 

78 if len(problems) > 0: 

79 raise ValueError("Errors found in: {}".format(", ".join(problems))) 

80 

81 

82def _make_pod_ports(port: int) -> List[Dict[str, Any]]: 

83 """Generate pod ports details. 

84 

85 Args: 

86 port (int): port to expose. 

87 

88 Returns: 

89 List[Dict[str, Any]]: pod port details. 

90 """ 

91 return [{"name": "lcm", "containerPort": port, "protocol": "TCP"}] 

92 

93 

94def _make_pod_envconfig( 

95 config: Dict[str, Any], relation_state: Dict[str, Any] 

96) -> Dict[str, Any]: 

97 """Generate pod environment configuration. 

98 

99 Args: 

100 config (Dict[str, Any]): configuration information. 

101 relation_state (Dict[str, Any]): relation state information. 

102 

103 Returns: 

104 Dict[str, Any]: pod environment configuration. 

105 """ 

106 envconfig = { 

107 # General configuration 

108 "ALLOW_ANONYMOUS_LOGIN": "yes", 

109 "OSMLCM_GLOBAL_LOGLEVEL": config["log_level"], 

110 # RO configuration 

111 "OSMLCM_RO_HOST": relation_state["ro_host"], 

112 "OSMLCM_RO_PORT": relation_state["ro_port"], 

113 "OSMLCM_RO_TENANT": "osm", 

114 # Kafka configuration 

115 "OSMLCM_MESSAGE_DRIVER": "kafka", 

116 "OSMLCM_MESSAGE_HOST": relation_state["message_host"], 

117 "OSMLCM_MESSAGE_PORT": relation_state["message_port"], 

118 # Database configuration 

119 "OSMLCM_DATABASE_DRIVER": "mongo", 

120 "OSMLCM_DATABASE_URI": relation_state["database_uri"], 

121 "OSMLCM_DATABASE_COMMONKEY": config["database_commonkey"], 

122 # Storage configuration 

123 "OSMLCM_STORAGE_DRIVER": "mongo", 

124 "OSMLCM_STORAGE_PATH": "/app/storage", 

125 "OSMLCM_STORAGE_COLLECTION": "files", 

126 "OSMLCM_STORAGE_URI": relation_state["database_uri"], 

127 # VCA configuration 

128 "OSMLCM_VCA_HOST": config["vca_host"], 

129 "OSMLCM_VCA_PORT": config["vca_port"], 

130 "OSMLCM_VCA_USER": config["vca_user"], 

131 "OSMLCM_VCA_PUBKEY": config["vca_pubkey"], 

132 "OSMLCM_VCA_SECRET": config["vca_password"], 

133 "OSMLCM_VCA_CACERT": config["vca_cacert"], 

134 "OSMLCM_VCA_CLOUD": config["vca_cloud"], 

135 "OSMLCM_VCA_K8S_CLOUD": config["vca_k8s_cloud"], 

136 } 

137 

138 if "vca_apiproxy" in config and config["vca_apiproxy"]: 

139 envconfig["OSMLCM_VCA_APIPROXY"] = config["vca_apiproxy"] 

140 

141 return envconfig 

142 

143 

144def _make_startup_probe() -> Dict[str, Any]: 

145 """Generate startup probe. 

146 

147 Returns: 

148 Dict[str, Any]: startup probe. 

149 """ 

150 return { 

151 "exec": {"command": ["/usr/bin/pgrep python3"]}, 

152 "initialDelaySeconds": 60, 

153 "timeoutSeconds": 5, 

154 } 

155 

156 

157def _make_readiness_probe(port: int) -> Dict[str, Any]: 

158 """Generate readiness probe. 

159 

160 Args: 

161 port (int): [description] 

162 

163 Returns: 

164 Dict[str, Any]: readiness probe. 

165 """ 

166 return { 

167 "httpGet": { 

168 "path": "/osm/", 

169 "port": port, 

170 }, 

171 "initialDelaySeconds": 45, 

172 "timeoutSeconds": 5, 

173 } 

174 

175 

176def _make_liveness_probe(port: int) -> Dict[str, Any]: 

177 """Generate liveness probe. 

178 

179 Args: 

180 port (int): [description] 

181 

182 Returns: 

183 Dict[str, Any]: liveness probe. 

184 """ 

185 return { 

186 "httpGet": { 

187 "path": "/osm/", 

188 "port": port, 

189 }, 

190 "initialDelaySeconds": 45, 

191 "timeoutSeconds": 5, 

192 } 

193 

194 

195def make_pod_spec( 

196 image_info: Dict[str, str], 

197 config: Dict[str, Any], 

198 relation_state: Dict[str, Any], 

199 app_name: str = "lcm", 

200 port: int = 9999, 

201) -> Dict[str, Any]: 

202 """Generate the pod spec information. 

203 

204 Args: 

205 image_info (Dict[str, str]): Object provided by 

206 OCIImageResource("image").fetch(). 

207 config (Dict[str, Any]): Configuration information. 

208 relation_state (Dict[str, Any]): Relation state information. 

209 app_name (str, optional): Application name. Defaults to "lcm". 

210 port (int, optional): Port for the container. Defaults to 9999. 

211 

212 Returns: 

213 Dict[str, Any]: Pod spec dictionary for the charm. 

214 """ 

215 if not image_info: 

216 return None 

217 

218 _validate_data(config, relation_state) 

219 

220 ports = _make_pod_ports(port) 

221 env_config = _make_pod_envconfig(config, relation_state) 

222 

223 return { 

224 "version": 3, 

225 "containers": [ 

226 { 

227 "name": app_name, 

228 "imageDetails": image_info, 

229 "imagePullPolicy": "Always", 

230 "ports": ports, 

231 "envConfig": env_config, 

232 } 

233 ], 

234 "kubernetesResources": { 

235 "ingressResources": [], 

236 }, 

237 }