1 # Copyright 2023 Canonical Ltd.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
16 from dataclasses
import dataclass
17 from typing
import List
19 from juju
.controller
import Controller
20 from juju
.model
import Model
22 from n2vc
.exceptions
import (
23 JujuControllerFailedConnecting
,
24 JujuModelAlreadyExists
,
30 """Information to connect to juju controller"""
37 cloud_credentials
: str
40 return f
"{self.__class__.__name__}(endpoint: {self.endpoint}, user: {self.user}, password: ******, caert: ******)"
43 return f
"{self.__class__.__name__}(endpoint: {self.endpoint}, user: {self.user}, password: ******, caert: ******)"
47 def __init__(self
, connection_info
: ConnectionInfo
) -> None:
48 self
.logger
= logging
.getLogger("temporal_libjuju")
49 self
.connection_info
= connection_info
51 async def get_controller(self
) -> Controller
:
52 controller
= Controller()
54 await controller
.connect(
55 endpoint
=self
.connection_info
.endpoint
,
56 username
=self
.connection_info
.user
,
57 password
=self
.connection_info
.password
,
58 cacert
=self
.connection_info
.cacert
,
61 except Exception as e
:
63 "Error connecting to controller={}: {}".format(
64 self
.connection_info
.endpoint
, e
67 await self
.disconnect_controller(controller
)
68 raise JujuControllerFailedConnecting(str(e
))
70 async def disconnect_controller(self
, controller
: Controller
) -> None:
72 await controller
.disconnect()
74 async def disconnect_model(self
, model
: Model
):
76 await model
.disconnect()
78 async def add_model(self
, model_name
: str):
79 """Exception is raised if model_name already exists"""
83 controller
= await self
.get_controller()
84 if await self
.model_exists(model_name
, controller
=controller
):
85 raise JujuModelAlreadyExists(
86 "Cannot create model {}".format(model_name
)
88 self
.logger
.debug("Creating model {}".format(model_name
))
89 model
= await controller
.add_model(
91 cloud_name
=self
.connection_info
.cloud_name
,
92 credential_name
=self
.connection_info
.cloud_credentials
,
95 await self
.disconnect_model(model
)
96 await self
.disconnect_controller(controller
)
98 async def model_exists(
99 self
, model_name
: str, controller
: Controller
= None
101 """Returns True if model exists. False otherwhise."""
102 need_to_disconnect
= False
105 controller
= await self
.get_controller()
106 need_to_disconnect
= True
108 return model_name
in await controller
.list_models()
110 if need_to_disconnect
:
111 await self
.disconnect_controller(controller
)
113 async def get_model(self
, controller
: Controller
, model_name
: str) -> Model
:
114 return await controller
.get_model(model_name
)
116 async def list_models(self
) -> List
[str]:
117 """List models in controller."""
119 controller
= await self
.get_controller()
120 return await controller
.list_models()
122 await self
.disconnect_controller(controller
)
124 async def destroy_model(self
, model_name
: str, force
=False) -> None:
129 controller
= await self
.get_controller()
130 if not await self
.model_exists(model_name
, controller
=controller
):
131 self
.logger
.warn(f
"Model {model_name} doesn't exist")
134 self
.logger
.debug(f
"Getting model {model_name} to destroy")
135 model
= await self
.get_model(controller
, model_name
)
136 await self
.disconnect_model(model
)
138 await controller
.destroy_model(
139 model_name
, destroy_storage
=True, force
=force
, max_wait
=60
142 except Exception as e
:
143 self
.logger
.warn(f
"Failed deleting model {model_name}: {e}")
146 await self
.disconnect_model(model
)
147 await self
.disconnect_controller(controller
)