Example #1
0
def build_ingress(services, dns_suffix, dual_dns_prefix_annotation_name,
                  ingress_info):
    ingress = client.ExtensionsV1beta1Ingress()
    # init metadata
    ingress.metadata = client.V1ObjectMeta()
    ingress.metadata.name = ingress_info["ingress_name"]
    # init spec
    ingress.spec = client.ExtensionsV1beta1IngressSpec()
    ingress.spec.rules = []
    service_tuples = _create_ingress_service_tuples(
        services, dual_dns_prefix_annotation_name)
    for dns_prefix, port, service in service_tuples:
        ingress_rule = client.ExtensionsV1beta1IngressRule()
        ingress_rule.host = "%s.%s" % (dns_prefix, dns_suffix)
        backend = client.ExtensionsV1beta1IngressBackend(
            service_name=service.metadata.name, service_port=port)
        ingress_path = [
            client.ExtensionsV1beta1HTTPIngressPath(path="/", backend=backend)
        ]
        ingress_rule.http = client.ExtensionsV1beta1HTTPIngressRuleValue(
            ingress_path)

        ingress.spec.rules.append(ingress_rule)

    if not ingress.spec.rules:
        ingress_dummy_backend = client.ExtensionsV1beta1IngressBackend(
            service_name=ingress_info["ingress_controller_service_name"],
            service_port=80)
        ingress.spec.backend = ingress_dummy_backend

    return ingress
Example #2
0
def main():
    # Fetching and loading Kubernetes Information
    config.load_kube_config()
    # For incluster details
    # config.load_incluster_config()

    extensions_v1_beta1 = client.ExtensionsV1beta1Api()

    body = client.ExtensionsV1beta1Ingress(
        api_version="networking.k8s.io/v1beta1",
        kind="Ingress",
        metadata=client.V1ObjectMeta(
            name="ingress-example",
            annotations={"nginx.ingress.kubernetes.io/rewrite-target": "/"}),
        spec=client.ExtensionsV1beta1IngressSpec(rules=[
            client.ExtensionsV1beta1IngressRule(
                host="abc.xyz.com",
                http=client.ExtensionsV1beta1HTTPIngressRuleValue(
                    paths=list[client.ExtensionsV1beta1HTTPIngressPath(
                        path="/api",
                        backend=client.ExtensionsV1beta1IngressBackend(
                            service_name="ingress", service_port=5000))]))
        ]))

    # Can specify a namespace that you have created
    extensions_v1_beta1.create_namespaced_ingress(namespace="default",
                                                  body=body)
Example #3
0
def generate_ingress(ingress_name: str):

    metadata = client.V1ObjectMeta(
        name=ingress_name,
        namespace="production",
        labels={"certmanager-solver": "nginx-platform-production"},
        annotations={
            "kubernetes.io/ingress.class":
            "nginx-platform-production",
            "kubernetes.io/tls-acme":
            "true",
            "meta.helm.sh/release-name":
            "platform-ingress",
            "meta.helm.sh/release-namespace":
            "production",
            "certmanager.k8s.io/cluster-issuer":
            "letsencrypt-prod",
            "nginx.ingress.kubernetes.io/ssl-redirect":
            "true",
            "nginx.ingress.kubernetes.io/proxy-body-size":
            "0",
            "nginx.ingress.kubernetes.io/server-snippet":
            "location = /check-dns {\n  return 200;\n}",
            "nginx.ingress.kubernetes.io/configuration-snippet":
            'proxy_set_header Host $host;\nproxy_set_header whitelabel "$host";',
        },
    )

    spec = client.ExtensionsV1beta1IngressSpec(
        rules=[
            client.ExtensionsV1beta1IngressRule(
                host=ingress_name,
                http=client.ExtensionsV1beta1HTTPIngressRuleValue(paths=[
                    client.ExtensionsV1beta1HTTPIngressPath(
                        path="/",
                        backend=client.ExtensionsV1beta1IngressBackend(
                            service_name="platform-app", service_port=3000),
                    )
                ]),
            )
        ],
        tls=[
            client.ExtensionsV1beta1IngressTLS(hosts=[ingress_name],
                                               secret_name=ingress_name +
                                               "-tls")
        ],
    )

    return client.ExtensionsV1beta1Ingress(kind="Ingress",
                                           api_version="extensions/v1beta1",
                                           metadata=metadata,
                                           spec=spec)
Example #4
0
def simple_ingress():
    """Return the Kubernetes config matching the simple-ingress.yaml manifest."""
    return client.ExtensionsV1beta1Ingress(
        api_version='extensions/v1beta1',
        kind='Ingress',
        metadata=client.V1ObjectMeta(name='my-ingress'),
        spec=client.ExtensionsV1beta1IngressSpec(rules=[
            client.ExtensionsV1beta1IngressRule(
                http=client.ExtensionsV1beta1HTTPIngressRuleValue(paths=[
                    client.ExtensionsV1beta1HTTPIngressPath(
                        backend=client.ExtensionsV1beta1IngressBackend(
                            service_name='my-service', service_port=80),
                        path='/')
                ]))
        ], ))
Example #5
0
def add_ingress_rules(name: str,
                      project_name: str,
                      service_port: int,
                      namespace: str = "default"):
    # update ingress path rules
    project_hash = hashlib.sha256(project_name.encode()).hexdigest()[:16]
    service_name = f'p{project_hash}-service'

    ext_v1_beta = client.ExtensionsV1beta1Api()
    prev_ingress = describe_ingress(name=name)
    path_rule = client.ExtensionsV1beta1HTTPIngressPath(
        backend=client.ExtensionsV1beta1IngressBackend(
            service_name=service_name, service_port=service_port),
        path=f'/{project_hash}/(.*)')
    prev_ingress.spec.rules[0].http.paths.append(path_rule)

    resp = ext_v1_beta.replace_namespaced_ingress(name=name,
                                                  namespace=namespace,
                                                  body=prev_ingress)
    # print(resp)
    return resp
Example #6
0
def create_ingress(extensions_v1_beta1):
    body = client.ExtensionsV1beta1Ingress(
        api_version="networking.k8s.io/v1beta1",
        kind="Ingress",
        metadata=client.V1ObjectMeta(
            name="ingress-example",
            annotations={"nginx.ingress.kubernetes.io/rewrite-target": "/"}),
        spec=client.ExtensionsV1beta1IngressSpec(rules=[
            client.ExtensionsV1beta1IngressRule(
                host="boddulabs.com",
                http=client.ExtensionsV1beta1HTTPIngressRuleValue(paths=[
                    client.ExtensionsV1beta1HTTPIngressPath(
                        path="/",
                        backend=client.ExtensionsV1beta1IngressBackend(
                            service_port=5678, service_name="service-example"))
                ]))
        ]))
    # Creation of the Deployment in specified namespace
    # (Can replace "default" with a namespace you may have created)
    extensions_v1_beta1.create_namespaced_ingress(namespace="default",
                                                  body=body)
Example #7
0
def create_deployment_ingress(deployment: k8s.V1Deployment) -> None:
    """Create an ingress to a service backed by a deployment.

    :param deployment: A configured deployment object.
    """
    namespace = deployment.metadata.namespace
    name = deployment.metadata.name
    pod_port = int(deployment.metadata.annotations["port"])

    ingress_path = f"{ingress_route(namespace, name)}(/|$)(.*)"

    ingress_spec = k8s.ExtensionsV1beta1IngressSpec(rules=[
        k8s.ExtensionsV1beta1IngressRule(
            http=k8s.ExtensionsV1beta1HTTPIngressRuleValue(paths=[
                k8s.ExtensionsV1beta1HTTPIngressPath(
                    path=ingress_path,
                    backend=k8s.ExtensionsV1beta1IngressBackend(
                        service_name=name, service_port=pod_port),
                )
            ]))
    ])

    ingress_metadata = k8s.V1ObjectMeta(
        namespace=namespace,
        name=name,
        annotations={
            "kubernetes.io/ingress.class": "nginx",
            "nginx.ingress.kubernetes.io/rewrite-target": "/$2",
        },
        labels={
            "app": "bodywork",
            "stage": name
        },
    )

    ingress = k8s.ExtensionsV1beta1Ingress(metadata=ingress_metadata,
                                           spec=ingress_spec)

    k8s.ExtensionsV1beta1Api().create_namespaced_ingress(namespace=namespace,
                                                         body=ingress)
Example #8
0
    def post(self):
        """
        """

        current_user_id = get_jwt_identity()
        current_user_roles = get_jwt_claims()['roles']

        project_schema = ProjectSchema()

        project_data = request.get_json()

        validated_project_data, errors = project_schema.load(project_data)

        if errors:
            return dict(status='fail', message=errors), 400

        if not has_role(current_user_roles, 'administrator'):
            validated_project_data['owner_id'] = current_user_id

        # check if project already exists
        existing_project = Project.find_first(
            name=validated_project_data['name'],
            owner_id=validated_project_data['owner_id'])

        if existing_project:
            return dict(
                status='fail',
                message=
                f'project with name {validated_project_data["name"]} already exists'
            ), 409

        try:
            validated_project_data['alias'] =\
                create_alias(validated_project_data['name'])
            namespace_name = validated_project_data['alias']
            cluster_id = validated_project_data['cluster_id']
            cluster = Cluster.get_by_id(cluster_id)

            if not cluster:
                return dict(status='fail',
                            message=f'cluster {cluster_id} not found'), 404

            kube_host = cluster.host
            kube_token = cluster.token

            kube_client = create_kube_clients(kube_host, kube_token)

            # create namespace in cluster
            cluster_namespace = kube_client.kube.create_namespace(
                client.V1Namespace(metadata=client.V1ObjectMeta(
                    name=namespace_name)))
            # create project in database
            if cluster_namespace:

                ingress_name = f"{validated_project_data['alias']}-ingress"

                ingress_meta = client.V1ObjectMeta(name=ingress_name)

                ingress_default_rule = client.ExtensionsV1beta1IngressRule(
                    host="traefik-ui.cranecloud.io",
                    http=client.ExtensionsV1beta1HTTPIngressRuleValue(paths=[
                        client.ExtensionsV1beta1HTTPIngressPath(
                            path="/*",
                            backend=client.ExtensionsV1beta1IngressBackend(
                                service_name="traefik-web-ui-ext",
                                service_port=80))
                    ]))

                ingress_spec = client.ExtensionsV1beta1IngressSpec(
                    rules=[ingress_default_rule])

                ingress_body = client.ExtensionsV1beta1Ingress(
                    metadata=ingress_meta, spec=ingress_spec)

                kube_client.extension_api.create_namespaced_ingress(
                    namespace=namespace_name, body=ingress_body)

                project = Project(**validated_project_data)

                saved = project.save()

                if not saved:
                    # delete the namespace
                    kube_client.kube.delete_namespace(namespace_name)
                    return dict(status='fail',
                                message='Internal Server Error'), 500

            new_project_data, errors = project_schema.dump(project)

            return dict(status='success',
                        data=dict(project=new_project_data)), 201

        except client.rest.ApiException as e:
            return dict(status='fail', message=e.body), e.status

        except Exception as err:
            return dict(status='fail', message=str(err)), 500
Example #9
0
    def get_user_vnc_pod(uuid, user):
        extension_api = ExtensionsV1beta1Api(get_kubernetes_api_client())
        app_api = AppsV1Api(get_kubernetes_api_client())
        core_api = CoreV1Api(get_kubernetes_api_client())
        result = {}
        has_deployment = False
        user_vnc = None
        try:
            setting = TaskSettings.objects.get(uuid=uuid)
            user_vnc, _ = TaskVNCPod.objects.get_or_create(
                settings=setting,
                user=user,
                defaults={
                    'settings': setting,
                    'user': user,
                    'pod_name': '',
                    'url_path': '',
                    'vnc_password': '',
                    'expire_time': round(time.time() + USER_SPACE_POD_TIMEOUT)
                })
            _, created = TaskStorage.objects.get_or_create(settings=setting,
                                                           user=user,
                                                           defaults={
                                                               'settings':
                                                               setting,
                                                               'user': user,
                                                               'pod_name': ''
                                                           })
            if user_vnc.pod_name:
                try:
                    # check whether deployment is on
                    has_deployment = True
                    _ = app_api.read_namespaced_deployment(
                        name=user_vnc.pod_name, namespace=KUBERNETES_NAMESPACE)
                except ApiException as ex:
                    if ex.status != 404:
                        LOGGER.exception(ex)
                    else:
                        has_deployment = False
            selector = "task-{}-user-{}-vnc".format(setting.uuid, user.id)
            if not has_deployment:
                # create a new deployment
                conf = json.loads(setting.container_config)
                user_dir = "user_{}_task_{}".format(user.id, setting.id)
                dep_name = "task-vnc-{}-{}".format(setting.uuid,
                                                   get_short_uuid())
                shared_pvc_name = "shared-{}".format(setting.uuid)
                shared_mount = client.V1VolumeMount(
                    mount_path=conf['persistent_volume']['mount_path'],
                    name=shared_pvc_name,
                    read_only=True)
                user_storage_name = "user-{}".format(setting.uuid)
                user_mount = client.V1VolumeMount(
                    mount_path='/cloud_scheduler_userspace',
                    name=user_storage_name,
                    sub_path=user_dir)
                username = '******'.format(user.username, setting.id)

                commands = [
                    'set +e',
                    'ln -s /cloud_scheduler_userspace /headless/Desktop/user_space',
                    'useradd -u {uid} {username}'.format(uid=499 + user.id,
                                                         username=username),
                    'usermod -d /headless {}'.format(username),
                    "su -s /bin/bash -c '/dockerstartup/vnc_startup.sh -w' {}".
                    format(username)
                ]
                if created:
                    cp_command = 'cp -r {}/* /headless/Desktop/user_space'.format(
                        conf['persistent_volume']['mount_path'] + '/' +
                        conf['task_initial_file_path'])
                    chown = 'chown -R {user}:{user} /headless/Desktop/user_space/*'.format(
                        user=username)
                    commands.insert(4, cp_command)
                    commands.insert(5, chown)
                vnc_pw = random_password(8)
                env_vnc_pw = client.V1EnvVar(name="VNC_PW", value=vnc_pw)
                container = client.V1Container(
                    name='headless-vnc',
                    image=config.USER_VNC_DOCKER_IMAGE,
                    env=[env_vnc_pw],
                    command=['/bin/bash'],
                    args=['-c', ';'.join(commands)],
                    volume_mounts=[shared_mount, user_mount])
                persistent_volume_claim = client.V1PersistentVolumeClaimVolumeSource(
                    claim_name=conf['persistent_volume']['name'])
                user_volume_claim = client.V1PersistentVolumeClaimVolumeSource(
                    claim_name=USERSPACE_NAME)
                shared_volume = client.V1Volume(
                    name=shared_pvc_name,
                    persistent_volume_claim=persistent_volume_claim)
                user_volume = client.V1Volume(
                    name=user_storage_name,
                    persistent_volume_claim=user_volume_claim)
                template = client.V1PodTemplateSpec(
                    metadata=client.V1ObjectMeta(labels={'app': selector}),
                    spec=client.V1PodSpec(containers=[container],
                                          volumes=[shared_volume,
                                                   user_volume]))
                spec = client.V1DeploymentSpec(
                    replicas=1,
                    template=template,
                    selector={'matchLabels': {
                        'app': selector
                    }})
                deployment = client.V1Deployment(
                    kind='Deployment',
                    metadata=client.V1ObjectMeta(
                        name=dep_name,
                        namespace=KUBERNETES_NAMESPACE,
                        labels={'app': selector}),
                    spec=spec)
                app_api.create_namespaced_deployment(
                    body=deployment, namespace=KUBERNETES_NAMESPACE)
                user_vnc.pod_name = dep_name
                user_vnc.vnc_password = vnc_pw
            if not user_vnc.url_path:
                # create service
                spec = client.V1ServiceSpec(
                    external_name=selector,
                    ports=[
                        client.V1ServicePort(name='websocket-port',
                                             port=config.USER_VNC_PORT,
                                             target_port=config.USER_VNC_PORT)
                    ],
                    selector={'app': selector},
                    type='ClusterIP',
                )
                service = client.V1Service(spec=spec,
                                           metadata=client.V1ObjectMeta(
                                               labels={'app': selector},
                                               name=selector,
                                               namespace=KUBERNETES_NAMESPACE))
                try:
                    core_api.create_namespaced_service(
                        namespace=KUBERNETES_NAMESPACE, body=service)
                except ApiException as ex:
                    if ex.status != 409:  # ignore conflict (duplicate)
                        LOGGER.exception(ex)
                        raise ApiException
                # create ingress
                url_path = str(get_uuid())
                spec = client.ExtensionsV1beta1IngressSpec(
                    rules=[
                        client.ExtensionsV1beta1IngressRule(
                            host=config.USER_VNC_HOST,
                            http=client.ExtensionsV1beta1HTTPIngressRuleValue(
                                paths=[
                                    client.ExtensionsV1beta1HTTPIngressPath(
                                        client.ExtensionsV1beta1IngressBackend(
                                            service_name=selector,
                                            service_port=config.USER_VNC_PORT),
                                        path='/' + url_path)
                                ]))
                    ],
                    tls=[
                        client.ExtensionsV1beta1IngressTLS(
                            hosts=[config.USER_VNC_HOST],
                            secret_name=config.USER_VNC_TLS_SECRET)
                    ],
                )
                ingress = client.ExtensionsV1beta1Ingress(metadata={
                    'name': selector,
                    'annotations': {
                        'kubernetes.io/ingress.class': 'nginx',
                        'nginx.ingress.kubernetes.io/proxy-read-timeout':
                        '86400',
                        'nginx.ingress.kubernetes.io/proxy-send-timeout':
                        '86400',
                    }
                },
                                                          spec=spec)
                need_patch = False
                try:
                    extension_api.create_namespaced_ingress(
                        KUBERNETES_NAMESPACE, ingress)
                except ApiException as ex:
                    if ex.status != 409:  # ignore conflict (duplicate)
                        LOGGER.exception(ex)
                        raise ApiException
                    else:
                        need_patch = True
                if need_patch:
                    extension_api.patch_namespaced_ingress(
                        selector, KUBERNETES_NAMESPACE, ingress)
                user_vnc.url_path = url_path
            user_vnc.expire_time = round(time.time() + USER_SPACE_POD_TIMEOUT)
            result['url_path'] = user_vnc.url_path
            result['vnc_password'] = user_vnc.vnc_password
            result['deployment_name'] = user_vnc.pod_name
            result['vnc_host'] = config.USER_VNC_HOST
            result['vnc_port'] = config.USER_VNC_WS_PORT
            user_vnc.save(force_update=True)
        except ApiException as ex:
            LOGGER.exception(ex)
        except Exception as ex:
            LOGGER.exception(ex)
        finally:
            if user_vnc:
                user_vnc.save(force_update=True)
            return result
Example #10
0
    def on_spawn(self, task):
        ports = []
        rules = []

        idx = 0
        for path, route in task.routes.items():
            port = route['port']
            idx += 1
            port_name = f'route{idx}'

            ports.append(
                client.V1ServicePort(
                    name=port_name,
                    port=port,
                    target_port=port,
                ))

            rules.append(
                client.ExtensionsV1beta1IngressRule(
                    host=f'{task.id}.{self.cluster.domain}',
                    http=client.ExtensionsV1beta1HTTPIngressRuleValue(paths=[
                        client.ExtensionsV1beta1HTTPIngressPath(
                            path=path,
                            backend=client.ExtensionsV1beta1IngressBackend(
                                service_name=task.id,
                                service_port=port_name,
                            ),
                        ),
                    ], ),
                ))

        if len(rules) == 0:
            return

        print('~~ creating task ingress', path, '-> port', port)

        self.cluster.core.create_namespaced_service(
            namespace=self.cluster.namespace,
            body=client.V1Service(
                metadata=client.V1ObjectMeta(
                    name=task.id,
                    namespace=self.cluster.namespace,
                    labels={
                        LABEL_TASK_ID: task.id,
                    },
                ),
                spec=client.V1ServiceSpec(
                    selector={
                        LABEL_TASK_ID: task.id,
                    },
                    ports=ports,
                ),
            ),
        )

        self.cluster.ext.create_namespaced_ingress(
            namespace=self.cluster.namespace,
            body=client.ExtensionsV1beta1Ingress(
                metadata=client.V1ObjectMeta(
                    name=task.id,
                    labels={
                        LABEL_TASK_ID: task.id,
                    },
                    annotations={
                        'kubernetes.io/ingress.class': 'traefik',
                        'traefik.frontend.rule.type': 'PathPrefix',
                    },
                ),
                spec=client.ExtensionsV1beta1IngressSpec(rules=rules, ),
            ),
        )
Example #11
0
    def post(self, project_id):
        """
        """

        resource_registry = {
            'db_deployment': False,
            'db_service': False,
            'image_pull_secret': False,
            'app_deployment': False,
            'app_service': False,
            'ingress_entry': False
        }

        current_user_id = get_jwt_identity()
        current_user_roles = get_jwt_claims()['roles']

        app_schema = AppSchema()

        app_data = request.get_json()

        validated_app_data, errors = app_schema.load(app_data,
                                                     partial=("project_id", ))

        if errors:
            return dict(status='fail', message=errors), 400

        existing_app = App.find_first(name=validated_app_data['name'],
                                      project_id=project_id)

        if existing_app:
            return dict(
                status='fail',
                message=
                f'App with name {validated_app_data["name"]} already exists'
            ), 409

        validated_app_data['port'] = validated_app_data.get('port', 80)

        app_name = validated_app_data['name']
        app_alias = create_alias(validated_app_data['name'])
        app_image = validated_app_data['image']
        command = validated_app_data.get('command', None)
        # env_vars = validated_app_data['env_vars']
        env_vars = validated_app_data.get('env_vars', None)
        private_repo = validated_app_data.get('private_image', False)
        docker_server = validated_app_data.get('docker_server', None)
        docker_username = validated_app_data.get('docker_username', None)
        docker_password = validated_app_data.get('docker_password', None)
        docker_email = validated_app_data.get('docker_email', None)
        replicas = validated_app_data.get('replicas', 1)
        app_port = validated_app_data.get('port', None)
        image_pull_secret = None

        command = command.split() if command else None

        project = Project.get_by_id(project_id)

        if not project:
            return dict(status='fail',
                        message=f'project {project_id} not found'), 404

        if not is_owner_or_admin(project, current_user_id, current_user_roles):
            return dict(status='fail', message='Unauthorised'), 403

        cluster = project.cluster
        namespace = project.alias

        if not cluster:
            return dict(status='fail', message="Invalid Cluster"), 500

        # check if app already exists
        app = App.find_first(**{'name': app_name})

        if app:
            return dict(status='fail',
                        message=f'App {app_name} already exists'), 409

        kube_host = cluster.host
        kube_token = cluster.token
        service_host = urlsplit(kube_host).hostname

        kube_client = create_kube_clients(kube_host, kube_token)

        try:

            # create the app
            new_app = App(name=app_name,
                          image=app_image,
                          project_id=project_id,
                          alias=app_alias,
                          port=app_port)

            if private_repo:

                # handle gcr credentials
                if 'gcr' in docker_server and docker_username == '_json_key':
                    docker_password = json.dumps(
                        json.loads(base64.b64decode(docker_password)))

                # create image pull secrets
                authstring = base64.b64encode(
                    f'{docker_username}:{docker_password}'.encode("utf-8"))

                secret_dict = dict(
                    auths={
                        docker_server: {
                            "username": docker_username,
                            "password": docker_password,
                            "email": docker_email,
                            "auth": str(authstring, "utf-8")
                        }
                    })

                secret_b64 = base64.b64encode(
                    json.dumps(secret_dict).encode("utf-8"))

                secret_body = client.V1Secret(
                    metadata=client.V1ObjectMeta(name=app_alias),
                    type='kubernetes.io/dockerconfigjson',
                    data={'.dockerconfigjson': str(secret_b64, "utf-8")})

                kube_client.kube.create_namespaced_secret(
                    namespace=namespace,
                    body=secret_body,
                    _preload_content=False)

                # update registry
                resource_registry['image_pull_secret'] = True

                image_pull_secret = client.V1LocalObjectReference(
                    name=app_alias)

            # create app deployment's pvc meta and spec
            # pvc_name = f'{app_alias}-pvc'
            # pvc_meta = client.V1ObjectMeta(name=pvc_name)

            # access_modes = ['ReadWriteOnce']
            # storage_class = 'openebs-standard'
            # resources = client.V1ResourceRequirements(
            #     requests=dict(storage='1Gi'))

            # pvc_spec = client.V1PersistentVolumeClaimSpec(
            #     access_modes=access_modes, resources=resources, storage_class_name=storage_class)

            # Create a PVC
            # pvc = client.V1PersistentVolumeClaim(
            #     api_version="v1",
            #     kind="PersistentVolumeClaim",
            #     metadata=pvc_meta,
            #     spec=pvc_spec
            # )

            # kube_client.kube.create_namespaced_persistent_volume_claim(
            #     namespace=namespace,
            #     body=pvc
            # )

            # create deployment
            dep_name = f'{app_alias}-deployment'

            # # EnvVar
            env = []
            if env_vars:
                for key, value in env_vars.items():
                    env.append(client.V1EnvVar(name=str(key),
                                               value=str(value)))

            # pod template
            container = client.V1Container(
                name=app_alias,
                image=app_image,
                ports=[client.V1ContainerPort(container_port=app_port)],
                env=env,
                command=command
                # volume_mounts=[client.V1VolumeMount(mount_path="/data", name=dep_name)]
            )

            #pod volumes
            # volumes = client.V1Volume(
            #     name=dep_name
            #     # persistent_volume_claim=client.V1PersistentVolumeClaimVolumeSource(claim_name=pvc_name)
            # )

            # spec
            template = client.V1PodTemplateSpec(
                metadata=client.V1ObjectMeta(labels={'app': app_alias}),
                spec=client.V1PodSpec(containers=[container],
                                      image_pull_secrets=[image_pull_secret]
                                      # volumes=[volumes]
                                      ))

            # spec of deployment
            spec = client.V1DeploymentSpec(
                replicas=replicas,
                template=template,
                selector={'matchLabels': {
                    'app': app_alias
                }})

            # Instantiate the deployment
            deployment = client.V1Deployment(
                api_version="apps/v1",
                kind="Deployment",
                metadata=client.V1ObjectMeta(name=dep_name),
                spec=spec)

            # create deployment in  cluster

            kube_client.appsv1_api.create_namespaced_deployment(
                body=deployment, namespace=namespace, _preload_content=False)

            # update registry
            resource_registry['app_deployment'] = True

            # create service in the cluster
            service_name = f'{app_alias}-service'

            service_meta = client.V1ObjectMeta(name=service_name,
                                               labels={'app': app_alias})

            service_spec = client.V1ServiceSpec(
                type='ClusterIP',
                ports=[client.V1ServicePort(port=3000, target_port=app_port)],
                selector={'app': app_alias})

            service = client.V1Service(metadata=service_meta,
                                       spec=service_spec)

            kube_client.kube.create_namespaced_service(namespace=namespace,
                                                       body=service,
                                                       _preload_content=False)

            # update resource registry
            resource_registry['app_service'] = True

            # subdomain for the app
            # sub_domain = f'{app_alias}.cranecloud.io'
            sub_domain = get_app_subdomain(app_alias)

            # create new ingres rule for the application
            new_ingress_backend = client.ExtensionsV1beta1IngressBackend(
                service_name=service_name, service_port=3000)

            new_ingress_rule = client.ExtensionsV1beta1IngressRule(
                host=sub_domain,
                http=client.ExtensionsV1beta1HTTPIngressRuleValue(paths=[
                    client.ExtensionsV1beta1HTTPIngressPath(
                        path="", backend=new_ingress_backend)
                ]))

            ingress_name = f'{project.alias}-ingress'

            # Check if there is an ingress resource in the namespace, create if not

            ingress_list = kube_client.extension_api.list_namespaced_ingress(
                namespace=namespace).items

            if not ingress_list:

                ingress_meta = client.V1ObjectMeta(name=ingress_name)

                ingress_spec = client.ExtensionsV1beta1IngressSpec(
                    # backend=ingress_backend,
                    rules=[new_ingress_rule])

                ingress_body = client.ExtensionsV1beta1Ingress(
                    metadata=ingress_meta, spec=ingress_spec)

                kube_client.extension_api.create_namespaced_ingress(
                    namespace=namespace, body=ingress_body)

                # update registry
                resource_registry['ingress_entry'] = True
            else:
                # Update ingress with new entry
                ingress = ingress_list[0]

                ingress.spec.rules.append(new_ingress_rule)

                kube_client.extension_api.patch_namespaced_ingress(
                    name=ingress_name, namespace=namespace, body=ingress)

            service_url = f'https://{sub_domain}'

            new_app.url = service_url

            saved = new_app.save()

            if not saved:
                return dict(status='fail',
                            message='Internal Server Error'), 500

            new_app_data, _ = app_schema.dump(new_app)

            return dict(status='success', data=dict(app=new_app_data)), 201

        except client.rest.ApiException as e:
            resource_clean_up(resource_registry, app_alias, namespace,
                              kube_client)
            return dict(status='fail', message=json.loads(e.body)), 500

        except Exception as e:
            resource_clean_up(resource_registry, app_alias, namespace,
                              kube_client)
            return dict(status='fail', message=str(e)), 500
Example #12
0
def userpod(pod_type,
            username,
            eppn,
            uid,
            ugid,
            groupname,
            ggid,
            annotations={}):
    """Starts a user pod

    Parameters
    ----------
    pod_type: str
        The workload type of the pod to launch, e.g. "theia-python"
    username: str
        The short username for the user, e.g. "mst3k"
    eppn: str
        The eppn / long username, e.g. "*****@*****.**"
    uid: int
        The numeric user ID of the user that the pod will run as
    ugid: int
        The numeric GID of the user's primary group
    groupname: str
        Opaque group name for project mount
    ggid: int
        The numeric group ID the pod will run as
    annotations: dict
        Any additional annotations for the ingress

    Returns
    -------
    pod_dns: str
        The partly-random domain name of the pos; can be used to check
        the pod's status, and determines the URL: https://<pod_dns>
    """

    cfg = get_config()
    v1 = client.CoreV1Api()
    v1beta = client.ExtensionsV1beta1Api()
    namespace = cfg["NAMESPACE"]

    type_maps = v1.list_namespaced_config_map(namespace,
                                              label_selector="class=userpod")
    ntypes = len(type_maps.items)
    podcfg = None
    for i in range(ntypes):
        if type_maps.items[i].metadata.name == pod_type:
            podcfg = type_maps.items[i]
            break
    if not podcfg:
        raise Exception("Unsupported type")

    poddata = podcfg.data

    passwd = poddata["passwd"]
    passwd = passwd.replace("<UID>", str(uid))
    passwd = passwd.replace("<UGID>", str(ugid))

    group = poddata["group"]
    group = group.replace("<UGID>", str(ugid))
    group = group.replace("<PGID>", str(ggid))

    pod_port = poddata["port"]
    envoycfg = cfg["ENVOY_TEMPLATE"]
    envoyext = cfg["ENVOY_EXTERNAL"]
    envoyadm = cfg["ENVOY_ADMIN"]
    envoycfg = envoycfg.replace("<SERVICEPORT>", pod_port)
    envoycfg = envoycfg.replace("<ENVOYADMIN>", envoyadm)
    envoycfg = envoycfg.replace("<ENVOYEXTERNAL>", envoyext)
    envoycfg = envoycfg.replace("<SHORTUSER>", username)
    envoycfg = envoycfg.replace("<LONGUSER>", eppn)

    podmap = {
        "passwd": passwd,
        "group": group,
        "envoy": envoycfg,
    }
    username_label = gen_user_label(username)
    dashed_username = username.replace("@", "-")
    dashed_username = dashed_username.replace(".", "-")

    cfgmap = client.V1ConfigMap(
        api_version="v1",
        kind="ConfigMap",
        metadata=client.V1ObjectMeta(
            generate_name="%s-%s-" % (pod_type, dashed_username),
            labels={
                "username-label": username_label,
            },
        ),
        data=podmap,
    )
    created_map = v1.create_namespaced_config_map(namespace, cfgmap)

    pod_name = created_map.metadata.name
    patch_labels = {
        "user-pod": pod_name,
    }
    label = {
        "metadata": {
            "labels": patch_labels,
        },
    }
    v1.patch_namespaced_config_map(pod_name, namespace=namespace, body=label)

    resource_labels = {
        "username-label": username_label,
        "user-pod": pod_name,
    }

    pod_volumes = [
        client.V1Volume(name="cfgfiles",
                        config_map=client.V1ConfigMapVolumeSource(
                            name=pod_name, )),
    ]

    userpod_mounts = [
        client.V1VolumeMount(
            name="cfgfiles",
            mount_path="/etc/passwd",
            sub_path="passwd",
        ),
        client.V1VolumeMount(
            name="cfgfiles",
            mount_path="/etc/group",
            sub_path="group",
        ),
    ]

    if "HOME_PREFIX" in cfg:
        # NOTE / TODO: This will change eventually
        pod_volumes.append(
            client.V1Volume(name="home",
                            host_path=client.V1HostPathVolumeSource(
                                path="%s/%s" % (cfg["HOME_PREFIX"], username),
                                type="Directory")))
        userpod_mounts.append(
            client.V1VolumeMount(name="home", mount_path="/home/user"))
    if "PROJECT_PREFIX" in cfg:
        pod_volumes.append(
            client.V1Volume(
                name="project",
                host_path=client.V1HostPathVolumeSource(
                    path="%s/%s" % (cfg["PROJECT_PREFIX"], groupname),
                    type="Directory")))
        userpod_mounts.append(
            client.V1VolumeMount(name="project", mount_path="/home/project"))

    registry = cfg["REGISTRY"]
    reg_org = cfg["REGISTRY_ORG"]
    envoy = cfg["ENVOY_CONTAINER"]

    supplemental = []
    if ugid != ggid:
        supplemental = [ggid]

    pod = client.V1Pod(api_version="v1",
                       kind="Pod",
                       metadata=client.V1ObjectMeta(
                           name=pod_name,
                           labels=resource_labels,
                       ),
                       spec=client.V1PodSpec(
                           volumes=pod_volumes,
                           restart_policy="OnFailure",
                           security_context=client.V1PodSecurityContext(
                               supplemental_groups=supplemental),
                           containers=[
                               client.V1Container(
                                   name=pod_type,
                                   image="%s/%s/%s:latest" %
                                   (registry, reg_org, pod_type),
                                   security_context=client.V1SecurityContext(
                                       run_as_user=uid,
                                       run_as_group=ugid,
                                   ),
                                   volume_mounts=userpod_mounts,
                               ),
                               client.V1Container(
                                   name="envoy",
                                   image=envoy,
                                   volume_mounts=[
                                       client.V1VolumeMount(
                                           name="cfgfiles",
                                           mount_path="/etc/envoy/envoy.yaml",
                                           sub_path="envoy",
                                       ),
                                   ],
                               ),
                           ],
                       ))
    v1.create_namespaced_pod(namespace=namespace, body=pod)

    service = client.V1Service(api_version="v1",
                               kind="Service",
                               metadata=client.V1ObjectMeta(
                                   name=pod_name,
                                   labels=resource_labels,
                                   namespace=namespace,
                               ),
                               spec=client.V1ServiceSpec(
                                   selector=resource_labels,
                                   ports=[
                                       client.V1ServicePort(
                                           port=80,
                                           protocol="TCP",
                                           target_port=int(envoyext),
                                       )
                                   ]))
    v1.create_namespaced_service(namespace, body=service)

    env_service = client.V1Service(api_version="v1",
                                   kind="Service",
                                   metadata=client.V1ObjectMeta(
                                       name="%s-envoy" % pod_name,
                                       labels=resource_labels,
                                       namespace=namespace,
                                   ),
                                   spec=client.V1ServiceSpec(
                                       selector=resource_labels,
                                       ports=[
                                           client.V1ServicePort(
                                               port=9000,
                                               protocol="TCP",
                                               target_port=int(envoyadm),
                                           )
                                       ]))
    v1.create_namespaced_service(namespace, body=env_service)

    pod_dom = cfg["POD_DOMAIN"]
    pod_dns = "%s.%s" % (pod_name, pod_dom)

    annotations["kubernetes.io/ingress.class"] = "nginx"

    ingress = client.ExtensionsV1beta1Ingress(
        metadata=client.V1ObjectMeta(
            name=pod_name,
            labels=resource_labels,
            namespace=namespace,
            annotations=annotations,
        ),
        spec=client.ExtensionsV1beta1IngressSpec(rules=[
            client.ExtensionsV1beta1IngressRule(
                host=pod_dns,
                http=client.ExtensionsV1beta1HTTPIngressRuleValue(paths=[
                    client.ExtensionsV1beta1HTTPIngressPath(
                        path="/",
                        backend=client.ExtensionsV1beta1IngressBackend(
                            service_name=pod_name,
                            service_port=80,
                        ),
                    )
                ], ),
            )
        ], ),
    )
    v1beta.create_namespaced_ingress(namespace, body=ingress)

    return pod_dns