1 # Copyright 2019 Canonical Ltd.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
16 from typing
import Union
23 from n2vc
.config
import EnvironConfig
24 from n2vc
.definitions
import RelationEndpoint
25 from n2vc
.exceptions
import K8sException
26 from n2vc
.k8s_conn
import K8sConnector
27 from n2vc
.kubectl
import Kubectl
28 from .exceptions
import MethodNotImplemented
29 from n2vc
.libjuju
import Libjuju
30 from n2vc
.utils
import obj_to_dict
, obj_to_yaml
31 from n2vc
.store
import MotorStore
32 from n2vc
.vca
.cloud
import Cloud
33 from n2vc
.vca
.connection
import get_connection
36 RBAC_LABEL_KEY_NAME
= "rbac-id"
37 RBAC_STACK_PREFIX
= "juju-credential"
40 def generate_rbac_id():
41 return binascii
.hexlify(os
.urandom(4)).decode()
44 class K8sJujuConnector(K8sConnector
):
51 kubectl_command
: str = "/usr/bin/kubectl",
52 juju_command
: str = "/usr/bin/juju",
58 :param fs: file system for kubernetes and helm configuration
59 :param db: Database object
60 :param kubectl_command: path to kubectl executable
61 :param helm_command: path to helm executable
63 :param: loop: Asyncio loop
67 K8sConnector
.__init
__(
71 on_update_db
=on_update_db
,
75 self
.loop
= loop
or asyncio
.get_event_loop()
76 self
.log
.debug("Initializing K8S Juju connector")
78 db_uri
= EnvironConfig(prefixes
=["OSMLCM_", "OSMMON_"]).get("database_uri")
79 self
._store
= MotorStore(db_uri
)
80 self
.loading_libjuju
= asyncio
.Lock(loop
=self
.loop
)
81 self
.uninstall_locks
= {}
83 self
.log
.debug("K8S Juju connector initialized")
84 # TODO: Remove these commented lines:
85 # self.authenticated = False
87 # self.juju_secret = ""
94 namespace
: str = "kube-system",
95 reuse_cluster_uuid
: str = None,
99 It prepares a given K8s cluster environment to run Juju bundles.
101 :param k8s_creds: credentials to access a given K8s cluster, i.e. a valid
103 :param namespace: optional namespace to be used for juju. By default,
104 'kube-system' will be used
105 :param reuse_cluster_uuid: existing cluster uuid for reuse
106 :param: kwargs: Additional parameters
109 :return: uuid of the K8s cluster and True if connector has installed some
110 software in the cluster
111 (on error, an exception will be raised)
113 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
115 cluster_uuid
= reuse_cluster_uuid
or str(uuid
.uuid4())
116 kubectl
= self
._get
_kubectl
(k8s_creds
)
118 # CREATING RESOURCES IN K8S
119 rbac_id
= generate_rbac_id()
120 metadata_name
= "{}-{}".format(RBAC_STACK_PREFIX
, rbac_id
)
121 labels
= {RBAC_STACK_PREFIX
: rbac_id
}
123 # Create cleanup dictionary to clean up created resources
124 # if it fails in the middle of the process
127 self
.log
.debug("Initializing K8s cluster for juju")
128 kubectl
.create_cluster_role(
132 self
.log
.debug("Cluster role created")
135 "delete": kubectl
.delete_cluster_role
,
136 "args": (metadata_name
,),
140 kubectl
.create_service_account(
144 self
.log
.debug("Service account created")
147 "delete": kubectl
.delete_service_account
,
148 "args": (metadata_name
,),
152 kubectl
.create_cluster_role_binding(
156 self
.log
.debug("Role binding created")
159 "delete": kubectl
.delete_cluster_role_binding
,
160 "args": (metadata_name
,),
163 token
, client_cert_data
= await kubectl
.get_secret_data(
167 default_storage_class
= kubectl
.get_default_storage_class()
168 self
.log
.debug("Default storage class: {}".format(default_storage_class
))
169 await libjuju
.add_k8s(
173 client_cert_data
=client_cert_data
,
174 configuration
=kubectl
.configuration
,
175 storage_class
=default_storage_class
,
176 credential_name
=self
._get
_credential
_name
(cluster_uuid
),
178 self
.log
.debug("K8s cluster added to juju controller")
179 return cluster_uuid
, True
180 except Exception as e
:
181 self
.log
.error("Error initializing k8scluster: {}".format(e
), exc_info
=True)
182 if len(cleanup_data
) > 0:
183 self
.log
.debug("Cleaning up created resources in k8s cluster...")
184 for item
in cleanup_data
:
185 delete_function
= item
["delete"]
186 delete_args
= item
["args"]
187 delete_function(*delete_args
)
188 self
.log
.debug("Cleanup finished")
191 """Repo Management"""
197 _type
: str = "charm",
200 password
: str = None,
202 raise MethodNotImplemented()
204 async def repo_list(self
):
205 raise MethodNotImplemented()
207 async def repo_remove(
211 raise MethodNotImplemented()
213 async def synchronize_repos(self
, cluster_uuid
: str, name
: str):
215 Returns None as currently add_repo is not implemented
225 uninstall_sw
: bool = False,
230 Resets the Kubernetes cluster by removing the model that represents it.
232 :param cluster_uuid str: The UUID of the cluster to reset
233 :param force: Force reset
234 :param uninstall_sw: Boolean to uninstall sw
235 :param: kwargs: Additional parameters
238 :return: Returns True if successful or raises an exception.
242 self
.log
.debug("[reset] Removing k8s cloud")
243 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
245 cloud
= Cloud(cluster_uuid
, self
._get
_credential
_name
(cluster_uuid
))
247 cloud_creds
= await libjuju
.get_cloud_credentials(cloud
)
249 await libjuju
.remove_cloud(cluster_uuid
)
251 credentials
= self
.get_credentials(cluster_uuid
=cluster_uuid
)
253 kubectl
= self
._get
_kubectl
(credentials
)
256 kubectl
.delete_cluster_role_binding
,
257 kubectl
.delete_service_account
,
258 kubectl
.delete_cluster_role
,
261 credential_attrs
= cloud_creds
[0].result
["attrs"]
262 if RBAC_LABEL_KEY_NAME
in credential_attrs
:
263 rbac_id
= credential_attrs
[RBAC_LABEL_KEY_NAME
]
264 metadata_name
= "{}-{}".format(RBAC_STACK_PREFIX
, rbac_id
)
265 for delete_func
in delete_functions
:
267 delete_func(metadata_name
)
268 except Exception as e
:
269 self
.log
.warning("Cannot remove resource in K8s {}".format(e
))
271 except Exception as e
:
272 self
.log
.debug("Caught exception during reset: {}".format(e
))
284 timeout
: float = 1800,
286 db_dict
: dict = None,
287 kdu_name
: str = None,
288 namespace
: str = None,
293 :param cluster_uuid str: The UUID of the cluster to install to
294 :param kdu_model str: The name or path of a bundle to install
295 :param kdu_instance: Kdu instance name
296 :param atomic bool: If set, waits until the model is active and resets
297 the cluster on failure.
298 :param timeout int: The time, in seconds, to wait for the install
300 :param params dict: Key-value pairs of instantiation parameters
301 :param kdu_name: Name of the KDU instance to be installed
302 :param namespace: K8s namespace to use for the KDU instance
303 :param kwargs: Additional parameters
306 :return: If successful, returns ?
308 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
312 raise K8sException("db_dict must be set")
314 raise K8sException("bundle must be set")
316 if bundle
.startswith("cs:"):
317 # For Juju Bundles provided by the Charm Store
319 elif bundle
.startswith("ch:"):
320 # For Juju Bundles provided by the Charm Hub (this only works for juju version >= 2.9)
322 elif bundle
.startswith("http"):
326 new_workdir
= kdu_model
.strip(kdu_model
.split("/")[-1])
327 os
.chdir(new_workdir
)
328 bundle
= "local:{}".format(kdu_model
)
330 # default namespace to kdu_instance
332 namespace
= kdu_instance
334 self
.log
.debug("Checking for model named {}".format(namespace
))
336 # Create the new model
337 self
.log
.debug("Adding model: {}".format(namespace
))
338 cloud
= Cloud(cluster_uuid
, self
._get
_credential
_name
(cluster_uuid
))
339 await libjuju
.add_model(namespace
, cloud
)
342 # TODO: Instantiation parameters
345 "Juju bundle that models the KDU, in any of the following ways:
346 - <juju-repo>/<juju-bundle>
347 - <juju-bundle folder under k8s_models folder in the package>
348 - <juju-bundle tgz file (w/ or w/o extension) under k8s_models folder
350 - <URL_where_to_fetch_juju_bundle>
353 previous_workdir
= os
.getcwd()
354 except FileNotFoundError
:
355 previous_workdir
= "/app/storage"
357 self
.log
.debug("[install] deploying {}".format(bundle
))
358 await libjuju
.deploy(bundle
, model_name
=namespace
, wait
=atomic
, timeout
=timeout
)
359 os
.chdir(previous_workdir
)
361 # update information in the database (first, the VCA status, and then, the namespace)
362 if self
.on_update_db
:
363 await self
.on_update_db(
366 filter=db_dict
["filter"],
367 vca_id
=kwargs
.get("vca_id"),
372 q_filter
={"_admin.deployed.K8s.kdu-instance": kdu_instance
},
373 update_dict
={"_admin.deployed.K8s.$.namespace": namespace
},
383 total_timeout
: float = 1800,
384 namespace
: str = None,
387 """Scale an application in a model
389 :param: kdu_instance str: KDU instance name
390 :param: scale int: Scale to which to set the application
391 :param: resource_name str: The application name in the Juju Bundle
392 :param: timeout float: The time, in seconds, to wait for the install
394 :param namespace str: The namespace (model) where the Bundle was deployed
395 :param kwargs: Additional parameters
398 :return: If successful, returns True
401 model_name
= self
._obtain
_namespace
(
402 kdu_instance
=kdu_instance
, namespace
=namespace
405 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
406 await libjuju
.scale_application(
407 model_name
=model_name
,
408 application_name
=resource_name
,
410 total_timeout
=total_timeout
,
412 except Exception as e
:
413 error_msg
= "Error scaling application {} of the model {} of the kdu instance {}: {}".format(
414 resource_name
, model_name
, kdu_instance
, e
416 self
.log
.error(error_msg
)
417 raise K8sException(message
=error_msg
)
420 async def get_scale_count(
424 namespace
: str = None,
427 """Get an application scale count
429 :param: resource_name str: The application name in the Juju Bundle
430 :param: kdu_instance str: KDU instance name
431 :param namespace str: The namespace (model) where the Bundle was deployed
432 :param kwargs: Additional parameters
434 :return: Return application instance count
437 model_name
= self
._obtain
_namespace
(
438 kdu_instance
=kdu_instance
, namespace
=namespace
441 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
442 status
= await libjuju
.get_model_status(model_name
=model_name
)
443 return len(status
.applications
[resource_name
].units
)
444 except Exception as e
:
446 f
"Error getting scale count from application {resource_name} of the model {model_name} of "
447 f
"the kdu instance {kdu_instance}: {e}"
449 self
.log
.error(error_msg
)
450 raise K8sException(message
=error_msg
)
452 async def instances_list(self
, cluster_uuid
: str) -> list:
454 returns a list of deployed releases in a cluster
456 :param cluster_uuid: the cluster
465 kdu_model
: str = None,
470 :param cluster_uuid str: The UUID of the cluster to upgrade
471 :param kdu_instance str: The unique name of the KDU instance
472 :param kdu_model str: The name or path of the bundle to upgrade to
473 :param params dict: Key-value pairs of instantiation parameters
475 :return: If successful, reference to the new revision number of the
479 # TODO: Loop through the bundle and upgrade each charm individually
482 The API doesn't have a concept of bundle upgrades, because there are
483 many possible changes: charm revision, disk, number of units, etc.
485 As such, we are only supporting a limited subset of upgrades. We'll
486 upgrade the charm revision but leave storage and scale untouched.
488 Scale changes should happen through OSM constructs, and changes to
489 storage would require a redeployment of the service, at least in this
492 raise MethodNotImplemented()
504 :param cluster_uuid str: The UUID of the cluster to rollback
505 :param kdu_instance str: The unique name of the KDU instance
506 :param revision int: The revision to revert to. If omitted, rolls back
507 the previous upgrade.
509 :return: If successful, returns the revision of active KDU instance,
510 or raises an exception
512 raise MethodNotImplemented()
520 namespace
: str = None,
523 """Uninstall a KDU instance
525 :param cluster_uuid str: The UUID of the cluster
526 :param kdu_instance str: The unique name of the KDU instance
527 :param namespace str: The namespace (model) where the Bundle was deployed
528 :param kwargs: Additional parameters
531 :return: Returns True if successful, or raises an exception
533 model_name
= self
._obtain
_namespace
(
534 kdu_instance
=kdu_instance
, namespace
=namespace
537 self
.log
.debug(f
"[uninstall] Destroying model: {model_name}")
539 will_not_delete
= False
540 if model_name
not in self
.uninstall_locks
:
541 self
.uninstall_locks
[model_name
] = asyncio
.Lock(loop
=self
.loop
)
542 delete_lock
= self
.uninstall_locks
[model_name
]
544 while delete_lock
.locked():
545 will_not_delete
= True
546 await asyncio
.sleep(0.1)
549 self
.log
.info("Model {} deleted by another worker.".format(model_name
))
553 async with delete_lock
:
554 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
556 await libjuju
.destroy_model(model_name
, total_timeout
=3600)
558 self
.uninstall_locks
.pop(model_name
)
560 self
.log
.debug(f
"[uninstall] Model {model_name} destroyed")
563 async def upgrade_charm(
567 charm_id
: str = None,
568 charm_type
: str = None,
569 timeout
: float = None,
571 """This method upgrade charms in VNFs
574 ee_id: Execution environment id
575 path: Local path to the charm
577 charm_type: Charm type can be lxc-proxy-charm, native-charm or k8s-proxy-charm
578 timeout: (Float) Timeout for the ns update operation
581 The output of the update operation if status equals to "completed"
584 "KDUs deployed with Juju Bundle do not support charm upgrade"
587 async def exec_primitive(
589 cluster_uuid
: str = None,
590 kdu_instance
: str = None,
591 primitive_name
: str = None,
592 timeout
: float = 300,
594 db_dict
: dict = None,
595 namespace
: str = None,
598 """Exec primitive (Juju action)
600 :param cluster_uuid str: The UUID of the cluster
601 :param kdu_instance str: The unique name of the KDU instance
602 :param primitive_name: Name of action that will be executed
603 :param timeout: Timeout for action execution
604 :param params: Dictionary of all the parameters needed for the action
605 :param db_dict: Dictionary for any additional data
606 :param namespace str: The namespace (model) where the Bundle was deployed
607 :param kwargs: Additional parameters
610 :return: Returns the output of the action
612 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
614 namespace
= self
._obtain
_namespace
(
615 kdu_instance
=kdu_instance
, namespace
=namespace
618 if not params
or "application-name" not in params
:
620 "Missing application-name argument, \
621 argument needed for K8s actions"
625 "[exec_primitive] Getting model "
626 "{} for the kdu_instance: {}".format(namespace
, kdu_instance
)
628 application_name
= params
["application-name"]
629 actions
= await libjuju
.get_actions(
630 application_name
=application_name
, model_name
=namespace
632 if primitive_name
not in actions
:
633 raise K8sException("Primitive {} not found".format(primitive_name
))
634 output
, status
= await libjuju
.execute_action(
635 application_name
=application_name
,
636 model_name
=namespace
,
637 action_name
=primitive_name
,
641 if status
!= "completed":
643 "status is not completed: {} output: {}".format(status
, output
)
645 if self
.on_update_db
:
646 await self
.on_update_db(
647 cluster_uuid
=cluster_uuid
,
648 kdu_instance
=kdu_instance
,
649 filter=db_dict
["filter"],
654 except Exception as e
:
655 error_msg
= "Error executing primitive {}: {}".format(primitive_name
, e
)
656 self
.log
.error(error_msg
)
657 raise K8sException(message
=error_msg
)
661 async def inspect_kdu(
667 Inspects a bundle and returns a dictionary of config parameters and
668 their default values.
670 :param kdu_model str: The name or path of the bundle to inspect.
672 :return: If successful, returns a dictionary of available parameters
673 and their default values.
677 if not os
.path
.exists(kdu_model
):
678 raise K8sException("file {} not found".format(kdu_model
))
680 with
open(kdu_model
, "r") as f
:
681 bundle
= yaml
.safe_load(f
.read())
685 'description': 'Test bundle',
686 'bundle': 'kubernetes',
689 'charm': 'cs:~charmed-osm/mariadb-k8s-20',
692 'password': 'manopw',
693 'root_password': 'osm4u',
696 'series': 'kubernetes'
701 # TODO: This should be returned in an agreed-upon format
702 kdu
= bundle
["applications"]
712 If available, returns the README of the bundle.
714 :param kdu_model str: The name or path of a bundle
716 :return: If found, returns the contents of the README.
720 files
= ["README", "README.txt", "README.md"]
721 path
= os
.path
.dirname(kdu_model
)
722 for file in os
.listdir(path
):
724 with
open(file, "r") as f
:
730 async def status_kdu(
734 complete_status
: bool = False,
735 yaml_format
: bool = False,
736 namespace
: str = None,
738 ) -> Union
[str, dict]:
739 """Get the status of the KDU
741 Get the current status of the KDU instance.
743 :param cluster_uuid str: The UUID of the cluster
744 :param kdu_instance str: The unique id of the KDU instance
745 :param complete_status: To get the complete_status of the KDU
746 :param yaml_format: To get the status in proper format for NSR record
747 :param namespace str: The namespace (model) where the Bundle was deployed
748 :param: kwargs: Additional parameters
751 :return: Returns a dictionary containing namespace, state, resources,
752 and deployment_time and returns complete_status if complete_status is True
754 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
757 model_name
= self
._obtain
_namespace
(
758 kdu_instance
=kdu_instance
, namespace
=namespace
760 model_status
= await libjuju
.get_model_status(model_name
=model_name
)
762 if not complete_status
:
763 for name
in model_status
.applications
:
764 application
= model_status
.applications
[name
]
765 status
[name
] = {"status": application
["status"]["status"]}
768 return obj_to_yaml(model_status
)
770 return obj_to_dict(model_status
)
774 async def add_relation(
776 provider
: RelationEndpoint
,
777 requirer
: RelationEndpoint
,
780 Add relation between two charmed endpoints
782 :param: provider: Provider relation endpoint
783 :param: requirer: Requirer relation endpoint
785 self
.log
.debug(f
"adding new relation between {provider} and {requirer}")
786 cross_model_relation
= (
787 provider
.model_name
!= requirer
.model_name
788 or requirer
.vca_id
!= requirer
.vca_id
791 if cross_model_relation
:
792 # Cross-model relation
793 provider_libjuju
= await self
._get
_libjuju
(provider
.vca_id
)
794 requirer_libjuju
= await self
._get
_libjuju
(requirer
.vca_id
)
795 offer
= await provider_libjuju
.offer(provider
)
797 saas_name
= await requirer_libjuju
.consume(
798 requirer
.model_name
, offer
, provider_libjuju
800 await requirer_libjuju
.add_relation(
807 vca_id
= provider
.vca_id
808 model
= provider
.model_name
809 libjuju
= await self
._get
_libjuju
(vca_id
)
810 # add juju relations between two applications
811 await libjuju
.add_relation(
813 endpoint_1
=provider
.endpoint
,
814 endpoint_2
=requirer
.endpoint
,
816 except Exception as e
:
817 message
= f
"Error adding relation between {provider} and {requirer}: {e}"
818 self
.log
.error(message
)
819 raise Exception(message
=message
)
821 async def update_vca_status(
822 self
, vcastatus
: dict, kdu_instance
: str, namespace
: str = None, **kwargs
825 Add all configs, actions, executed actions of all applications in a model to vcastatus dict
827 :param vcastatus dict: dict containing vcastatus
828 :param kdu_instance str: The unique id of the KDU instance
829 :param namespace str: The namespace (model) where the Bundle was deployed
830 :param: kwargs: Additional parameters
836 model_name
= self
._obtain
_namespace
(
837 kdu_instance
=kdu_instance
, namespace
=namespace
840 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
842 for vca_model_name
in vcastatus
:
843 # Adding executed actions
844 vcastatus
[vca_model_name
][
846 ] = await libjuju
.get_executed_actions(model_name
=model_name
)
848 for application
in vcastatus
[vca_model_name
]["applications"]:
849 # Adding application actions
850 vcastatus
[vca_model_name
]["applications"][application
][
853 # Adding application configs
854 vcastatus
[vca_model_name
]["applications"][application
][
856 ] = await libjuju
.get_application_configs(
857 model_name
=model_name
, application_name
=application
860 except Exception as e
:
861 self
.log
.debug("Error in updating vca status: {}".format(str(e
)))
863 async def get_services(
864 self
, cluster_uuid
: str, kdu_instance
: str, namespace
: str
866 """Return a list of services of a kdu_instance"""
868 namespace
= self
._obtain
_namespace
(
869 kdu_instance
=kdu_instance
, namespace
=namespace
872 credentials
= self
.get_credentials(cluster_uuid
=cluster_uuid
)
873 kubectl
= self
._get
_kubectl
(credentials
)
874 return kubectl
.get_services(
875 field_selector
="metadata.namespace={}".format(namespace
)
878 async def get_service(
879 self
, cluster_uuid
: str, service_name
: str, namespace
: str
881 """Return data for a specific service inside a namespace"""
883 credentials
= self
.get_credentials(cluster_uuid
=cluster_uuid
)
884 kubectl
= self
._get
_kubectl
(credentials
)
885 return kubectl
.get_services(
886 field_selector
="metadata.name={},metadata.namespace={}".format(
887 service_name
, namespace
891 def get_credentials(self
, cluster_uuid
: str) -> str:
893 Get Cluster Kubeconfig
895 k8scluster
= self
.db
.get_one(
896 "k8sclusters", q_filter
={"_id": cluster_uuid
}, fail_on_empty
=False
899 self
.db
.encrypt_decrypt_fields(
900 k8scluster
.get("credentials"),
902 ["password", "secret"],
903 schema_version
=k8scluster
["schema_version"],
904 salt
=k8scluster
["_id"],
907 return yaml
.safe_dump(k8scluster
.get("credentials"))
909 def _get_credential_name(self
, cluster_uuid
: str) -> str:
911 Get credential name for a k8s cloud
913 We cannot use the cluster_uuid for the credential name directly,
914 because it cannot start with a number, it must start with a letter.
915 Therefore, the k8s cloud credential name will be "cred-" followed
918 :param: cluster_uuid: Cluster UUID of the kubernetes cloud (=cloud_name)
920 :return: Name to use for the credential name.
922 return "cred-{}".format(cluster_uuid
)
928 """Get the namespace UUID
929 Gets the namespace's unique name
931 :param cluster_uuid str: The UUID of the cluster
932 :returns: The namespace UUID, or raises an exception
937 def generate_kdu_instance_name(**kwargs
):
938 db_dict
= kwargs
.get("db_dict")
939 kdu_name
= kwargs
.get("kdu_name", None)
941 kdu_instance
= "{}-{}".format(kdu_name
, db_dict
["filter"]["_id"])
943 kdu_instance
= db_dict
["filter"]["_id"]
946 async def _get_libjuju(self
, vca_id
: str = None) -> Libjuju
:
950 :param: vca_id: VCA ID
951 If None, get a libjuju object with a Connection to the default VCA
952 Else, geta libjuju object with a Connection to the specified VCA
955 while self
.loading_libjuju
.locked():
956 await asyncio
.sleep(0.1)
958 async with self
.loading_libjuju
:
959 vca_connection
= await get_connection(self
._store
)
960 self
.libjuju
= Libjuju(vca_connection
, loop
=self
.loop
, log
=self
.log
)
963 vca_connection
= await get_connection(self
._store
, vca_id
)
971 def _get_kubectl(self
, credentials
: str) -> Kubectl
:
975 :param: kubeconfig_credentials: Kubeconfig credentials
977 kubecfg
= tempfile
.NamedTemporaryFile()
978 with
open(kubecfg
.name
, "w") as kubecfg_file
:
979 kubecfg_file
.write(credentials
)
980 return Kubectl(config_file
=kubecfg
.name
)
982 def _obtain_namespace(self
, kdu_instance
: str, namespace
: str = None) -> str:
984 Obtain the namespace/model name to use in the instantiation of a Juju Bundle in K8s. The default namespace is
985 the kdu_instance name. However, if the user passes the namespace where he wants to deploy the bundle,
986 that namespace will be used.
988 :param kdu_instance: the default KDU instance name
989 :param namespace: the namespace passed by the User
992 # deault the namespace/model name to the kdu_instance name TODO -> this should be the real return... But
993 # once the namespace is not passed in most methods, I had to do this in another way. But I think this should
994 # be the procedure in the future return namespace if namespace else kdu_instance
996 # TODO -> has referred above, this should be avoided in the future, this is temporary, in order to avoid
997 # compatibility issues
1001 else self
._obtain
_namespace
_from
_db
(kdu_instance
=kdu_instance
)
1004 def _obtain_namespace_from_db(self
, kdu_instance
: str) -> str:
1005 db_nsrs
= self
.db
.get_one(
1006 table
="nsrs", q_filter
={"_admin.deployed.K8s.kdu-instance": kdu_instance
}
1008 for k8s
in db_nsrs
["_admin"]["deployed"]["K8s"]:
1009 if k8s
.get("kdu-instance") == kdu_instance
:
1010 return k8s
.get("namespace")