7510a6cbb7c87230a74eb4d2173dcbd6820b670d
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 typing
import Any
, Dict
, NoReturn
25 from pydantic
import ValidationError
27 from ops
.charm
import CharmBase
, CharmEvents
28 from ops
.framework
import EventBase
, EventSource
, StoredState
29 from ops
.main
import main
30 from ops
.model
import ActiveStatus
, BlockedStatus
, MaintenanceStatus
31 from oci_image
import OCIImageResource
, OCIImageResourceError
33 from pod_spec
import make_pod_spec
35 logger
= logging
.getLogger(__name__
)
40 class ConfigurePodEvent(EventBase
):
41 """Configure Pod event"""
46 class NgUiEvents(CharmEvents
):
49 configure_pod
= EventSource(ConfigurePodEvent
)
52 class NgUiCharm(CharmBase
):
58 def __init__(self
, *args
) -> NoReturn
:
59 """NGUI Charm constructor."""
60 super().__init
__(*args
)
62 # Internal state initialization
63 self
.state
.set_default(pod_spec
=None)
65 # North bound interface initialization
66 self
.state
.set_default(nbi_host
=None)
67 self
.state
.set_default(nbi_port
=None)
69 self
.http_port
= NGUI_PORT
70 self
.image
= OCIImageResource(self
, "image")
72 # Registering regular events
73 self
.framework
.observe(self
.on
.start
, self
.configure_pod
)
74 self
.framework
.observe(self
.on
.config_changed
, self
.configure_pod
)
75 # self.framework.observe(self.on.upgrade_charm, self.configure_pod)
77 # Registering custom internal events
78 self
.framework
.observe(self
.on
.configure_pod
, self
.configure_pod
)
80 # Registering required relation changed events
81 self
.framework
.observe(
82 self
.on
.nbi_relation_changed
, self
._on
_nbi
_relation
_changed
85 # Registering required relation departed events
86 self
.framework
.observe(
87 self
.on
.nbi_relation_departed
, self
._on
_nbi
_relation
_departed
90 def _on_nbi_relation_changed(self
, event
: EventBase
) -> NoReturn
:
91 """Reads information about the nbi relation.
94 event (EventBase): NBI relation event.
96 data_loc
= event
.unit
if event
.unit
else event
.app
97 logger
.error(dict(event
.relation
.data
))
98 nbi_host
= event
.relation
.data
[data_loc
].get("host")
99 nbi_port
= event
.relation
.data
[data_loc
].get("port")
104 and (self
.state
.nbi_host
!= nbi_host
or self
.state
.nbi_port
!= nbi_port
)
106 self
.state
.nbi_host
= nbi_host
107 self
.state
.nbi_port
= nbi_port
108 self
.on
.configure_pod
.emit()
110 def _on_nbi_relation_departed(self
, event
: EventBase
) -> NoReturn
:
111 """Clears data from nbi relation.
114 event (EventBase): NBI relation event.
116 self
.state
.nbi_host
= None
117 self
.state
.nbi_port
= None
118 self
.on
.configure_pod
.emit()
120 def _missing_relations(self
) -> str:
121 """Checks if there missing relations.
124 str: string with missing relations
127 "nbi": self
.state
.nbi_host
,
130 missing_relations
= [k
for k
, v
in data_status
.items() if not v
]
132 return ", ".join(missing_relations
)
135 def relation_state(self
) -> Dict
[str, Any
]:
136 """Collects relation state configuration for pod spec assembly.
139 Dict[str, Any]: relation state information.
142 "nbi_host": self
.state
.nbi_host
,
143 "nbi_port": self
.state
.nbi_port
,
145 return relation_state
147 def configure_pod(self
, event
: EventBase
) -> NoReturn
:
148 """Assemble the pod spec and apply it, if possible.
151 event (EventBase): Hook or Relation event that started the
154 if missing
:= self
._missing
_relations
():
155 self
.unit
.status
= BlockedStatus(
156 f
"Waiting for {missing} relation{'s' if ',' in missing else ''}"
160 if not self
.unit
.is_leader():
161 self
.unit
.status
= ActiveStatus("ready")
164 self
.unit
.status
= MaintenanceStatus("Assembling pod spec")
166 # Fetch image information
168 self
.unit
.status
= MaintenanceStatus("Fetching image information")
169 image_info
= self
.image
.fetch()
170 except OCIImageResourceError
:
171 self
.unit
.status
= BlockedStatus("Error fetching image information")
175 pod_spec
= make_pod_spec(
181 except ValidationError
as exc
:
182 logger
.exception("Config/Relation data validation error")
183 self
.unit
.status
= BlockedStatus(str(exc
))
186 if self
.state
.pod_spec
!= pod_spec
:
187 self
.model
.pod
.set_spec(pod_spec
)
188 self
.state
.pod_spec
= pod_spec
190 self
.unit
.status
= ActiveStatus("ready")
193 if __name__
== "__main__":