2 # Copyright 2021 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
27 from typing
import NoReturn
, Optional
29 from charms
.kafka_k8s
.v0
.kafka
import KafkaEvents
, KafkaRequires
30 from ops
.main
import main
31 from opslib
.osm
.charm
import CharmedOsmBase
, RelationsMissing
32 from opslib
.osm
.interfaces
.mongo
import MongoClient
33 from opslib
.osm
.pod
import (
38 from opslib
.osm
.validator
import ModelValidator
, validator
41 logger
= logging
.getLogger(__name__
)
46 class ConfigModel(ModelValidator
):
47 database_commonkey
: str
48 mongodb_uri
: Optional
[str]
50 image_pull_policy
: str
51 security_context
: bool
53 @validator("log_level")
54 def validate_log_level(cls
, v
):
55 if v
not in {"INFO", "DEBUG"}:
56 raise ValueError("value must be INFO or DEBUG")
59 @validator("mongodb_uri")
60 def validate_mongodb_uri(cls
, v
):
61 if v
and not v
.startswith("mongodb://"):
62 raise ValueError("mongodb_uri is not properly formed")
65 @validator("image_pull_policy")
66 def validate_image_pull_policy(cls
, v
):
69 "ifnotpresent": "IfNotPresent",
73 if v
not in values
.keys():
74 raise ValueError("value must be always, ifnotpresent or never")
78 class PlaCharm(CharmedOsmBase
):
81 def __init__(self
, *args
) -> NoReturn
:
82 super().__init
__(*args
, oci_image
="image")
84 self
.kafka
= KafkaRequires(self
)
85 self
.framework
.observe(self
.on
.kafka_available
, self
.configure_pod
)
86 self
.framework
.observe(self
.on
.kafka_broken
, self
.configure_pod
)
88 self
.mongodb_client
= MongoClient(self
, "mongodb")
89 self
.framework
.observe(self
.on
["mongodb"].relation_changed
, self
.configure_pod
)
90 self
.framework
.observe(self
.on
["mongodb"].relation_broken
, self
.configure_pod
)
92 def _check_missing_dependencies(self
, config
: ConfigModel
):
93 missing_relations
= []
95 if not self
.kafka
.host
or not self
.kafka
.port
:
96 missing_relations
.append("kafka")
97 if not config
.mongodb_uri
and self
.mongodb_client
.is_missing_data_in_unit():
98 missing_relations
.append("mongodb")
100 if missing_relations
:
101 raise RelationsMissing(missing_relations
)
103 def build_pod_spec(self
, image_info
):
105 config
= ConfigModel(**dict(self
.config
))
107 if config
.mongodb_uri
and not self
.mongodb_client
.is_missing_data_in_unit():
108 raise Exception("Mongodb data cannot be provided via config and relation")
111 self
._check
_missing
_dependencies
(config
)
113 # Create Builder for the PodSpec
114 pod_spec_builder
= PodSpecV3Builder(
115 enable_security_context
=config
.security_context
118 # Add secrets to the pod
119 mongodb_secret_name
= f
"{self.app.name}-mongodb-secret"
120 pod_spec_builder
.add_secret(
123 "uri": config
.mongodb_uri
or self
.mongodb_client
.connection_string
,
124 "commonkey": config
.database_commonkey
,
129 container_builder
= ContainerV3Builder(
132 config
.image_pull_policy
,
133 run_as_non_root
=config
.security_context
,
135 container_builder
.add_port(name
=self
.app
.name
, port
=PORT
)
136 container_builder
.add_envs(
138 # General configuration
139 "ALLOW_ANONYMOUS_LOGIN": "yes",
140 "OSMPLA_GLOBAL_LOG_LEVEL": config
.log_level
,
141 # Kafka configuration
142 "OSMPLA_MESSAGE_DRIVER": "kafka",
143 "OSMPLA_MESSAGE_HOST": self
.kafka
.host
,
144 "OSMPLA_MESSAGE_PORT": self
.kafka
.port
,
145 # Database configuration
146 "OSMPLA_DATABASE_DRIVER": "mongo",
150 container_builder
.add_secret_envs(
151 secret_name
=mongodb_secret_name
,
153 "OSMPLA_DATABASE_URI": "uri",
154 "OSMPLA_DATABASE_COMMONKEY": "commonkey",
158 container
= container_builder
.build()
160 # Add Pod restart policy
161 restart_policy
= PodRestartPolicy()
162 restart_policy
.add_secrets(secret_names
=(mongodb_secret_name
,))
163 pod_spec_builder
.set_restart_policy(restart_policy
)
165 # Add container to pod spec
166 pod_spec_builder
.add_container(container
)
168 return pod_spec_builder
.build()
171 if __name__
== "__main__":