コード例 #1
0
    def get_certification_issuer(self, track: str) -> Optional[str]:
        logger.info(icon=f"{self.ICON} 🏵️️",
                    title="Checking certification issuer",
                    end="")

        raise_exception = False
        if settings.K8S_CLUSTER_ISSUER:
            cert_issuer: str = settings.K8S_CLUSTER_ISSUER
            logger.info(message=" (settings): ", end="")
            raise_exception = True
        else:
            cert_issuer = f"certificate-letsencrypt-{track}"
            logger.info(message=" (track): ", end="")

        os_command = ["kubectl", "get", "clusterissuer", cert_issuer]
        result = run_os_command(os_command, shell=True)
        if not result.return_code:
            logger.success(message=cert_issuer)
            return cert_issuer
        else:
            error_message = f'No issuer "{cert_issuer}" found, using cluster defaults'
            if raise_exception:
                logger.error(message=error_message, raise_exception=True)
            else:
                logger.info(message=error_message)
            return None
コード例 #2
0
 def update_repos(self) -> None:
     logger.info(icon=f"{self.ICON}  🔄", title="Updating Helm repos: ", end="")
     result = run_os_command(["helm", "repo", "update"])
     if not result.return_code:
         logger.success()
     else:
         logger.std(result, raise_exception=True)
コード例 #3
0
    def delete(
        self,
        resource: str,
        name: Optional[str] = None,
        labels: Optional[Dict[str, str]] = None,
        namespace: str = settings.K8S_NAMESPACE,
    ) -> None:
        os_command = [
            "kubectl",
            "delete",
            resource,
            "--ignore-not-found",
            "--wait=true",
            f"--namespace={namespace}",
        ]

        logger.info(icon=f"{self.ICON}  🗑️ ",
                    title=f"Removing {resource}",
                    end="")
        if labels:
            labels_str = self.labels_to_string(labels)
            os_command += ["-l", labels_str]
            logger.info(title=f" with labels {labels_str}", end="")
        if name:
            os_command += [name]
            logger.info(title=f" with name '{name}'", end="")
        logger.info(": ", end="")
        result = run_os_command(os_command, shell=True)
        if not result.return_code:
            logger.success()
        else:
            logger.std(result, raise_exception=True)
コード例 #4
0
ファイル: git.py プロジェクト: joonvena/kolga
    def update_submodules(self, depth: int = 0, jobs: int = 0) -> None:
        """
        Update all submodules

        Returns:
            None
        """
        os_command = [
            "git",
            "submodule",
            "update",
            "--init",
            "--recursive",
        ]

        if depth:
            os_command += ["--depth", f"{depth}"]

        if jobs:
            os_command += ["--jobs", f"{jobs}"]

        logger.info(icon=f"{self.ICON} 🌱", title="Updating submodules: ", end="")

        result = run_os_command(os_command)
        if result.return_code:
            logger.std(result, raise_exception=True)
        logger.success()
コード例 #5
0
 def remove_repo(self, repo_name: str) -> None:
     logger.info(
         icon=f"{self.ICON}  âž–",
         title=f"Removing Helm repo {repo_name}: ",
         end="",
     )
     result = run_os_command(["helm", "repo", "remove", repo_name])
     if not result.return_code:
         logger.success()
     else:
         logger.std(result, raise_exception=True)
コード例 #6
0
    def delete_image(self, image: DockerImage) -> None:
        logger.warning(icon=f"{self.ICON}", message="Removing Docker image")
        for tag in image.tags:
            logger.info(message=f"\t {image.repository}:{tag}: ", end="")
            delete_command = ["docker", "rmi", f"{image.repository}:{tag}"]
            result = run_os_command(delete_command, shell=False)

            if result.return_code:
                logger.std(result, raise_exception=False)
            else:
                logger.success()
コード例 #7
0
    def pull_image(self, image: str) -> bool:
        logger.info(icon=f"{self.ICON} ⏬", title=f"Pulling {image}:", end=" ")
        pull_command = ["docker", "pull", image]
        result = run_os_command(pull_command, shell=False)

        if result.return_code:
            logger.std(result, raise_exception=False)
        else:
            logger.success()
            return True
        return False
コード例 #8
0
    def add_repo(self, repo_name: str, repo_url: str, update: bool = True) -> None:
        logger.info(
            icon=f"{self.ICON}  âž•",
            title=f"Adding Helm repo {repo_url} with name {repo_name}: ",
            end="",
        )
        result = run_os_command(["helm", "repo", "add", repo_name, repo_url])
        if not result.return_code:
            logger.success()
        else:
            logger.std(result, raise_exception=True)

        if update:
            self.update_repos()
コード例 #9
0
    def _create_basic_auth_data(
        self,
        basic_auth_users: List[BasicAuthUser] = settings.K8S_INGRESS_BASIC_AUTH
    ) -> Dict[str, str]:
        """
        Create secret data from list of `BasicAuthUser`

        The user credentials from the list of users will be encrypted and added
        to a temporary file using the `htpasswd` tool from Apache. The file is
        then read and base64 encoded (as required by Kubernetes secrets).

        Args:
            basic_auth_users: List of `BasicAuthUser`s

        Returns:
            A dict with the key `auth` and base64 content of a htpasswd file as value
        """
        logger.info(icon=f"{self.ICON}  🔨",
                    title="Generating basic auth data: ",
                    end="")

        if not basic_auth_users:
            return {}

        with tempfile.NamedTemporaryFile() as f:
            passwd_path = Path(f.name)
            for i, user in enumerate(basic_auth_users):
                os_command = ["htpasswd", "-b"]
                if i == 0:
                    os_command.append("-c")
                os_command += [str(passwd_path), user.username, user.password]
                result = run_os_command(os_command)
                if result.return_code:
                    logger.error(
                        message=
                        "The 'htpasswd' command failed to create an entry",
                        raise_exception=True,
                    )
            encoded_file = self._b64_encode_file(passwd_path)

        logger.success()
        logger.info(
            message=
            f"\t {len(settings.K8S_INGRESS_BASIC_AUTH)} users will be added to basic auth"
        )

        return {"auth": encoded_file}
コード例 #10
0
ファイル: kubernetes.py プロジェクト: Hi-Fi/kolga
    def create_namespace(self, namespace: str = settings.K8S_NAMESPACE) -> str:
        """
        Create a Kubernetes namespace

        Args:
            namespace: Name of the namespace to create

        Returns:
            On success, returns the name of the namespace

        Raises:
            ApiException: If the namespace creation fails by other means than a
                          namespace conflict, something that happens if the
                          namespace already exists.
        """
        v1 = k8s_client.CoreV1Api(self.client)
        logger.info(icon=f"{self.ICON}  🔨",
                    title=f"Checking namespace {namespace}: ",
                    end="")

        try:
            v1.read_namespace(name=namespace)
        except ApiException as e:
            self._handle_api_error(e)
            if e.status != 404:
                raise e
        else:
            logger.success()
            return namespace

        v1_metadata = k8s_client.V1ObjectMeta(
            labels=self.get_namespace_labels(),
            name=namespace,
        )
        v1_namespace = k8s_client.V1Namespace(metadata=v1_metadata)
        try:
            v1.create_namespace(v1_namespace)
        except ApiException as e:
            self._handle_api_error(e)
            if e.status != 409:  # Namespace exists
                raise e

        logger.success()

        return namespace
コード例 #11
0
    def setup_buildkit(self, name: str = "kolgabk") -> None:
        setup_command = [
            "docker",
            "buildx",
            "create",
            "--name",
            name,
            "--use",
        ]

        result = run_os_command(setup_command)
        if result.return_code:
            logger.std(result, raise_exception=True)
        else:
            logger.success(
                icon=f"{self.ICON} 🔑",
                message=
                f"New buildx builder instance is set up (Instance name: {name})",
            )
コード例 #12
0
    def create_client(self, track: str) -> k8s_client.ApiClient:
        try:
            kubeconfig, method = settings.setup_kubeconfig(track)
        except NoClusterConfigError as exc:
            logger.error(
                icon=f"{self.ICON}  🔑",
                message="Can't log in to Kubernetes cluster, all auth methods exhausted",
                error=exc,
                raise_exception=True,
            )

        logger.success(
            icon=f"{self.ICON}  🔑", message=f"Using {method} for Kubernetes auth"
        )

        config = k8s_client.Configuration()
        k8s_config.load_kube_config(client_configuration=config, config_file=kubeconfig)

        return k8s_client.ApiClient(configuration=config)
コード例 #13
0
    def get(
        self,
        resource: str,
        name: Optional[str] = None,
        labels: Optional[Dict[str, str]] = None,
        namespace: str = settings.K8S_NAMESPACE,
        raise_exception: bool = True,
    ) -> SubprocessResult:
        os_command = ["kubectl", "get"]

        logger.info(icon=f"{self.ICON}  ℹ️ ", title=f"Getting {resource}", end="")
        os_command += self._resource_command(
            resource=resource, name=name, labels=labels, namespace=namespace
        )
        logger.info(": ", end="")
        result = run_os_command(os_command, shell=True)  # nosec
        if not result.return_code:
            logger.success()
        else:
            logger.std(result, raise_exception=raise_exception)
        return result
コード例 #14
0
    def logs(
        self,
        labels: Optional[Dict[str, str]] = None,
        since_time: Optional[str] = None,
        namespace: str = settings.K8S_NAMESPACE,
        print_result: bool = True,
        raise_exception: bool = True,
    ) -> SubprocessResult:
        os_command = [
            "kubectl",
            "logs",
            f"--namespace={namespace}",
            "--prefix=true",
            "--timestamps=true",
            "--tail=100",
        ]

        logger.info(icon=f"{self.ICON}  📋️️ ",
                    title="Getting logs for resource: ",
                    end="")

        if labels:
            labels_str = self.labels_to_string(labels)
            os_command += ["-l", labels_str]
            logger.info(title=f" with labels {labels_str}", end="")

        if since_time:
            os_command += [f"--since-time={since_time}"]
            logger.info(title=f" since {since_time}", end="")

        result = run_os_command(os_command, shell=True)
        if not result.return_code:
            logger.success()
            if print_result:
                logger.std(result)
        else:
            logger.std(result, raise_exception=raise_exception)

        return result
コード例 #15
0
    def create_secret(
        self,
        data: Dict[str, str],
        namespace: str,
        track: str,
        project: Project,
        secret_name: str,
        encode: bool = True,
    ) -> None:
        deploy_name = get_deploy_name(track=track, postfix=project.name)
        v1 = k8s_client.CoreV1Api(self.client)
        v1_metadata = k8s_client.V1ObjectMeta(name=secret_name,
                                              namespace=namespace,
                                              labels={"release": deploy_name})

        if encode:
            encoded_data = self._encode_secret(data)
        else:
            encoded_data = data

        body = k8s_client.V1Secret(data=encoded_data,
                                   metadata=v1_metadata,
                                   type="generic")
        logger.info(
            icon=f"{self.ICON}  🔨",
            title=
            f"Creating secret '{secret_name}' for namespace '{namespace}': ",
            end="",
        )
        try:
            v1.create_namespaced_secret(namespace=namespace, body=body)
        except ApiException:
            try:
                v1.replace_namespaced_secret(name=secret_name,
                                             namespace=namespace,
                                             body=body)
            except ApiException as e:
                self._handle_api_error(e, raise_client_exception=True)
        logger.success()
コード例 #16
0
    def login(
        self,
        username: str = settings.CONTAINER_REGISTRY_USER,
        password: str = settings.CONTAINER_REGISTRY_PASSWORD,
        registry: str = settings.CONTAINER_REGISTRY,
    ) -> None:
        login_command = [
            "docker",
            "login",
            "-u",
            username,
            "-p",
            password,
            registry,
        ]

        result = run_os_command(login_command)
        if result.return_code:
            logger.std(result, raise_exception=True)
        else:
            logger.success(
                icon=f"{self.ICON} 🔑",
                message=f"Logged in to registry (User: {username})",
            )
コード例 #17
0
    def upgrade_chart(
        self,
        name: str,
        values: HelmValues,
        namespace: str,
        chart: str = "",
        chart_path: Optional[Path] = None,
        values_files: Optional[List[Path]] = None,
        install: bool = True,
        version: Optional[str] = None,
        raise_exception: bool = True,
    ) -> SubprocessResult:
        if chart_path:
            if not chart_path.is_absolute():
                chart_path = settings.devops_root_path / chart_path
            if not chart_path.exists():
                logger.error(
                    message=f"Path '{str(chart_path)}' does not exist",
                    error=OSError(),
                    raise_exception=True,
                )
            chart = str(chart_path)

        logger.info(
            icon=f"{self.ICON}  📄",
            title=f"Upgrading chart from '{chart}': ",
            end="",
        )

        replica_timeout_multiplier = 2 if settings.K8S_REPLICACOUNT > 1 else 1
        timeout = (
            (settings.K8S_PROBE_INITIAL_DELAY * replica_timeout_multiplier)
            + (settings.K8S_PROBE_FAILURE_THRESHOLD * settings.K8S_PROBE_PERIOD)
            + 120  # Buffer time
        )

        # Construct initial helm upgrade command
        install_arg = "--install" if install else ""
        helm_command = [
            "helm",
            "upgrade",
            "--atomic",
            "--timeout",
            f"{timeout}s",
            "--history-max",
            "30",
            install_arg,
            "--namespace",
            f"{namespace}",
        ]

        if version:
            helm_command += ["--version", version]

        # Add values files
        if values_files:
            helm_command += self.get_chart_params(flag="--values", values=values_files)

        safe_name = kubernetes_safe_name(name=name)
        values_yaml = yaml.dump(values)

        with NamedTemporaryFile(buffering=0) as fobj:
            fobj.write(values_yaml.encode())
            result = run_os_command(
                [*helm_command, "--values", fobj.name, f"{safe_name}", f"{chart}"],
            )

        if result.return_code:
            logger.std(result, raise_exception=raise_exception)
            return result

        logger.success()
        logger.info(f"\tName: {safe_name} (orig: {name})")
        logger.info(f"\tNamespace: {namespace}")

        return result