github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/acceptancetests/jujupy/k8s_provider/microk8s.py (about) 1 # This file is part of JujuPy, a library for driving the Juju CLI. 2 # Copyright 2013-2019 Canonical Ltd. 3 # 4 # This program is free software: you can redistribute it and/or modify it 5 # under the terms of the Lesser GNU General Public License version 3, as 6 # published by the Free Software Foundation. 7 # 8 # This program is distributed in the hope that it will be useful, but WITHOUT 9 # ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, 10 # SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser 11 # GNU General Public License for more details. 12 # 13 # You should have received a copy of the Lesser GNU General Public License 14 # along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 # Functionality for handling installed or other juju binaries 17 # (including paths etc.) 18 19 20 from __future__ import print_function 21 22 import json 23 import logging 24 import os 25 import shutil 26 from pprint import pformat 27 from time import sleep 28 29 import dns.resolver 30 import yaml 31 32 from jujupy.utility import until_timeout 33 34 from .base import Base, K8sProviderType 35 from .factory import register_provider 36 37 logger = logging.getLogger(__name__) 38 39 40 @register_provider 41 class MicroK8s(Base): 42 43 name = K8sProviderType.MICROK8S 44 cloud_name = 'microk8s' # built-in cloud name 45 46 def __init__(self, bs_manager, cluster_name=None, enable_rbac=False, timeout=1800): 47 super().__init__(bs_manager, cluster_name, enable_rbac, timeout) 48 self.default_storage_class_name = 'microk8s-hostpath' 49 50 def _ensure_cluster_stack(self): 51 pass 52 53 def _tear_down_substrate(self): 54 # No need to tear down microk8s. 55 logger.warn('skip tearing down microk8s') 56 57 def _ensure_kube_dir(self): 58 # choose to use microk8s.kubectl 59 mkubectl = shutil.which('microk8s.kubectl') 60 if mkubectl is None: 61 raise AssertionError("microk8s.kubectl is required!") 62 self.kubectl_path = mkubectl 63 64 # export microk8s.config to kubeconfig file. 65 with open(self.kube_config_path, 'w') as f: 66 kubeconfig_content = self.sh('microk8s.config') 67 logger.debug('writing kubeconfig to %s\n%s', self.kube_config_path, kubeconfig_content) 68 f.write(kubeconfig_content) 69 70 def _ensure_cluster_config(self): 71 self.enable_microk8s_addons() 72 73 def _node_address_getter(self, node): 74 # microk8s uses the node's 'InternalIP`. 75 return [addr['address'] for addr in node['status']['addresses'] if addr['type'] == 'InternalIP'][0] 76 77 def _microk8s_status(self, wait_ready=False, timeout=None): 78 timeout = timeout or 2 * 60 79 args = ['microk8s.status', '--yaml'] 80 if wait_ready: 81 args += ['--wait-ready', '--timeout', timeout] 82 return yaml.load( 83 self.sh(*args), Loader=yaml.Loader, 84 ) 85 86 def enable_microk8s_addons(self, addons=None): 87 # addons are required to be enabled. 88 addons = addons or ['storage', 'dns', 'ingress'] 89 if self.enable_rbac: 90 if 'rbac' not in addons: 91 addons.append('rbac') 92 else: 93 addons = [addon for addon in addons if addon != 'rbac'] 94 logger.info('disabling rbac -> %s', self.sh('microk8s.disable', 'rbac')) 95 96 def wait_until_ready(timeout, checker): 97 for _ in until_timeout(timeout): 98 if checker(): 99 break 100 sleep(5) 101 102 def check_addons(): 103 addons_status = self._microk8s_status(True)['addons'] 104 not_enabled = [ 105 # addon can be like metallb:10.64.140.43-10.64.140.49 106 addon for addon in addons if addons_status.get(addon.split(':')[0]) != 'enabled' 107 ] 108 if len(not_enabled) == 0: 109 logger.info('addons are all ready now -> \n%s', pformat(addons_status)) 110 return True 111 logger.info(f'addons are waiting to be enabled: {", ".join(not_enabled)}...') 112 return False 113 114 out = self.sh('microk8s.enable', *addons) 115 logger.info(out) 116 # wait for a bit to let all addons are fully provisoned. 117 wait_until_ready(300, check_addons)