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()
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()
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 ------------')
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 ------------')