Ejemplo n.º 1
0
async def start(kubeconf_a, kubeconf_b):
    # Create client API instances to each cluster.
    api_client_a = k8s.config.new_client_from_config(kubeconf_a)
    api_client_b = k8s.config.new_client_from_config(kubeconf_b)

    # Specify and dispatch the tasks.
    tasks = [
        watch_resource("Cluster A: ", k8s.CoreV1Api(api_client_a).list_namespace),
        watch_resource("Cluster B: ", k8s.CoreV1Api(api_client_b).list_namespace),
    ]
    await asyncio.gather(*tasks)

    # Close all pending connections.
    await api_client_a.close()
    await api_client_b.close()
Ejemplo n.º 2
0
async def main():
    # Load default kubeconfig file and create an aiohttp client instance.
    config = k8s.utils.load_config(warn=False)
    client = k8s.clients.get_aiohttp(config)
    proxy = k8s.api_proxy.Proxy(config)

    # Namespaces and Pods are in the K8s Core API, Deployments in an extension.
    corev1 = k8s.CoreV1Api(proxy)
    extv1beta = k8s.ExtensionsV1beta1Api(proxy)

    # Compile the necessary request parameters (headers, body, parameters, ...)
    # for the API calls we want to make. We also specify watch=True because we
    # want to listen to changes.
    cargs_ns = corev1.list_namespace(timeout_seconds=5, watch=True)
    cargs_pods = corev1.list_pod_for_all_namespaces(timeout_seconds=5,
                                                    watch=True)
    cargs_deploy = extv1beta.list_deployment_for_all_namespaces(
        timeout_seconds=5, watch=True)  # noqa

    # Define and dispatch the tasks.
    tasks = [
        watch_resource(client, cargs_ns),
        watch_resource(client, cargs_pods),
        watch_resource(client, cargs_deploy),
    ]
    await asyncio.gather(*tasks)

    # Close all pending connections.
    await client.close()
Ejemplo n.º 3
0
async def setup():
    # Create a client instance and load the credentials from ~/.kube/kubeconfig
    api_client = k8s.config.new_client_from_config()

    # Specify and dispatch the tasks.
    tasks = [
        create_deployment(api_client),
        watch_resource(k8s.CoreV1Api(api_client).list_namespace,
                       timeout_seconds=1),
    ]
    await asyncio.gather(*tasks)

    print('\nShutting down')
    await api_client.close()
Ejemplo n.º 4
0
async def main():
    # Create a client instance and load the credentials from ~/.kube/kubeconfig
    api_client = k8s.config.new_client_from_config()
    api_v1 = k8s.CoreV1Api(api_client)

    # Name of the namespace.
    name = 'foo'

    # ----------------------------------------------------------------------
    # Create the namespace.
    # ----------------------------------------------------------------------
    manifest = {
        'apiVersion': 'v1',
        'kind': 'Namespace',
        'metadata': {
            'name': name
        },
    }
    resp = await api_v1.create_namespace(body=manifest)
    if resp.http.status == 201:
        print(f'Namespace <{name}> created')
    elif resp.http.status == 409:
        print(f'Namespace <{name}> already exists')
    else:
        print(f'Error {resp.http.status}')
        print(resp.http.text)

    # ----------------------------------------------------------------------
    # When we try to create the same namespace a second time it must fail.
    # ----------------------------------------------------------------------
    resp = await api_v1.create_namespace(body=manifest)
    assert resp.http.status == 409
    print(f'Namespace <{name}> already exists')

    # ----------------------------------------------------------------------
    # Delete the namespace.
    # ----------------------------------------------------------------------
    delete_opts = k8s.V1DeleteOptions(
        api_version='v1',
        kind='DeleteOptions',
        grace_period_seconds=0,
        propagation_policy='Foreground',
    )
    resp = await api_v1.delete_namespace(name=name, body=delete_opts)
    assert resp.http.status == 200
    print(f'Namespace <{name}> deleted')

    # Close all pending connections.
    await api_client.close()
Ejemplo n.º 5
0
async def start(kubeconf_a, kubeconf_b):
    # Create client API instances to each cluster.
    config_a = k8s.utils.load_config(kubeconf_a, warn=False)
    config_b = k8s.utils.load_config(kubeconf_b, warn=False)
    client_a = k8s.clients.get_aiohttp(config_a)
    client_b = k8s.clients.get_aiohttp(config_b)
    proxy_a = k8s.api_proxy.Proxy(config_a)
    proxy_b = k8s.api_proxy.Proxy(config_b)

    cargs_ns_a = k8s.CoreV1Api(proxy_a).list_namespace(timeout_seconds=5,
                                                       watch=True)
    cargs_ns_b = k8s.CoreV1Api(proxy_b).list_namespace(timeout_seconds=5,
                                                       watch=True)

    # Specify and dispatch the tasks.
    tasks = [
        watch_resource(client_a, cargs_ns_a),
        watch_resource(client_b, cargs_ns_b),
    ]
    await asyncio.gather(*tasks)

    # Close all pending connections.
    await client_a.close()
    await client_b.close()
Ejemplo n.º 6
0
async def setup():
    config = k8s.utils.load_config(warn=False)
    client = k8s.clients.get_aiohttp(config)
    proxy = k8s.api_proxy.Proxy(config)

    # Specify and dispatch the tasks.
    cargs = k8s.CoreV1Api(proxy).list_namespace(watch=True, timeout_seconds=1)
    tasks = [
        create_deployment(proxy, client),
        watch_resource(client.request(**cargs)),
    ]
    await asyncio.gather(*tasks)

    print('\nShutting down')
    await client.close()
Ejemplo n.º 7
0
async def main():
    # Create a client instance and load the credentials from ~/.kube/kubeconfig
    api_client = k8s.config.new_client_from_config()

    # Namespaces and Pods are part of the K8s Core API.
    corev1 = k8s.CoreV1Api(api_client)

    # Deployments are part of the Extension API (still in beta).
    extv1beta = k8s.ExtensionsV1beta1Api(api_client)

    # Specify and dispatch the tasks.
    tasks = [
        watch_resource(corev1.list_namespace, timeout_seconds=5),
        watch_resource(corev1.list_pod_for_all_namespaces, timeout_seconds=5),
        watch_resource(extv1beta.list_deployment_for_all_namespaces,
                       timeout_seconds=5),
    ]
    await asyncio.gather(*tasks)

    # Close all pending connections.
    await api_client.close()
Ejemplo n.º 8
0
    async def test_exec_ws(self):
        """Verify the Websocket connection sends the correct headers."""

        # Create an ApiClient but close the aiohttp session created in the ctor
        # and replace it with our own Mock.
        api_client = k8s.api_client.ApiClient(
            k8s.configuration.Configuration())
        await api_client.close()
        api_client.session = mock.MagicMock()

        # Make the websocket request through our Mock.
        core_api = k8s.CoreV1Api(api_client=api_client)
        resp = await core_api.connect_get_namespaced_pod_exec(
            'pod',
            'namespace',
            command="mock-command",
            stderr=True,
            stdin=False,
            stdout=True,
            tty=False)

        # The WS connection must have received the correct headers.
        api_client.session.ws_connect.assert_called_once_with(
            'wss://localhost/api/v1/namespaces/namespace/pods/pod/exec?'
            'command=mock-command&stderr=True&stdin=False&stdout=True&tty=False',
            headers={
                'sec-websocket-protocol': 'v4.channel.k8s.io',
                'Accept': '*/*',
                'User-Agent': 'Swagger-Codegen/1.0/python',
                'Content-Type': 'application/json'
            })

        # The response must contain the verbatim response from the Websocket
        # session and no parsed data.
        http = api_client.session.ws_connect.return_value
        self.assertEqual(resp, ApiResponse(http=http, obj=None))
Ejemplo n.º 9
0
async def create_deployment(api_client):
    img_alpine_34, img_alpine_35 = 'alpine:3.4', 'alpine:3.5'
    time_between_steps = 3

    # Create deployment.
    base_path = os.path.dirname(os.path.abspath(__file__))
    fname = os.path.join(base_path, 'manifests/create-deployment.yaml')
    body = yaml.safe_load(open(fname, 'r'))
    name, namespace = body['metadata']['name'], body['metadata']['namespace']
    img_orig = body['spec']['template']['spec']['containers'][0]['image']
    del fname

    body['spec']['template']['spec']['containers'][0]['image'] = img_alpine_34
    img_new = body['spec']['template']['spec']['containers'][0]['image']
    print(f'Replaced <{img_orig}> with <{img_new}>')

    # -------------------------------------------------------------------------
    #                           Create Deployment
    # -------------------------------------------------------------------------
    k8s_v1beta = k8s.ExtensionsV1beta1Api(api_client)
    print(f'Creating deployment {name}...')
    resp = await k8s_v1beta.create_namespaced_deployment(body=body,
                                                         namespace=namespace)
    assert isinstance(resp.http, aiohttp.client_reqrep.ClientResponse)
    print(' ->', resp.http.method, resp.http.status, resp.http.url)

    # -------------------------------------------------------------------------
    #                            Path Deployment
    # -------------------------------------------------------------------------
    # print(f'Patching deployment {name}...')
    # fname = os.path.join(base_path, 'manifests/patch-deployment.yaml')
    # body_patch = yaml.safe_load(open(fname, 'r'))
    # resp = await k8s_v1beta.patch_namespaced_deployment(
    #     name=name, namespace=namespace, body=body_patch)
    # await asyncio.sleep(time_between_steps)

    # -------------------------------------------------------------------------
    #                        Execute Command in Pod
    # -------------------------------------------------------------------------
    print(f'\nConnecting to a login pod: ', end='', flush=True)

    # Try several times to connect to the pod. We have to do this because we
    # only just created the pod and it takes a few seconds until it is ready.
    for i in range(300):
        # Get a list of all pods.
        resp = await k8s.CoreV1Api(api_client).list_namespaced_pod(namespace)
        assert isinstance(resp.http, aiohttp.client_reqrep.ClientResponse)

        # Find all running pods whose name starts with 'login'.
        pods = resp.obj.items
        pods = [_ for _ in pods if _.metadata.name.lower().startswith('login')]
        pods = [_ for _ in pods if _.status.phase.lower() == 'running']

        # Wait, rinse and repeat if Kubernetes returned an error or we found no
        # running login Pod.
        if resp.http.status != 200 or len(pods) == 0:
            print('.', end='', flush=True)
            await asyncio.sleep(0.1)
            continue
        print('\n ->', resp.http.method, resp.http.status, resp.http.url, '\n')

        # Could be a stale deployment - not a problem, but let user know.
        if len(pods) > 1:
            print('Found multiple login pods')

        # Extract the pod name that we will connect to.
        pod_name = pods[0].metadata.name
        print(f'Connecting to Pod <{pod_name}>')

        # Connect to the pod and run a few shell commands. This will return a
        # Websocket connection that we will consume afterwards.
        v1_ws = k8s.CoreV1Api(api_client=api_client)
        exec_command = [
            '/bin/sh', '-c',
            'echo This is stderr >&2; sleep 0s; echo This is stdout'
        ]
        websocket = await v1_ws.connect_get_namespaced_pod_exec(
            pod_name,
            namespace,
            command=exec_command,
            stderr=True,
            stdin=False,
            stdout=True,
            tty=False)
        assert isinstance(websocket.http,
                          aiohttp.client._WSRequestContextManager)

        # Consume the Websocket until all commands have finished and Kubernetes
        # closes the connection.
        async with websocket.http as ws:
            async for msg in ws:
                print(f'  Websocket received: {msg.data}')
        break
    else:
        print('No login has entered "running" state yet: skip connection test')

    # -------------------------------------------------------------------------
    #                          Replace Deployment
    # -------------------------------------------------------------------------
    img_orig = body['spec']['template']['spec']['containers'][0]['image']
    body['spec']['template']['spec']['containers'][0]['image'] = img_alpine_35
    img_new = body['spec']['template']['spec']['containers'][0]['image']
    print(f'Replaced <{img_orig}> with <{img_new}>')

    print(f'\nReplacing deployment {name}...')
    resp = await k8s_v1beta.replace_namespaced_deployment(name=name,
                                                          namespace=namespace,
                                                          body=body)
    assert isinstance(resp.http, aiohttp.client_reqrep.ClientResponse)
    print(' ->', resp.http.method, resp.http.status, resp.http.url)

    # -------------------------------------------------------------------------
    #                           Delete Deployment
    # -------------------------------------------------------------------------
    await asyncio.sleep(time_between_steps)
    print(f'\nDeleting deployment {name}...')
    del_opts = k8s.V1DeleteOptions(
        api_version='v1',
        kind='DeleteOptions',
        grace_period_seconds=0,
        propagation_policy='Foreground',
    )
    resp = await k8s_v1beta.delete_namespaced_deployment(name=name,
                                                         namespace=namespace,
                                                         body=del_opts)
    assert isinstance(resp.http, aiohttp.client_reqrep.ClientResponse)
    print(' ->', resp.http.method, resp.http.status, resp.http.url)

    print('------------ End of Demo ------------')
Ejemplo n.º 10
0
async def create_deployment(proxy, client):
    img_alpine_34, img_alpine_35 = 'alpine:3.4', 'alpine:3.5'
    time_between_steps = 3

    # Load the manifest with a valid Python/Alpine container deployment.
    base_path = os.path.dirname(os.path.abspath(__file__))
    fname = os.path.join(base_path, 'manifests/create-deployment.yaml')
    body = yaml.safe_load(open(fname, 'r'))
    name, namespace = body['metadata']['name'], body['metadata']['namespace']
    img_orig = body['spec']['template']['spec']['containers'][0]['image']
    del fname

    # Assign a specific container image (we will later replace that container
    # and re-deploy to illustrate the workflow).
    body['spec']['template']['spec']['containers'][0]['image'] = img_alpine_34
    img_new = body['spec']['template']['spec']['containers'][0]['image']
    print(f'Replaced <{img_orig}> with <{img_new}>')

    # ----------------------------------------------------------------------
    #                  Create the namespace <aiokubernet>.
    # ----------------------------------------------------------------------
    manifest = {
        'apiVersion': 'v1',
        'kind': 'Namespace',
        'metadata': {
            'name': namespace
        },
    }
    cargs = k8s.api.CoreV1Api(proxy).create_namespace(body=manifest)
    ret = await client.request(**cargs)
    if ret.status == 201:
        print(f'Namespace <{namespace}> created')
    elif ret.status == 409:
        print(f'Namespace <{namespace}> already exists')
    else:
        print(f'Error {ret.status}')
        print(await ret.text())

    # -------------------------------------------------------------------------
    #                           Create Deployment
    # -------------------------------------------------------------------------
    k8s_v1beta = k8s.ExtensionsV1beta1Api(proxy)
    print(f'Creating deployment {name}...')
    cargs = k8s_v1beta.create_namespaced_deployment(body=body,
                                                    namespace=namespace)
    http = await client.request(**cargs)
    print(' ->', http.method, http.status, http.url)
    del cargs, http

    # -------------------------------------------------------------------------
    #                            Patch Deployment
    # -------------------------------------------------------------------------
    # print(f'Patching deployment {name}...')
    # fname = os.path.join(base_path, 'manifests/patch-deployment.yaml')
    # body_patch = yaml.safe_load(open(fname, 'r'))
    # resp = await k8s_v1beta.patch_namespaced_deployment(
    #     name=name, namespace=namespace, body=body_patch)
    # await asyncio.sleep(time_between_steps)

    # -------------------------------------------------------------------------
    #                       Search For The Login Pod
    # -------------------------------------------------------------------------
    print(f'\nConnecting to a login pod: ', end='', flush=True)

    # Try several times to connect to the pod. We have to do this because we
    # only just created the pod and it takes a few seconds until it is ready.
    for i in range(300):
        # Get a list of all pods.
        cargs = k8s.CoreV1Api(proxy).list_namespaced_pod(namespace)
        http = await client.request(**cargs)
        pods = k8s.swagger.unpack(await http.read())

        # Find all running pods whose name starts with 'login'.
        pods = [
            _ for _ in pods.items
            if _.metadata.name.lower().startswith('login')
        ]
        pods = [_ for _ in pods if _.status.phase.lower() == 'running']

        # Briefly wait before looking for a suitable pod, unless we already found one.
        if http.status != 200 or len(pods) == 0:
            print('.', end='', flush=True)
            await asyncio.sleep(0.1)
            continue
        print('\n ->', http.method, http.status, http.url, '\n')
        del cargs, http

        # Could be a stale deployment - not a problem, but let user know.
        if len(pods) > 1:
            print('Found multiple login pods')

        # Extract the pod name that we will connect to.
        login_pod_name = pods[0].metadata.name
        print(f'Connecting to Pod <{login_pod_name}>')
        break
    else:
        login_pod_name = None
        print('No login has entered "running" state yet: skip connection test')

    # -------------------------------------------------------------------------
    #             Execute Command in Login Pod via GET request.
    # -------------------------------------------------------------------------
    if login_pod_name is not None:
        print('\nNon-interactive Websocket')
        # Connect to the pod and print something to the terminal.
        cargs = k8s.CoreV1Api(
            api_client=proxy).connect_get_namespaced_pod_exec(
                login_pod_name,
                namespace,
                command=['/bin/sh', '-c', 'echo Hello World'],
                stderr=True,
                stdin=True,
                stdout=True,
                tty=True)

        # Tell K8s that this is a Websocket, not a GET.
        cargs['headers']['sec-websocket-protocol'] = 'v4.channel.k8s.io'
        url = k8s.api_client.get_websocket_url(cargs['url'])

        # Connect the Websocket and consume the Websocket until Kubernetes closes it.
        ws_session = client.ws_connect(url, headers=cargs['headers'])
        async with ws_session as ws:
            async for msg in ws:
                chan, msg = msg.data[0], msg.data[1:]
                print(f'  Websocket Channel {chan}: {msg}')
        del cargs, url, ws_session

    # -------------------------------------------------------------------------
    #      Use interactive Websocket to execute commands in Login Pod
    # -------------------------------------------------------------------------
    if login_pod_name is not None:
        print('\nInteractive Websocket')

        # Connect to the pod and issue a single command to spawn a shell. We
        # will then use a websocket to send commands to that shell.
        wargs = k8s.CoreV1Api(
            api_client=proxy).connect_get_namespaced_pod_exec(
                login_pod_name,
                namespace,
                command=['/bin/sh'],
                stderr=True,
                stdin=True,
                stdout=True,
                tty=True)

        # Tell K8s that this is a Websocket, not a GET.
        wargs['headers']['sec-websocket-protocol'] = 'v4.channel.k8s.io'
        url = k8s.api_client.get_websocket_url(wargs['url'])

        # Connect the Websocket and consume the Websocket until Kubernetes closes it.
        ws_session = client.ws_connect(url, headers=wargs['headers'])
        async with ws_session as ws:
            # The \x00 prefix denotes `stdin`, which is where we need to send
            # the command to. The rest is just a sequence of two shell commands.
            await ws.send_bytes(b'\x00' + b'ls --color=never /\nexit\n')

            # Read until we receive something on channel 3 to tell us that this was it.
            async for msg in ws:
                chan, msg = msg.data[0], msg.data[1:]
                print(f'  Websocket Channel {chan}: {msg}')

    # -------------------------------------------------------------------------
    #                          Replace Deployment
    # -------------------------------------------------------------------------
    img_orig = body['spec']['template']['spec']['containers'][0]['image']
    body['spec']['template']['spec']['containers'][0]['image'] = img_alpine_35
    img_new = body['spec']['template']['spec']['containers'][0]['image']
    print(f'Replaced <{img_orig}> with <{img_new}>')

    print(f'\nReplacing deployment {name}...')
    cargs = k8s_v1beta.replace_namespaced_deployment(name,
                                                     namespace,
                                                     body=body)
    http = await client.request(**cargs)
    assert isinstance(http, aiohttp.client_reqrep.ClientResponse)
    print(' ->', http.method, http.status, http.url)
    del cargs, http

    # -------------------------------------------------------------------------
    #                           Delete Deployment
    # -------------------------------------------------------------------------
    await asyncio.sleep(time_between_steps)
    print(f'\nDeleting deployment {name}...')
    del_opts = k8s.V1DeleteOptions(
        api_version='v1',
        kind='DeleteOptions',
        grace_period_seconds=0,
        propagation_policy='Foreground',
    )
    cargs = k8s_v1beta.delete_namespaced_deployment(name,
                                                    namespace,
                                                    body=del_opts)
    http = await client.request(**cargs)
    assert isinstance(http, aiohttp.client_reqrep.ClientResponse)
    print(' ->', http.method, http.status, http.url)
    del cargs, http

    print('------------ End of Demo ------------')