def __init__(self, provider, args, render_args, log): self.args = args self.log = log if 'render' in self.args: num_warnings = 0 for src_dir in self.args.src_dirs: src_dir = os.path.realpath(src_dir) name = self.args.deployment_name or os.path.basename(src_dir) build_dir = Path(self.args.build_dir).joinpath(self.args.provider) if not os.path.isdir(src_dir): self.log.warning(colors.orange(f'"{src_dir}" is not a directory, skip')) num_warnings += 1 continue try: renderer = provider.renderer( name=name, src_dir=src_dir, build_dir=build_dir, namespaces_dir=self.args.namespaces_dir, defaults_path=self.args.defaults_path, args=self.args, log=self.log, **vars(provider.renderer.get_parser().parse_args(render_args))) self.log.info( colors.green_bold('Rendering ') + colors.bold(src_dir) + ' in ' + colors.bold(self.args.build_dir) + ' using the provider ' + colors.bold(self.args.provider) ) renderer.run() # Clean and store secret info in build dir. # Note that this affects only secrets that have been registered in the previous # rendering. Secrets from deployments excluded by user filters are not stored and existing secrets # won't be removed. So any testing/deployment should also explicitly respect the user filters to # exclude secrets as well. Secret.clean_build_secrets(build_dir) for secret in Secret.get_registered(): secret.store(build_dir) except RenderError as e: self.log.error(colors.red(f'Render error in source directory "{src_dir}":')) self.log.error(colors.red_bold(str(e))) sys.exit(1) if num_warnings > 0: self.log.warning(colors.orange(f'Rendering finished with {num_warnings} warnings')) else: self.log.info(colors.green_bold(f'Rendering finished')) sys.exit(0)
def get_log_format(args, loglevel): if args.logfile: return '%(asctime)s %(levelname)-8s %(name)s %(message)s' if loglevel == logging.DEBUG: return '%(levelname)-8s ' + colors.bold('%(name)s') + ' %(message)s' return colors.bold('%(name)s') + ' %(message)s'
def __init__(self, provider, args, deploy_args, log): self.args = args self.log = log if 'deploy' in self.args: num_warnings = 0 for src_dir in self.args.src_dirs: src_dir = os.path.realpath(src_dir) name = self.args.deployment_name or os.path.basename(src_dir) build_dir = Path(self.args.build_dir).joinpath(self.args.provider) if not os.path.isdir(src_dir): self.log.warning(colors.orange(f'"{src_dir}" is not a directory, skip')) num_warnings += 1 continue try: deployer = provider.deployer( name=name, src_dir=src_dir, build_dir=build_dir, namespaces_dir=self.args.namespaces_dir, defaults_path=self.args.defaults_path, args=self.args, log=self.log, **vars(provider.deployer.get_parser().parse_args(deploy_args))) self.log.info( colors.green_bold('Deploying ') + colors.bold(src_dir) + ' in ' + colors.bold(self.args.build_dir) + ' using the provider ' + colors.bold(self.args.provider) ) # Create secrets secrets = [] # Respect user filters for secret in Secret.get_stored(build_dir, name): deployment = secret.deployment if deployment.skipped(self.args): self.log.info(f'... Secret "{colors.blue(secret.name)}" for ' f'deployment "{colors.blue(deployment)}" skipped by user filter.') continue secrets.append(secret) secret.deploy(self.log, self.args.recreate_secrets) # Remove unused secrets Secret.clean_all(secrets, self.log, dry_run=False) # Do the deployments deployer.run() except DeployError as e: self.log.error(colors.red(f'Deployment failed in source directory "{src_dir}":')) self.log.error(colors.red_bold(str(e))) sys.exit(1) if num_warnings > 0: self.log.warning(colors.orange(f'Deployment finished with {num_warnings} warnings')) else: self.log.info(colors.green_bold(f'Deployment finished')) sys.exit(0)
def setup_parser(): parser = argparse.ArgumentParser(description='An awesome universal deployment tool for k8s', usage=colors.bold( f'adeploy -p {colors.blue("provider")} {colors.gray("[optional args ...]")} ' f'{colors.blue("build-step")} {colors.gray("[optional build args ...]")} ' f'{colors.blue("src_dir")} [{colors.blue("src_dir")} ...]')) parser.add_argument('-l', '--log', dest='logfile', help='Path to logfile') parser.add_argument('-d', '--debug', action='store_true') parser.add_argument('-s', '--skip-colors', dest='skip_colors', action='store_true') parser.add_argument('-p', '--provider', dest='provider', help='The provider to use, type --providers to get a list of supported providers.', required='--providers' not in sys.argv and '--version' not in sys.argv) parser.add_argument('--providers', dest='list_providers', action='store_true', help='A list of supported providers') parser.add_argument('-n', '--name', dest="deployment_name", default=None, help='Specify a deployment name. This will overwrite deployment name derived from repo dir') parser.add_argument('--adeploy-dir', dest='adeploy_dir', help='Configuration directory for adeploy', default=Path.home().joinpath('.adeploy'), metavar='adeploy_dir', type=Path) parser.add_argument('--build-dir', dest='build_dir', help='Build directory for output', default='./build', metavar='build_dir') parser.add_argument('--defaults', dest='defaults_path', default='defaults.yml', help='YML file or directory containing <deployment_name>.yml with default variables. ' 'Relative to source dirs.') parser.add_argument('--namespaces', dest='namespaces_dir', default='namespaces', help='Directory containing namespaces and variables for deployments') parser.add_argument('--recreate-secrets', dest='recreate_secrets', action='store_true', help='Force to re-create secrets. This might invoke a password store to retrieve secrets.') parser.add_argument('--filter-namespace', dest='filters_namespace', nargs='+', action='append', help='Only include specified namespace. Argument can be specified multiple times.') parser.add_argument('--filter-release', dest='filters_release', nargs='+', action='append', help='Only include specified deployment release i.e. "prod", "testing". ' 'Argument can be specified multiple times.') parser.add_argument('--show-configs', dest='show_configs', action='store_true', default=False, help='Print out rendered namespace configurations and quit.') parser.add_argument('--gopass-repo', dest='gopass_repo', nargs='+', action='append', help='Gopass repo names, where the awesome secrets are stored. This argument can be specified ' 'multiple times for multiple Gpoass repos. This params can also be specified by the env ' 'var ADEPLOY_GOPASS_REPOS where comma separated list of Gopass repo names is expected. ' 'If args are specified, these take precedence and the env var is ignored.') parser.add_argument('--version', action='store_true', help='Print version and exit') subparsers = parser.add_subparsers(title=f'Available build steps', metavar=colors.bold('build-steps')) for (module, class_name) in get_submodules(steps): module_name = module.__name__ subparser = subparsers.add_parser(module_name, help=f'Call module "{module_name}", ' f'type: {sys.argv[0]} {module_name} --help for more options') subparser.add_argument("src_dirs", help="Directory containing deployment sources", nargs='*', default='.', metavar='src_dir') if module_name == 'config': subparser.add_argument("-o,--out", dest='config_out', default="", help="Filename to store the rendered namespace configurations as JSON.") subparser.add_argument(f'--{module_name}', default=True, help=argparse.SUPPRESS) return parser