Example #1
0
    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)
Example #2
0
    def __init__(
        self,
        repository_url: str,
        cfg_set,
        target_team: str = None,
    ):
        if not '://' in repository_url:
            repository_url = 'x://' + repository_url

        self._repository_url = urlparse(not_none(repository_url))
        self._repo_host = self._repository_url.hostname
        self.cfg_set = not_none(cfg_set)
        self._target_team = target_team
        concourse_cfg = cfg_set.concourse()
        job_mapping_set = cfg_set.job_mapping(
            concourse_cfg.job_mapping_cfg_name())

        org_name, repo_name = self._repository_url.path.lstrip('/').split('/')

        if target_team:
            return  # if tgt-team is explicitly configured, we do not need to look it up

        for job_mapping in job_mapping_set.job_mappings().values():
            for org in job_mapping.github_organisations():
                if org.org_name() != org_name:
                    continue
                if not org.repository_matches(repo_name):
                    continue
                github_cfg = cfg_set.github(org.github_cfg_name())
                if github_cfg.matches_hostname(self._repo_host):
                    self.job_mapping = job_mapping
                    return
        else:
            raise JobMappingNotFoundError(
                f'could not find matching job-mapping for org {org}')
Example #3
0
 def __init__(
     self,
     pipeline_name,
     pipeline_definition,
     main_repo,
     concourse_target_cfg: model.concourse.ConcourseConfig,
     concourse_target_team,
     secret_cfg,
     job_mapping: model.concourse.JobMapping=None,
     override_definitions=[{},],
     exception=None,
     pipeline_definition_committish: str=None,
 ):
     try:
         self.pipeline_name = not_empty(pipeline_name)
         self.pipeline_definition = not_none(pipeline_definition)
         self.main_repo = not_none(main_repo)
         self.concourse_target_cfg = not_none(concourse_target_cfg)
         self.concourse_target_team = not_none(concourse_target_team)
         self.override_definitions = not_none(override_definitions)
         self.secret_cfg = secret_cfg
         self.job_mapping = job_mapping
         self.pipeline_definition_committish = pipeline_definition_committish
     except Exception as e:
         raise ValueError(
             f'{e=} missing value: {pipeline_name=} {pipeline_definition=} {main_repo=} '
             f'{concourse_target_cfg=} {concourse_target_team=} {override_definitions=}'
         )
     self.exception = exception
Example #4
0
    def __init__(
        self,
        repository_url: str,
        cfg_set,
    ):
        self._repository_url = urlparse(not_none(repository_url))
        self._repo_host = self._repository_url.hostname
        self.cfg_set = not_none(cfg_set)
        concourse_cfg = cfg_set.concourse()
        job_mapping_set = cfg_set.job_mapping(
            concourse_cfg.job_mapping_cfg_name())

        org_name, repo_name = self._repository_url.path.lstrip('/').split('/')

        for job_mapping in job_mapping_set.job_mappings().values():
            for org in job_mapping.github_organisations():
                if org.org_name() != org_name:
                    continue
                if not org.repository_matches(repo_name):
                    continue
                github_cfg = cfg_set.github(org.github_cfg_name())
                if github_cfg.matches_hostname(self._repo_host):
                    self.job_mapping = job_mapping
                    return
        else:
            raise JobMappingNotFoundError(
                f'could not find matching job-mapping for org {org}')
Example #5
0
    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)
Example #6
0
    def validate_component_name(name: str):
        not_none(name)

        if len(name) == 0:
            raise InvalidComponentReferenceError(
                'Component name must not be empty')

        # valid component names are fully qualified github repository URLs without a schema
        # (e.g. github.com/example_org/example_name)
        if urllib.parse.urlparse(name).scheme:
            raise InvalidComponentReferenceError(
                'Component name must not contain schema')

        # prepend dummy schema so that urlparse will parse away the hostname
        parsed = urllib.parse.urlparse('dummy://' + name)

        if not parsed.hostname:
            raise InvalidComponentReferenceError(name)

        path_parts = parsed.path.strip('/').split('/')
        if not len(path_parts) == 2:
            raise InvalidComponentReferenceError(
                'Component name must end with github repository path')

        return name
Example #7
0
 def __init__(
     self,
     pipeline_name,
     pipeline_definition,
     main_repo,
     concourse_target_cfg,
     concourse_target_team,
     secret_cfg,
     override_definitions=[
         {},
     ],
     exception=None,
 ):
     try:
         self.pipeline_name = not_empty(pipeline_name)
         self.pipeline_definition = not_none(pipeline_definition)
         self.main_repo = not_none(main_repo)
         self.concourse_target_cfg = not_none(concourse_target_cfg)
         self.concourse_target_team = not_none(concourse_target_team)
         self.override_definitions = not_none(override_definitions)
         self.secret_cfg = secret_cfg
     except Exception as e:
         raise ValueError(
             f'{e=} missing value: {pipeline_name=} {pipeline_definition=} {main_repo=} '
             f'{concourse_target_cfg=} {concourse_target_team=} {override_definitions=}'
         )
     self.exception = exception
Example #8
0
def merge_products(left_product, right_product):
    not_none(left_product)
    not_none(right_product)

    # start with a copy of left_product
    merged = ComponentDescriptor.from_dict(deepcopy(dict(left_product.raw.items())))
    for component in right_product.components():
        existing_component = merged.component(component)
        if existing_component:
            # it is acceptable to add an existing component iff it is identical
            if existing_component.raw == component.raw:
                continue # skip
            else:
                raise ValueError(
                    'conflicting component definitions: {c1}, {c2}'.format(
                        c1=':'.join((existing_component.name(), existing_component.version())),
                        c2=':'.join((component.name(), component.version())),
                    )
                )
        merged.add_component(component)

    # merge overwrites
    for component_overwrite in right_product.component_overwrites():
        # only one overwrite per component is allowed
        for co in left_product.component_overwrites():
            if co.declaring_component == component_overwrite.declaring_component():
                raise ValueError(f'overwrite redefinition: {co}')
        merged._add_component_overwrite(component_overwrite=component_overwrite)

    return merged
Example #9
0
def greatest_references(references: typing.Iterable[DependencyBase]):
    '''
    yields the component references from the specified iterable of ComponentReference that
    have the greates version (grouped by component name).
    Id est: if the sequence contains exactly one version of each contained component name,
    the sequence is returned unchanged.
    '''
    not_none(references)
    references = list(references)
    for ref in references:
        check_type(ref, DependencyBase)

    names = [
        ref.name() for ref
        in references
    ]

    for name in names:
        matching_refs = [r for r in references if r.name() == name]
        if len(matching_refs) == 1:
            # in case reference name was unique, do not bother sorting
            # (this also works around issues from non-semver versions)
            yield matching_refs[0]
            continue

        # there might be multiple component versions of the same name
        # --> use the greatest version in that case
        matching_refs = sorted(
            matching_refs,
            key=lambda r: semver.parse_version_info(r.version()),
        )
        # greates version comes last
        yield matching_refs[-1]
Example #10
0
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)
Example #11
0
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,
        )
Example #12
0
    def add_triage(
        self,
        triage: Triage,
        scope: TriageScope=None,
        product_id=None,
        group_id=None,
        component_version=None,
    ):
        '''
        adds an existing Protecode triage to a specified target. The existing triage is usually
        retrieved from an already uploaded product (which is represented by `AnalysisResult`).
        This method is offered to support "transporting" existing triages.

        Note that - depending on the effective target scope, the `product_id`, `group_id` formal
        parameters are either required or forbidden.

        Note that Protecode will only accept triages for matching (component, vulnerabilities,
        version) tuples. In particular, triages for different component versions will be silently
        ignored. Explicitly pass `component_version` of target protecode app (/product) to force
        Protecode into accepting the given triage.

        @param triage: the triage to "copy"
        @param scope: if given, overrides the triage's scope
        @param product_id: target product_id. required iff scope in FN, FH, R
        @param group_id: target group_id. required iff scope is G(ROUP)
        @param component_version: overwrite target component version
        '''
        # if no scope is set, use the one from passed triage
        scope = scope if scope else triage.scope()

        # depending on the scope, different arguments are required
        if scope == TriageScope.ACCOUNT_WIDE:
            pass
        elif scope in (TriageScope.FILE_NAME, TriageScope.FILE_HASH, TriageScope.RESULT):
            not_none(product_id)
        elif scope == TriageScope.GROUP:
            not_none(group_id)
        else:
            raise NotImplementedError()

        if not component_version:
            component_version = triage.component_version()

        # "copy" data from existing triage
        triage_dict = {
            'component': triage.component_name(),
            'version': component_version,
            'vulns': [triage.vulnerability_id()],
            'scope': triage.scope().value,
            'reason': triage.reason(),
            'description': triage.description(),
        }

        if product_id:
            triage_dict['product_id'] = product_id

        if group_id:
            triage_dict['group_id'] = group_id

        return self.add_triage_raw(triage_dict=triage_dict)
Example #13
0
    def create(name: str, variant_name: str, args_dict: dict):
        if name not in TRAITS:
            raise ModelValidationError('no such trait: ' + str(name))
        not_none(args_dict)

        ctor = TRAITS[name]

        return ctor(name=name, variant_name=variant_name, raw_dict=args_dict)
Example #14
0
    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)
Example #15
0
 def __init__(self, repo, github_cfg, github_repo_path):
     not_none(repo)
     if not isinstance(repo, git.Repo):
         # assume it's a file path if it's not already a git.Repo
         repo = git.Repo(str(repo))
     self.repo = repo
     self.github_cfg = github_cfg
     self.github_repo_path = github_repo_path
Example #16
0
 def __init__(
     self,
     definition_descriptor,
     deploy_status,
     error_details=None,
 ):
     self.definition_descriptor = not_none(definition_descriptor)
     self.deploy_status = not_none(deploy_status)
     self.error_details = error_details
Example #17
0
 def __init__(
     self,
     job_mapping,
     cfg_set,
     repository_filter: callable=None,
 ):
     self.job_mapping = not_none(job_mapping)
     self.cfg_set = not_none(cfg_set)
     self.repository_filter = repository_filter
Example #18
0
    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)
Example #19
0
 def __init__(
     self,
     definition_descriptor: DefinitionDescriptor,
     render_status,
     error_details=None,
 ):
     self.definition_descriptor = not_none(definition_descriptor)
     self.render_status = not_none(render_status)
     self.error_details = error_details
Example #20
0
    def add_triage(self,
                   triage: Triage,
                   scope: TriageScope = None,
                   product_id=None,
                   group_id=None):
        '''
        adds an existing Protecode triage to a specified target. The existing triage is usually
        retrieved from an already uploaded product (which is represented by `AnalysisResult`).
        This method is offered to support "transporting" existing triages.

        Note that - depending on the effective target scope, the `product_id`, `group_id` formal
        parameters are either required or forbidden.

        @param triage: the triage to "copy"
        @param scope: if given, overrides the triage's scope
        @param product_id: target product_id. required iff scope in FN, FH, R
        @param group_id: target group_id. required iff scope is G(ROUP)
        '''
        url = self._routes.triage()

        # if no scope is set, use the one from passed triage
        scope = scope if scope else triage.scope()

        # depending on the scope, different arguments are required
        if scope == TriageScope.ACCOUNT_WIDE:
            none(product_id)
            none(group_id)
        elif scope in (TriageScope.FILE_NAME, TriageScope.FILE_HASH,
                       TriageScope.RESULT):
            not_none(product_id)
            none(group_id)
        elif scope == TriageScope.GROUP:
            none(product_id)
            not_none(group_id)
        else:
            raise NotImplementedError()

        # "copy" data from existing triage
        triage_dict = {
            'component': triage.component_name(),
            'version': triage.component_version(),
            'vulns': [triage.vulnerability_id()],
            'scope': triage.scope().value,
            'reason': triage.reason(),
            'description': triage.description(),
        }

        if product_id:
            triage_dict['product_id'] = product_id

        if group_id:
            triage_dict['group_id'] = group_id

        self._put(
            url=url,
            json=triage_dict,
        )
Example #21
0
    def __init__(self, api_routes, basic_credentials, tls_verify=False):
        self._routes = not_none(api_routes)
        self._credentials = not_none(basic_credentials)
        self._auth = (basic_credentials.username(), basic_credentials.passwd())
        self._tls_verify = tls_verify
        self._session_id = None
        self._session = requests.Session()
        mount_default_adapter(session=self._session, )

        self._csrf_token = None
Example #22
0
    def set_kubecfg(self, kubeconfig_dict: dict):
        not_none(kubeconfig_dict)

        configuration = kubernetes.client.Configuration()
        cfg_loader = KubeConfigLoader(dict(kubeconfig_dict))
        cfg_loader.load_and_set(configuration)
        # pylint: disable=no-member
        kubernetes.client.Configuration.set_default(configuration)
        # pylint: enable=no-member
        self.kubeconfig = configuration
Example #23
0
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
    )
Example #24
0
 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))
Example #25
0
 def __init__(self,
              name,
              base_definition,
              jobs,
              exception=None,
              template='default'):
     self.name = not_none(name)
     self.base_definition = ensure_dict(base_definition, allow_empty=True)
     self.jobs = ensure_dict(jobs, allow_empty=False)
     self.template = not_none(template)
     self.exception = exception
Example #26
0
 def __init__(
     self,
     type_name,
     base_name,
     qualifier=None,
     logical_name=None,
 ):
     self._type_name = not_none(type_name)
     self._base_name = not_none(base_name)
     self._qualifier = qualifier if qualifier else ''
     self._logical_name = logical_name
Example #27
0
def determine_email_address(
    github_user_name: str,
    github_api: GitHub,
) -> typing.Optional[str]:
    not_none(github_user_name)
    try:
        user = github_api.user(github_user_name)
    except NotFoundError:
        logger.warning(f'failed to lookup {github_user_name=} {github_api._github_url=}')
        return None

    return user.email
Example #28
0
    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),
            ),
        )
Example #29
0
    def set_kubecfg(self, kubeconfig_dict: typing.Union[dict, model.kubernetes.KubernetesConfig]):
        not_none(kubeconfig_dict)
        if isinstance(kubeconfig_dict, model.kubernetes.KubernetesConfig):
            kubeconfig_dict = kubeconfig_dict.kubeconfig()

        configuration = kubernetes.client.Configuration()
        cfg_loader = KubeConfigLoader(dict(kubeconfig_dict))
        cfg_loader.load_and_set(configuration)
        # pylint: disable=no-member
        kubernetes.client.Configuration.set_default(configuration)
        # pylint: enable=no-member
        self.kubeconfig = configuration
Example #30
0
 def __init__(
         self,
         status: UploadStatus,
         component: Component,
         result: AnalysisResult,
 ):
     self.status = not_none(status)
     self.component = not_none(component)
     if result:
         self.result = result
     else:
         self.result = None