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)