def deploy_webhook_dispatcher_landscape( cfg_set, webhook_dispatcher_deployment_cfg: WebhookDispatcherDeploymentConfig, chart_dir: str, deployment_name: str, ): not_empty(deployment_name) chart_dir = os.path.abspath(chart_dir) cfg_factory = global_ctx().cfg_factory() # Set the global context to the cluster specified in KubernetesConfig kubernetes_config_name = webhook_dispatcher_deployment_cfg.kubernetes_config_name( ) kubernetes_config = cfg_factory.kubernetes(kubernetes_config_name) kube_ctx.set_kubecfg(kubernetes_config.kubeconfig()) kubernetes_cfg_name = webhook_dispatcher_deployment_cfg.kubernetes_config_name( ) kubernetes_cfg = cfg_factory.kubernetes(kubernetes_cfg_name) whd_helm_values = create_webhook_dispatcher_helm_values( cfg_set=cfg_set, webhook_dispatcher_deployment_cfg=webhook_dispatcher_deployment_cfg, config_factory=cfg_factory, ) execute_helm_deployment(kubernetes_cfg, deployment_name, chart_dir, deployment_name, whd_helm_values)
def deploy_oauth2_proxy( kubernetes_config: KubernetesConfig, oauth2_proxy_config: Oauth2ProxyConfig, deployment_name: str, ): not_empty(deployment_name) cfg_factory = global_ctx().cfg_factory() kube_ctx.set_kubecfg(kubernetes_config.kubeconfig()) ensure_cluster_version(kubernetes_config) ingress_config = cfg_factory.ingress(oauth2_proxy_config.ingress_config()) helm_values = create_oauth2_proxy_helm_values( oauth2_proxy_config=oauth2_proxy_config, ingress_config=ingress_config, deployment_name=deployment_name, ) execute_helm_deployment( kubernetes_config, oauth2_proxy_config.namespace(), 'stable/oauth2-proxy', deployment_name, helm_values, )
def _cfg_element_names(self, cfg_type_name: str): '''Returns cfg-elements of the given cfg_type Parameters ---------- cfg_type_name: str The cfg type name Returns ------- Iterable[str] Contains the names of all cfg-elements of the given cfg_type known to this ConfigFactory. Raises ------ ValueError If the specified cfg_type is unknown. ''' not_empty(cfg_type_name) self._retrieve_cfg_elements(cfg_type_name=cfg_type_name) self._ensure_type_is_known(cfg_type_name=cfg_type_name) if cfg_type_name in self.raw: return set(self.raw[cfg_type_name].keys()) else: return set()
def _cfg_elements(self, cfg_type_name: str): '''Returns all cfg_elements for the given cfg_type. Parameters ---------- cfg_type_name: str The name of the cfg_type whose instances should be retrieved. Yields ------- NamedModelElement Instance of the given cfg_type. Raises ------ ValueError If the specified cfg_type is unknown. ''' not_empty(cfg_type_name) self._retrieve_cfg_elements(cfg_type_name=cfg_type_name) self._ensure_type_is_known(cfg_type_name=cfg_type_name) for element_name in self._cfg_element_names(cfg_type_name): yield self._cfg_element(cfg_type_name, element_name)
def deploy_tekton_dashboard_ingress( kubernetes_config: KubernetesConfig, tekton_dashboard_ingress_config: TektonDashboardIngressConfig, chart_dir: str, deployment_name: str, ): not_empty(deployment_name) cfg_factory = global_ctx().cfg_factory() chart_dir = os.path.abspath(chart_dir) kube_ctx.set_kubecfg(kubernetes_config.kubeconfig()) ensure_cluster_version(kubernetes_config) ingress_config = cfg_factory.ingress( tekton_dashboard_ingress_config.ingress_config()) helm_values = create_tekton_dashboard_helm_values( tekton_dashboard_ingress_config=tekton_dashboard_ingress_config, ingress_config=ingress_config, ) execute_helm_deployment( kubernetes_config, tekton_dashboard_ingress_config.namespace(), chart_dir, deployment_name, helm_values, )
def deploy_oauth2_proxy( oauth2_proxy_config: Oauth2ProxyConfig, chart_dir: str, deployment_name: str, ): not_empty(deployment_name) cfg_factory = global_ctx().cfg_factory() kubernetes_config = cfg_factory.kubernetes( oauth2_proxy_config.kubernetes_config_name()) kube_ctx.set_kubecfg(kubernetes_config.kubeconfig()) ingress_config = cfg_factory.ingress(oauth2_proxy_config.ingress_config()) helm_values = create_oauth2_proxy_helm_values( oauth2_proxy_config=oauth2_proxy_config, ingress_config=ingress_config, deployment_name=deployment_name, config_factory=cfg_factory, ) execute_helm_deployment( kubernetes_config, oauth2_proxy_config.namespace(), chart_dir, deployment_name, helm_values, )
def deploy_gardenlinux_cache( kubernetes_config: KubernetesConfig, gardenlinux_cache_config: GardenlinuxCacheConfig, chart_dir: str, deployment_name: str, ): not_empty(deployment_name) cfg_factory = global_ctx().cfg_factory() chart_dir = os.path.abspath(chart_dir) kube_ctx.set_kubecfg(kubernetes_config.kubeconfig()) ensure_cluster_version(kubernetes_config) ingress_config = cfg_factory.ingress( gardenlinux_cache_config.ingress_config()) helm_values = create_gardenlinux_cache_helm_values( gardenlinux_cache_config=gardenlinux_cache_config, ingress_config=ingress_config, ) execute_helm_deployment( kubernetes_config, gardenlinux_cache_config.namespace(), chart_dir, deployment_name, helm_values, )
def create_tls_secret(tls_config: TlsConfig, tls_secret_name: str, namespace: str, basic_auth_cred: BasicAuthCred = None): """ Creates a secret with the configured TLS certificates in the K8s cluster. Optionally adds credentials for Basic Authentication""" not_none(tls_config) not_empty(tls_secret_name) not_empty(namespace) ctx = kube_ctx namespace_helper = ctx.namespace_helper() namespace_helper.create_if_absent(namespace) secret_helper = ctx.secret_helper() if not secret_helper.get_secret(tls_secret_name, namespace): data = { 'tls.key': tls_config.private_key(), 'tls.crt': tls_config.certificate(), } if basic_auth_cred: ht = HtpasswdFile() ht.set_password(basic_auth_cred.user, basic_auth_cred.password) data['auth'] = ht.to_string().decode('utf-8') secret_helper.put_secret( name=tls_secret_name, data=data, namespace=namespace, )
def create_image_pull_secret( credentials: GcrCredentials, image_pull_secret_name: str, namespace: str, ): """Create an image pull secret in the K8s cluster to allow pods to download images from gcr""" not_none(credentials) not_empty(image_pull_secret_name) not_empty(namespace) ctx = kube_ctx namespace_helper = ctx.namespace_helper() namespace_helper.create_if_absent(namespace) secret_helper = ctx.secret_helper() if not secret_helper.get_secret(image_pull_secret_name, namespace): secret_helper.create_gcr_secret( namespace=namespace, name=image_pull_secret_name, password=credentials.passwd(), user_name=credentials.username(), email=credentials.email(), server_url=credentials.host(), ) service_account_helper = ctx.service_account_helper() service_account_helper.patch_image_pull_secret_into_service_account( name="default", namespace=namespace, image_pull_secret_name=image_pull_secret_name)
def __init__( self, git_helper: GitHelper, repo_dir: str, release_version: str, repository_version_file_path: str, repository_branch: str, commit_message_prefix: str, publishing_policy: ReleaseCommitPublishingPolicy, release_commit_callback: str = None, ): self.git_helper = not_none(git_helper) self.repository_branch = not_empty(repository_branch) repo_dir_absolute = os.path.abspath(not_empty(repo_dir)) self.repo_dir = repo_dir_absolute self.release_version = not_empty(release_version) self.repository_version_file_path = os.path.join( repo_dir_absolute, repository_version_file_path, ) self.commit_message_prefix = commit_message_prefix self.publishing_policy = publishing_policy if release_commit_callback: self.release_commit_callback = os.path.join( repo_dir_absolute, release_commit_callback, ) else: self.release_commit_callback = None self.head_commit = None # stored while applying - used for revert
def create_basic_auth_secret( secret_name: str, namespace: str, basic_auth_cred: BasicAuthCred = None, ): """ Creates a secret with the configured TLS certificates in the K8s cluster. Optionally adds credentials for Basic Authentication""" not_empty(secret_name) not_empty(namespace) ctx = kube_ctx namespace_helper = ctx.namespace_helper() namespace_helper.create_if_absent(namespace) secret_helper = ctx.secret_helper() if not secret_helper.get_secret(secret_name, namespace): ht = HtpasswdFile() ht.set_password(basic_auth_cred.user, basic_auth_cred.password) data = { 'auth': ht.to_string().decode('utf-8'), } secret_helper.put_secret( name=secret_name, data=data, namespace=namespace, )
def _cfg_element_names(self, cfg_type_name: str): '''Returns cfg-elements of the given cfg_type Parameters ---------- cfg_type_name: str The cfg type name Returns ------- Iterable[str] Contains the names of all cfg-elements of the given cfg_type known to this ConfigFactory. Raises ------ ValueError If the specified cfg_type is unknown. ''' not_empty(cfg_type_name) known_types = self._cfg_types() if cfg_type_name not in known_types: raise ValueError("Unknown config type '{c}'. Known types: {k}".format( c=cfg_type_name, k=', '.join(known_types.keys()), )) if cfg_type_name in self.raw: return set(self.raw[cfg_type_name].keys()) else: return set()
def __init__( self, git_helper: GitHelper, repo_dir: str, release_version: str, repository_version_file_path: str, repository_branch: str, release_commit_message_prefix: str, publishing_policy: ReleaseCommitPublishingPolicy, release_commit_callback_image_reference: str, release_commit_callback: str = None, ): self.git_helper = not_none(git_helper) self.repository_branch = not_empty(repository_branch) self.repo_dir = os.path.abspath(repo_dir) self.release_version = not_empty(release_version) self.repository_version_file_path = os.path.join( self.repo_dir, repository_version_file_path, ) self.release_commit_message_prefix = release_commit_message_prefix self.publishing_policy = publishing_policy self.release_commit_callback_image_reference = release_commit_callback_image_reference self.release_commit_callback = release_commit_callback self.head_commit = None # stored while applying - used for revert
def __init__( self, git_helper: GitHelper, repo_dir: str, release_version: str, repository_version_file_path: str, repository_branch: str, version_operation: str, prerelease_suffix: str, publishing_policy: ReleaseCommitPublishingPolicy, next_cycle_commit_message_prefix: str = None, next_version_callback: str = None, ): self.git_helper = not_none(git_helper) self.repository_branch = not_empty(repository_branch) self.repo_dir = os.path.abspath(repo_dir) self.release_version = not_empty(release_version) self.version_operation = not_empty(version_operation) self.prerelease_suffix = not_empty(prerelease_suffix) self.publishing_policy = publishing_policy self.next_cycle_commit_message_prefix = next_cycle_commit_message_prefix self.repository_version_file_path = os.path.join( self.repo_dir, repository_version_file_path, ) self.next_version_callback = next_version_callback
def create_if_absent(self, namespace: str): '''Create a new namespace iff it does not already exist''' not_empty(namespace) existing_namespace = self.get_namespace(namespace) if not existing_namespace: self.create_namespace(namespace)
def wait_until_deployment_available(self, namespace: str, name: str, timeout_seconds: int = 60): '''Block until the given deployment has at least one available replica (or timeout) Return `True` if the deployment is available, `False` if a timeout occured. ''' not_empty(namespace) not_empty(name) w = watch.Watch() # Work around IncompleteRead errors resulting in ProtocolErrors - no fault of our own start_time = int(time.time()) while (start_time + timeout_seconds) > time.time(): try: for event in w.stream(self.apps_api.list_namespaced_deployment, namespace=namespace, timeout_seconds=timeout_seconds): deployment_spec = event['object'] if deployment_spec is not None: if deployment_spec.metadata.name == name: if deployment_spec.status.available_replicas is not None \ and deployment_spec.status.available_replicas > 0: return True # Check explicitly if timeout occurred if (start_time + timeout_seconds) < time.time(): return False # Regular Watch.stream() timeout occurred, no need for further checks return False except ProtocolError: info('http connection error - ignored')
def create_ingress(self, namespace: str, ingress: V1beta1Ingress): '''Create an ingress in a given namespace. Raises an `ApiException` if such an ingress already exists.''' not_empty(namespace) not_none(ingress) self.extensions_v1beta1_api.create_namespaced_ingress(namespace=namespace, body=ingress)
def create_deployment(self, namespace: str, deployment: V1Deployment): '''Create a deployment in a given namespace. Raises an `ApiException` if such a deployment already exists.''' not_empty(namespace) not_none(deployment) self.apps_api.create_namespaced_deployment(namespace=namespace, body=deployment)
def __init__( self, git_helper: GitHelper, repo_dir: str, release_version: str, repository_version_file_path: str, repository_branch: str, version_operation: str, prerelease_suffix: str, publishing_policy: ReleaseCommitPublishingPolicy, next_version_callback: str = None, ): self.git_helper = not_none(git_helper) self.repository_branch = not_empty(repository_branch) repo_dir_absolute = os.path.abspath(not_empty(repo_dir)) self.repo_dir = repo_dir_absolute self.release_version = not_empty(release_version) self.version_operation = not_empty(version_operation) self.prerelease_suffix = not_empty(prerelease_suffix) self.publishing_policy = publishing_policy self.repository_version_file_path = os.path.join( repo_dir_absolute, repository_version_file_path, ) if next_version_callback: self.next_version_callback = os.path.join( repo_dir_absolute, next_version_callback, ) else: self.next_version_callback = None
def create_service(self, namespace: str, service: V1Service): '''Create a service in a given namespace. Raises an `ApiException` if such a Service already exists. ''' not_empty(namespace) not_none(service) self.core_api.create_namespaced_service(namespace=namespace, body=service)
def create_or_update_config_map(self, namespace: str, name: str, data: dict): not_empty(namespace) not_empty(name) not_none(data) if self.read_config_map(namespace=namespace, name=name): self.replace_config_map(namespace=namespace, name=name, data=data) else: self.create_config_map(namespace=namespace, name=name, data=data)
def _send_mail( email_cfg: EmailConfig, recipients: typing.Iterable[str], mail_template: str, subject: str, replace_tokens: dict={}, cc_recipients: typing.Iterable[str]=[], mimetype='text', ): not_none(email_cfg) not_empty(recipients) not_none(mail_template) not_empty(subject) # create body from template mail_body = mailer.create_body( mail_template=mail_template, replace_tokens=replace_tokens, ) recipients = {r.lower() for r in recipients} cc_recipients = {r.lower() for r in cc_recipients} sender_name = email_cfg.sender_name() # used for the header sender = sender_name # passed as from-address to the mailserver if email_cfg.use_tls(): smtp_server = smtplib.SMTP_SSL(email_cfg.smtp_host()) else: smtp_server = smtplib.SMTP(email_cfg.smtp_host()) if email_cfg.has_credentials(): credentials = email_cfg.credentials() sender = credentials.username() sender_name = email_cfg.sender_name() or sender smtp_server.login(user=credentials.username(), password=credentials.passwd()) # create mail envelope mail = mailer.create_mail( subject=subject, sender=sender_name, recipients=recipients, cc_recipients=cc_recipients, text=mail_body, mimetype=mimetype, ) recipients.update(cc_recipients) mailer.send_mail( smtp_server=smtp_server, msg=mail, sender=sender, recipients=recipients )
def __init__( self, githubrepobranch: GitHubRepoBranch, github_helper: GitHubRepositoryHelper, release_version: str, repo_dir: str, ): self.githubrepobranch = not_none(githubrepobranch) self.github_helper = not_none(github_helper) self.release_version = not_empty(release_version) self.repo_dir = os.path.abspath(not_empty(repo_dir))
def create_config_map(self, namespace: str, name: str, data: dict): not_empty(namespace) not_empty(name) not_none(data) self.core_api.create_namespaced_config_map( namespace=namespace, body=V1ConfigMap( data=data, metadata=V1ObjectMeta(name=name, namespace=namespace), ), )
def read_config_map(self, namespace: str, name: str): '''Return the `V1ConfigMap` with the given name in the given namespace, or `None` if no such config map exists.''' not_empty(namespace) not_empty(name) try: config_map = self.core_api.read_namespaced_config_map(namespace=namespace, name=name) except ApiException as ae: if ae.status == 404: return None raise ae return config_map
def _cfg_elements(self, cfg_type_name: str): '''Returns all container cfg elements of the given cfg_type Raises ------ ValueError If the specified cfg_type is unknown. ''' not_empty(cfg_type_name) for element_name in self._cfg_element_names(cfg_type_name): yield self._cfg_element(cfg_type_name, element_name)
def _send_mail( email_cfg: EmailConfig, recipients: typing.Iterable[str], mail_template: str, subject: str, attachments: typing.Sequence[mail.model.Attachment] = (), replace_tokens: dict = {}, cc_recipients: typing.Iterable[str] = [], mimetype='text', ): not_none(email_cfg) not_empty(recipients) not_none(mail_template) not_empty(subject) # create body from template mail_body = mailer.create_body( mail_template=mail_template, replace_tokens=replace_tokens, ) recipients = {r.lower() for r in recipients} cc_recipients = {r.lower() for r in cc_recipients} sender_name = email_cfg.sender_name() if email_cfg.use_tls(): smtp_server = smtplib.SMTP_SSL(email_cfg.smtp_host()) else: smtp_server = smtplib.SMTP(email_cfg.smtp_host()) if email_cfg.has_credentials(): credentials = email_cfg.credentials() smtp_server.login(user=credentials.username(), password=credentials.passwd()) # create mail envelope mail = mailer.create_mail( subject=subject, sender=sender_name, recipients=recipients, cc_recipients=cc_recipients, text=mail_body, mimetype=mimetype, attachments=attachments, ) recipients.update(cc_recipients) recipients = email_cfg.filter_recipients(recipients) smtp_server.send_message( msg=mail, to_addrs=recipients) # from_addr is taken from header
def test_not_empty(self): result = examinee.not_empty('foo') self.assertEqual('foo', result) forbidden = ['', None, [], ()] for value in forbidden: with capture_out() as (stdout, stderr): with self.assertRaises(Failure): examinee.not_empty(value) self.assertIn('must not be empty', stderr.getvalue().strip()) self.assertTrue(len(stdout.getvalue()) == 0)
def __init__( self, slack_cfg_name: str, slack_channel: str, release_version: str, release_notes: ReleaseNotes, githubrepobranch: GitHubRepoBranch, ): self.slack_cfg_name = not_empty(slack_cfg_name) self.slack_channel = not_empty(slack_channel) self.release_version = not_empty(release_version) self.githubrepobranch = not_none(githubrepobranch) self.release_notes = not_none(release_notes)
def validate(self): version.parse_to_semver(self.release_version) # if a tag with the given release version exists, we cannot create another release # pointing to it if self.github_helper.tag_exists(tag_name=self.release_version): raise RuntimeError( f"Cannot create tag '{self.release_version}' for release: Tag already exists" ) if self.component_descriptor_file_path: existing_file(self.component_descriptor_file_path) with open(self.component_descriptor_file_path) as f: # TODO: Proper validation not_empty(f.read().strip())