diff --git a/stx/lib/stx/config.py b/stx/lib/stx/config.py index 70e444bd5..aa76fd16b 100644 --- a/stx/lib/stx/config.py +++ b/stx/lib/stx/config.py @@ -64,7 +64,7 @@ class Config: """Construct an empty instance; must call "load" explicitly before using""" self.prjdir = require_env('PRJDIR') self.config_filename = os.path.join(self.prjdir, 'stx.conf') - self.use_minikube = os.getenv('STX_PLATFORM', 'minikube') == 'minikube' + self._use_minikube = os.getenv('STX_PLATFORM', 'minikube') == 'minikube' if self.use_minikube: self.minikube_profile = require_env('MINIKUBENAME') else: @@ -74,6 +74,7 @@ class Config: self.docker_tag = require_env('DOCKER_TAG_LOCAL') self.kubectl_cmd = None self.helm_cmd = None + self.minikube_docker_cmd = None reg_list_str = os.getenv('STX_INSECURE_DOCKER_REGISTRIES') if reg_list_str: @@ -115,6 +116,15 @@ class Config: def all_container_names(self): return ALL_CONTAINER_NAMES + [] + def require_minikube(self): + if not self.use_minikube: + raise RuntimeError("minikube not in use") + + def minikube_docker(self): + """Returns the command for docker cli connected to minikube's built-in docker demon""" + self.require_minikube() + return self.minikube_docker_cmd + @property def insecure_docker_reg_list(self): """List of insecure docker registries we are allowed to access""" @@ -129,6 +139,10 @@ class Config: def project_name(self): return self.get('project', 'name') + @property + def use_minikube(self): + return self._use_minikube + def _init_kubectl_cmd(self): # helm if self.use_minikube: @@ -144,3 +158,6 @@ class Config: if self.k8s_namespace: self.kubectl_cmd += f' -n {self.k8s_namespace}' self.helm_cmd += f' -n {self.k8s_namespace}' + # minikube_docker + if self.use_minikube: + self.minikube_docker_cmd = f'eval `minikube -p {self.minikube_profile} docker-env` && docker' diff --git a/stx/lib/stx/stx_cleanup.py b/stx/lib/stx/stx_cleanup.py new file mode 100644 index 000000000..be9af6c96 --- /dev/null +++ b/stx/lib/stx/stx_cleanup.py @@ -0,0 +1,121 @@ +# Copyright (c) 2021 Wind River Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +from stx import k8s +from stx import utils +import subprocess +import traceback + +logger = logging.getLogger('STX-Cleanup') +utils.set_logger(logger) + + +class HandleCleanupTask: + + def __init__(self, config, shell): + self.config = config + self.k8s = k8s.KubeHelper(config) + self.shell = shell + self.logger = logger + + def cleanup_docker(self, args): + self.logger.info("Cleaning up docker") + cmd = "docker system prune --volumes" + if args.force: + cmd += " --force" + shell_cmd = self.shell.create_shell_command('docker', cmd, no_tty=False) + if not args.dry_run: + self.logger.debug('Running command: %s', shell_cmd) + subprocess.run(shell_cmd, shell=True, check=True) + else: + self.logger.debug('Would run command: %s', shell_cmd) + + def cleanup_minikube(self, args): + self.logger.info("Cleaning up minikube docker") + cmd = self.config.minikube_docker() + " system prune --volumes" + if args.force: + cmd += " --force" + shell_cmd = cmd + if not args.dry_run: + self.logger.debug('Running command: %s', shell_cmd) + subprocess.run(shell_cmd, shell=True, check=True) + else: + self.logger.debug('Would run command: %s', shell_cmd) + + def cleanup_default(self, args): + + failed = False + + # cleanup docker pod + try: + if self.__container_is_running('docker'): + self.cleanup_docker(args) + else: + self.logger.info('docker container not running, skipping clean up') + except subprocess.CalledProcessError: + traceback.print_exc() + failed = True + + # cleanup minikube's docker + try: + if self.config.use_minikube: + self.cleanup_minikube(args) + else: + self.logger.info('minikube not in use, skipping minikube clean up') + except subprocess.CalledProcessError: + traceback.print_exc() + failed = True + + # multiple errors + if failed: + raise RuntimeError("one or more cleanup commands failed") + + def __container_is_running(self, name): + return bool(self.k8s.get_pod_name(name)) + + def __add_common_args(self, p): + p.add_argument( + '-f', '--force', help="Don't prompt for confirmation", + action='store_const', const=True) + p.add_argument( + '-n', '--dry-run', help="Log shell commands without executing them", + action='store_const', const=True) + + def add_parser(self, subparsers): + + # stx cleanup + cleanup_parser = subparsers.add_parser( + 'cleanup', + help='Cleanup various things', + usage='stx cleanup [--force] [--dry-run] [docker|minikube]', + epilog='With no arguments clean everything' + ) + self.__add_common_args(cleanup_parser) + cleanup_parser.set_defaults(handle=self.cleanup_default) + cleanup_subparsers = cleanup_parser.add_subparsers() + + # stx cleanup docker + cleanup_docker_parser = cleanup_subparsers.add_parser( + 'docker', + help='Delete cache & orphans in builder docker demon') + self.__add_common_args(cleanup_docker_parser) + cleanup_docker_parser.set_defaults(handle=self.cleanup_docker) + + # stx cleanup minikube + cleanup_minikube_parser = cleanup_subparsers.add_parser( + 'minikube', + help='Delete cache & orphans in minikube\'s docker demon') + self.__add_common_args(cleanup_minikube_parser) + cleanup_minikube_parser.set_defaults(handle=self.cleanup_minikube) diff --git a/stx/lib/stx/stx_main.py b/stx/lib/stx/stx_main.py index 9b25946f4..98457af3b 100644 --- a/stx/lib/stx/stx_main.py +++ b/stx/lib/stx/stx_main.py @@ -17,6 +17,7 @@ import logging from stx import config from stx import stx_build # pylint: disable=E0611 +from stx import stx_cleanup # pylint: disable=E0611 from stx import stx_configparser # pylint: disable=E0611 from stx import stx_control # pylint: disable=E0611 from stx import stx_repomgr # pylint: disable=E0611 @@ -41,6 +42,7 @@ class CommandLine: self.handlebuild = stx_build.HandleBuildTask(self.config) self.handlerepomgr = stx_repomgr.HandleRepomgrTask(self.config) self.handleshell = stx_shell.HandleShellTask(self.config) + self.handlecleanup = stx_cleanup.HandleCleanupTask(self.config, self.handleshell) self.parser = self.parseCommandLine() def parseCommandLine(self): @@ -172,6 +174,9 @@ remove_repo|search_pkg|upload_pkg|delete_pkg ]') help='Container name (default: builder)') shell_subparser.set_defaults(handle=self.handleshell.cmd_shell) + # stx cleanup + self.handlecleanup.add_parser(subparsers) + # common args parser.add_argument('-d', '--debug', help='Enable debug output\n\n',