def delete(self, key): try: del self.struct['data'][key] except KeyError: raise HokusaiError("Cannot unset '%s' as it does not exist" % key)
def setup(project_name, template_remote, template_dir, template_vars, allow_missing_vars): mkpath(os.path.join(CWD, HOKUSAI_CONFIG_DIR)) config.create(clean_string(project_name)) ecr = ECR() if ecr.project_repo_exists(): print_green("Project repo %s already exists. Skipping create." % ecr.project_repo) else: ecr.create_project_repo() print_green("Created project repo %s" % ecr.project_repo) scratch_dir = None if template_remote: scratch_dir = tempfile.mkdtemp() git_repo_and_branch = template_remote.split('#', 1) git_repo = git_repo_and_branch[0] if len(git_repo_and_branch) == 2: git_branch = git_repo_and_branch[1] else: git_branch = "master" shout("git clone -b %s --single-branch %s %s" % (git_branch, git_repo, scratch_dir)) custom_template_dir = None if allow_missing_vars: loader_kwargs = {} else: loader_kwargs = {"undefined": StrictUndefined} if scratch_dir and template_dir: custom_template_dir = os.path.join(scratch_dir, os.path.basename(template_dir)) env = Environment(loader=FileSystemLoader( os.path.join(scratch_dir, os.path.basename(template_dir))), **loader_kwargs) elif scratch_dir: custom_template_dir = scratch_dir env = Environment(loader=FileSystemLoader(scratch_dir), **loader_kwargs) elif template_dir: custom_template_dir = os.path.abspath(template_dir) env = Environment(loader=FileSystemLoader( os.path.abspath(template_dir)), **loader_kwargs) else: try: base_path = sys._MEIPASS env = Environment(loader=FileSystemLoader( os.path.join(base_path, 'hokusai', 'templates'))) except: env = Environment(loader=PackageLoader('hokusai', 'templates')) required_templates = [ 'Dockerfile.j2', '.dockerignore.j2', 'hokusai/build.yml.j2', 'hokusai/development.yml.j2', 'hokusai/test.yml.j2', 'hokusai/staging.yml.j2', 'hokusai/production.yml.j2' ] template_context = { "project_name": config.project_name, "project_repo": ecr.project_repo } for s in template_vars: if '=' not in s: raise HokusaiError( "Error: template variables must be of the form 'key=value'") split = s.split('=', 1) template_context[split[0]] = split[1] try: for template in required_templates: if custom_template_dir and not os.path.isfile( os.path.join(custom_template_dir, template)): raise HokusaiError("Could not find required template file %s" % template) with open(os.path.join(CWD, template.rstrip('.j2')), 'w') as f: f.write(env.get_template(template).render(**template_context)) print_green("Created %s" % template.rstrip('.j2')) if custom_template_dir: for root, _, files in os.walk(custom_template_dir): subpath = os.path.relpath(root, custom_template_dir) if subpath is not '.': mkpath(os.path.join(CWD, subpath)) for file in files: if subpath is not '.': file_path = os.path.join(subpath, file) else: file_path = file if file_path in required_templates: continue if file_path.endswith('.j2'): with open(os.path.join(CWD, file_path.rstrip('.j2')), 'w') as f: f.write( env.get_template(file_path).render( **template_context)) else: copyfile(os.path.join(custom_template_dir, file_path), os.path.join(CWD, file_path)) print_green("Created %s" % file_path.rstrip('.j2')) finally: if scratch_dir: rmtree(scratch_dir)
def run(self, image_tag, cmd, tty=False, env=(), constraint=()): if not self.ecr.project_repo_exists(): raise HokusaiError("Project repo does not exist. Aborting.") if os.environ.get('USER') is not None: uuid = "%s-%s" % (os.environ.get('USER'), k8s_uuid()) else: uuid = k8s_uuid() name = "%s-hokusai-run-%s" % (config.project_name, uuid) image_name = "%s:%s" % (self.ecr.project_repo, image_tag) container = { "args": cmd.split(' '), "name": name, "image": image_name, "imagePullPolicy": "Always", 'envFrom': [{ 'configMapRef': { 'name': "%s-environment" % config.project_name } }] } if tty: container.update({"stdin": True, "stdinOnce": True, "tty": True}) if env: container['env'] = [] for s in env: if '=' not in s: raise HokusaiError( "Error: environment variables must be of the form 'KEY=VALUE'" ) split = s.split('=', 1) container['env'].append({'name': split[0], 'value': split[1]}) spec = {"containers": [container]} if constraint: spec['nodeSelector'] = {} for label in constraint: if '=' not in label: raise HokusaiError( "Error: Node selectors must of the form 'key=value'") split = label.split('=', 1) spec['nodeSelector'][split[0]] = split[1] overrides = {"apiVersion": "v1", "spec": spec} if tty: shout(self.kctl.command( "run %s -t -i --image=%s --restart=Never --overrides='%s' --rm" % (name, image_name, json.dumps(overrides))), print_output=True) else: return returncode( self.kctl.command( "run %s --attach --image=%s --overrides='%s' --restart=Never --rm" % (name, image_name, json.dumps(overrides))))
def project_name(self): project = self.get('project-name') if project is None: raise HokusaiError( "Unconfigured 'project-name'! Plz check ./hokusai/config.yml") return project
def update(self, tag, constraint, git_remote, resolve_tag_sha1=True): if not self.ecr.project_repo_exists(): raise HokusaiError("Project repo does not exist. Aborting.") if resolve_tag_sha1: tag = self.ecr.find_git_sha1_image_tag(tag) if tag is None: raise HokusaiError( "Could not find a git SHA1 for tag %s. Aborting." % tag) if self.namespace is None: print_green("Deploying %s to %s..." % (tag, self.context)) else: print_green("Deploying %s to %s/%s..." % (tag, self.context, self.namespace)) if self.namespace is None: self.ecr.retag(tag, self.context) print_green("Updated tag %s -> %s" % (tag, self.context)) deployment_tag = "%s--%s" % ( self.context, datetime.datetime.utcnow().strftime("%Y-%m-%d--%H-%M-%S")) self.ecr.retag(tag, deployment_tag) print_green("Updated tag %s -> %s" % (tag, deployment_tag)) remote = git_remote or config.git_remote if remote is not None: print_green("Pushing deployment tags to %s..." % remote) shout("git tag -f %s" % self.context, print_output=True) shout("git tag %s" % deployment_tag, print_output=True) shout("git push --force %s --tags" % remote, print_output=True) if config.pre_deploy is not None: print_green("Running pre-deploy hook '%s'..." % config.pre_deploy) return_code = CommandRunner(self.context, namespace=self.namespace).run( tag, config.pre_deploy, constraint=constraint) if return_code: raise HokusaiError( "Pre-deploy hook failed with return code %s" % return_code, return_code=return_code) deployment_timestamp = datetime.datetime.utcnow().strftime("%s%f") for deployment in self.cache: containers = deployment['spec']['template']['spec']['containers'] container_names = [container['name'] for container in containers] deployment_targets = [{ "name": name, "image": "%s:%s" % (self.ecr.project_repo, tag) } for name in container_names] patch = { "spec": { "template": { "metadata": { "labels": { "deploymentTimestamp": deployment_timestamp } }, "spec": { "containers": deployment_targets } } } } print_green("Patching deployment %s..." % deployment['metadata']['name']) shout( self.kctl.command( "patch deployment %s -p '%s'" % (deployment['metadata']['name'], json.dumps(patch)))) print_green("Waiting for rollout to complete...") rollout_commands = [ self.kctl.command("rollout status deployment/%s" % deployment['metadata']['name']) for deployment in self.cache ] return_code = shout_concurrent(rollout_commands) if return_code: raise HokusaiError("Deployment failed!", return_code=return_code) if config.post_deploy is not None: print_green("Running post-deploy hook '%s'..." % config.post_deploy) return_code = CommandRunner(self.context, namespace=self.namespace).run( tag, config.post_deploy, constraint=constraint) if return_code: raise HokusaiError( "Post-deploy hook failed with return code %s" % return_code, return_code=return_code)