def signal_handler(self, sig, frame): Prompt.notice("\nCtrl-c captured. Executing teardown function.") if not self.kill_captured: self.kill_captured = True self.cleanup() self.on_sig_kill() sys.exit(0)
def validate(self): with open(self.options['config'], 'r') as f: self.config = json.load(f) self.config['ROOT_DIR'] = os.getcwd() missing = [] for item in self.validation_config: key = item['key'] required = item['required'] key_found = key in self.config or key in os.environ if not self.config.get(key): self.config[key] = os.environ.get(key, "") if (not key_found and required) or (key_found and required and not self.config[key]): missing.append(item) elif not required and not self.config[key]: warning = f"Config missing optional field: {key} - {Colors.WARNING}{item['description']}" if item.get('default-message'): warning += f" - {Colors.CYAN}{item['default-message']}" Prompt.notice(warning) if missing: missing_formatted = [ f"{x['key']}: {x['description']}" for x in missing ] Prompt.error( f"The following keys are missing/empty from your env or config file: {missing_formatted}", close=True) # Prepares build files if provided super().docker_tmp_file_handler(self.config, self.TMP_BUILD_FILES)
def __docker_build_tmp_files_cleanup(self, base_path, file_path, tmp_build): if os.path.isdir(file_path): dest = os.path.join(base_path, f"{os.path.basename(file_path)}.zip") else: dest = os.path.join(base_path, os.path.basename(file_path)) os.remove(dest) Prompt.notice( f"Removed build artifact for image: {tmp_build['image']} - {dest}")
def __docker_build_tmp_files_copy(self, base_path, file_path, tmp_build, config, key): if os.path.isdir(file_path): dest = make_archive( os.path.join(base_path, os.path.basename(file_path)), 'zip', file_path) config[f"BUILD_FILE_{key}"] = f"{os.path.basename(file_path)}.zip" else: dest = os.path.join(base_path, os.path.basename(file_path)) copyfile(file_path, dest) config[f"BUILD_FILE_{key}"] = os.path.basename(dest) Prompt.notice( f"Copied build file for image: {tmp_build['image']} - {dest}")
def check_ports(self): Prompt.notice( f"Checking if ports are available for deployment: {self.REQUIRED_PORTS}" ) import socket ports_in_use = [] for port in self.REQUIRED_PORTS: with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock: if sock.connect_ex(('127.0.0.1', port)) == 0: ports_in_use.append(port) if ports_in_use: Prompt.error( f"Cannot deploy. The following ports are in use: {ports_in_use}", close=True)
def execute(self, cmd, env_dict, display_stdout=True, on_error_fn=None, show_env=False, display_stderr=True): env = os.environ.copy() normalized_dict = {} for key, value in env_dict.items(): if isinstance(value, (list, dict)): value = json.dumps(value) if value is None: value = "" normalized_dict[key] = value env.update(normalized_dict) output = "" Prompt.notice(f"Executing command: {Colors.WARNING}{cmd}") if show_env: Prompt.notice( f"Environment Variables: {json.dumps(env_dict, indent=4, sort_keys=True)}" ) args = dict(stdout=subprocess.PIPE, bufsize=0, env=env, shell=True) if not display_stderr: args.update(dict(stderr=subprocess.DEVNULL)) with subprocess.Popen(cmd, **args) as proc: for line in proc.stdout: formatted = line.rstrip().decode('utf-8', 'ignore') output += formatted if display_stdout: print(formatted) if proc.returncode != 0: if on_error_fn: on_error_fn() Prompt.error( f"[{cmd}] Failed [code:{proc.returncode}]- {proc.stderr}", close=True) return output
def on_complete(self): Prompt.notice( f"Next step: {Colors.CYAN}python run.py deploy --type docker-compose --config configuration.json" )