Start of a kubernetes server running against an unauthenticated host, also with support for basic http auth
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/936fe421 Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/936fe421 Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/936fe421 Branch: refs/heads/trunk Commit: 936fe42152217e0a35eb3cf52ffe155b518cd8c4 Parents: f3943ea Author: anthony-shaw <[email protected]> Authored: Wed Jan 13 14:46:08 2016 +1100 Committer: anthony-shaw <[email protected]> Committed: Wed Jan 13 14:46:08 2016 +1100 ---------------------------------------------------------------------- libcloud/container/base.py | 12 + libcloud/container/drivers/kubernetes.py | 310 +++++++++++++++++++ libcloud/container/providers.py | 2 + libcloud/container/types.py | 1 + .../fixtures/kubernetes/_api_v1_namespaces.json | 44 +++ .../kubernetes/_api_v1_namespaces_default.json | 19 ++ .../_api_v1_namespaces_default_DELETE.json | 20 ++ .../kubernetes/_api_v1_namespaces_test.json | 19 ++ .../fixtures/kubernetes/_api_v1_nodes.json | 81 +++++ .../kubernetes/_api_v1_nodes_127_0_0_1.json | 73 +++++ libcloud/test/container/test_kubernetes.py | 125 ++++++++ libcloud/test/secrets.py-dist | 1 + 12 files changed, 707 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/936fe421/libcloud/container/base.py ---------------------------------------------------------------------- diff --git a/libcloud/container/base.py b/libcloud/container/base.py index a03ea91..ea41e50 100644 --- a/libcloud/container/base.py +++ b/libcloud/container/base.py @@ -402,3 +402,15 @@ class ContainerDriver(BaseDriver): """ raise NotImplementedError( 'list_clusters not implemented for this driver') + + def get_cluster(self, id): + """ + Get a cluster by ID + + :param id: The ID of the cluster to get + :type id: ``str`` + + :rtype: :class:`.ContainerCluster` + """ + raise NotImplementedError( + 'list_clusters not implemented for this driver') http://git-wip-us.apache.org/repos/asf/libcloud/blob/936fe421/libcloud/container/drivers/kubernetes.py ---------------------------------------------------------------------- diff --git a/libcloud/container/drivers/kubernetes.py b/libcloud/container/drivers/kubernetes.py new file mode 100644 index 0000000..a947fd2 --- /dev/null +++ b/libcloud/container/drivers/kubernetes.py @@ -0,0 +1,310 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import base64 +import datetime + +try: + import simplejson as json +except: + import json + +from libcloud.utils.py3 import httplib +from libcloud.utils.py3 import b + +from libcloud.common.base import JsonResponse, ConnectionUserAndKey +from libcloud.common.types import InvalidCredsError + +from libcloud.container.base import (Container, ContainerDriver, + ContainerCluster) + +from libcloud.container.providers import Provider +from libcloud.container.types import ContainerState + + +VALID_RESPONSE_CODES = [httplib.OK, httplib.ACCEPTED, httplib.CREATED, + httplib.NO_CONTENT] + +ROOT_URL = '/api/' + + +class KubernetesResponse(JsonResponse): + + valid_response_codes = [httplib.OK, httplib.ACCEPTED, httplib.CREATED, + httplib.NO_CONTENT] + + def parse_error(self): + if self.status == 401: + raise InvalidCredsError('Invalid credentials') + return self.body + + def success(self): + return self.status in self.valid_response_codes + + +class KubernetesException(Exception): + + def __init__(self, code, message): + self.code = code + self.message = message + self.args = (code, message) + + def __str__(self): + return "%s %s" % (self.code, self.message) + + def __repr__(self): + return "KubernetesException %s %s" % (self.code, self.message) + + +class KubernetesConnection(ConnectionUserAndKey): + responseCls = KubernetesResponse + timeout = 60 + + def add_default_headers(self, headers): + """ + Add parameters that are necessary for every request + If user and password are specified, include a base http auth + header + """ + headers['Content-Type'] = 'application/json' + if self.key and self.secret: + user_b64 = base64.b64encode(b('%s:%s' % (self.key, self.secret))) + headers['Authorization'] = 'Basic %s' % (user_b64.decode('utf-8')) + return headers + + +class KubernetesContainerDriver(ContainerDriver): + type = Provider.KUBERNETES + name = 'Kubernetes' + website = 'http://kubernetes.io' + connectionCls = KubernetesConnection + supports_clusters = True + + def __init__(self, key=None, secret=None, secure=False, host='localhost', + port=4243, key_file=None, cert_file=None): + """ + :param key: API key or username to used (required) + :type key: ``str`` + + :param secret: Secret password to be used (required) + :type secret: ``str`` + + :param secure: Whether to use HTTPS or HTTP. Note: Some providers + only support HTTPS, and it is on by default. + :type secure: ``bool`` + + :param host: Override hostname used for connections. + :type host: ``str`` + + :param port: Override port used for connections. + :type port: ``int`` + + :param key_file: Path to private key for TLS connection (optional) + :type key_file: ``str`` + + :param cert_file: Path to public key for TLS connection (optional) + :type cert_file: ``str`` + + :return: ``None`` + """ + super(KubernetesContainerDriver, self).__init__(key=key, secret=secret, + secure=secure, + host=host, + port=port, + key_file=key_file, + cert_file=cert_file) + if host.startswith('https://'): + secure = True + + # strip the prefix + prefixes = ['http://', 'https://'] + for prefix in prefixes: + if host.startswith(prefix): + host = host.strip(prefix) + + if key_file or cert_file: + # docker tls authentication- + # https://docs.docker.com/articles/https/ + # We pass two files, a key_file with the + # private key and cert_file with the certificate + # libcloud will handle them through LibcloudHTTPSConnection + if not (key_file and cert_file): + raise Exception( + 'Needs both private key file and ' + 'certificate file for tls authentication') + self.connection.key_file = key_file + self.connection.cert_file = cert_file + self.connection.secure = True + else: + self.connection.secure = secure + self.connection.key = key + self.connection.secret = secret + + self.connection.host = host + self.connection.port = port + + def list_containers(self, image=None, all=True): + """ + List the deployed container images + + :param image: Filter to containers with a certain image + :type image: :class:`libcloud.container.base.ContainerImage` + + :param all: Show all container (including stopped ones) + :type all: ``bool`` + + :rtype: ``list`` of :class:`libcloud.container.base.Container` + """ + try: + result = self.connection.request( + ROOT_URL + "v1/nodes/").object + except Exception as exc: + if hasattr(exc, 'errno') and exc.errno == 111: + raise KubernetesException( + exc.errno, + 'Make sure kube host is accessible' + 'and the API port is correct') + raise + + containers = [self._to_container(value) for value in result['items']] + return containers + + def get_container(self, id): + """ + Get a container by ID + + :param id: The ID of the container to get + :type id: ``str`` + + :rtype: :class:`libcloud.container.base.Container` + """ + result = self.connection.request(ROOT_URL + "v1/nodes/%s" % + id).object + + return self._to_container(result) + + def list_clusters(self): + """ + Get a list of namespaces that pods can be deployed into + + :param location: The location to search in + :type location: :class:`libcloud.container.base.ClusterLocation` + + :rtype: ``list`` of :class:`libcloud.container.base.ContainerCluster` + """ + try: + result = self.connection.request( + ROOT_URL + "v1/namespaces/").object + except Exception as exc: + if hasattr(exc, 'errno') and exc.errno == 111: + raise KubernetesException( + exc.errno, + 'Make sure kube host is accessible' + 'and the API port is correct') + raise + + clusters = [self._to_cluster(value) for value in result['items']] + return clusters + + def get_cluster(self, id): + """ + Get a cluster by ID + + :param id: The ID of the cluster to get + :type id: ``str`` + + :rtype: :class:`libcloud.container.base.ContainerCluster` + """ + result = self.connection.request(ROOT_URL + "v1/namespaces/%s" % + id).object + + return self._to_cluster(result) + + def destroy_cluster(self, cluster): + """ + Delete a cluster (namespace) + + :return: ``True`` if the destroy was successful, otherwise ``False``. + :rtype: ``bool`` + """ + self.connection.request(ROOT_URL + "v1/namespaces/%s" % + cluster.id, method='DELETE').object + return True + + def create_cluster(self, name, location=None): + """ + Create a container cluster (a namespace) + + :param name: The name of the cluster + :type name: ``str`` + + :param location: The location to create the cluster in + :type location: :class:`.ClusterLocation` + + :rtype: :class:`.ContainerCluster` + """ + request = { + 'metadata': { + 'name': name + } + } + result = self.connection.request(ROOT_URL + "v1/namespaces", + method='POST', + data=json.dumps(request)).object + return self._to_cluster(result) + + def _to_container(self, data): + """ + Convert container in Container instances + """ + metadata = data['metadata'] + return Container( + id=data['spec']['externalID'], + name=metadata['name'], + image=None, + ip_addresses="ips", + state=ContainerState.RUNNING, + driver=self.connection.driver, + extra=None) + + def _to_cluster(self, data): + """ + Convert namespace to a cluster + """ + metadata = data['metadata'] + status = data['status'] + return ContainerCluster( + id=metadata['name'], + name=metadata['name'], + driver=self.connection.driver, + extra={'phase': status['phase']}) + + def _get_api_version(self): + """ + Get the docker API version information + """ + result = self.connection.request('/version').object + api_version = result.get('ApiVersion') + + return api_version + + +def ts_to_str(timestamp): + """ + Return a timestamp as a nicely formated datetime string. + """ + date = datetime.datetime.fromtimestamp(timestamp) + date_string = date.strftime("%d/%m/%Y %H:%M %Z") + return date_string http://git-wip-us.apache.org/repos/asf/libcloud/blob/936fe421/libcloud/container/providers.py ---------------------------------------------------------------------- diff --git a/libcloud/container/providers.py b/libcloud/container/providers.py index 458eae8..c39566f 100644 --- a/libcloud/container/providers.py +++ b/libcloud/container/providers.py @@ -26,6 +26,8 @@ DRIVERS = { ('libcloud.container.drivers.joyent', 'JoyentContainerDriver'), Provider.ECS: ('libcloud.container.drivers.ecs', 'ElasticContainerDriver'), + Provider.KUBERNETES: + ('libcloud.container.drivers.kubernetes', 'KubenetesContainerDriver'), } http://git-wip-us.apache.org/repos/asf/libcloud/blob/936fe421/libcloud/container/types.py ---------------------------------------------------------------------- diff --git a/libcloud/container/types.py b/libcloud/container/types.py index 6236468..263d9d4 100644 --- a/libcloud/container/types.py +++ b/libcloud/container/types.py @@ -44,6 +44,7 @@ class Provider(object): DOCKER = 'docker' JOYENT = 'joyent' ECS = 'ecs' + KUBERNETES = 'kubernetes' class ContainerState(Type): http://git-wip-us.apache.org/repos/asf/libcloud/blob/936fe421/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces.json ---------------------------------------------------------------------- diff --git a/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces.json b/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces.json new file mode 100644 index 0000000..38e8bec --- /dev/null +++ b/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces.json @@ -0,0 +1,44 @@ +{ + "kind": "NamespaceList", + "apiVersion": "v1", + "metadata": { + "selfLink": "/api/v1/namespaces", + "resourceVersion": "443" + }, + "items": [ + { + "metadata": { + "name": "default", + "selfLink": "/api/v1/namespaces/default", + "uid": "43e99cf9-b99d-11e5-8d53-0050568157ec", + "resourceVersion": "6", + "creationTimestamp": "2016-01-13T02:28:00Z" + }, + "spec": { + "finalizers": [ + "kubernetes" + ] + }, + "status": { + "phase": "Active" + } + }, + { + "metadata": { + "name": "test", + "selfLink": "/api/v1/namespaces/test", + "uid": "7cb89199-b9a6-11e5-8d53-0050568157ec", + "resourceVersion": "419", + "creationTimestamp": "2016-01-13T03:34:01Z" + }, + "spec": { + "finalizers": [ + "kubernetes" + ] + }, + "status": { + "phase": "Active" + } + } + ] +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/936fe421/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces_default.json ---------------------------------------------------------------------- diff --git a/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces_default.json b/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces_default.json new file mode 100644 index 0000000..d59d2c1 --- /dev/null +++ b/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces_default.json @@ -0,0 +1,19 @@ +{ + "kind": "Namespace", + "apiVersion": "v1", + "metadata": { + "name": "default", + "selfLink": "/api/v1/namespaces/default", + "uid": "43e99cf9-b99d-11e5-8d53-0050568157ec", + "resourceVersion": "6", + "creationTimestamp": "2016-01-13T02:28:00Z" + }, + "spec": { + "finalizers": [ + "kubernetes" + ] + }, + "status": { + "phase": "Active" + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/936fe421/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces_default_DELETE.json ---------------------------------------------------------------------- diff --git a/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces_default_DELETE.json b/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces_default_DELETE.json new file mode 100644 index 0000000..8b47e26 --- /dev/null +++ b/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces_default_DELETE.json @@ -0,0 +1,20 @@ +{ + "kind": "Namespace", + "apiVersion": "v1", + "metadata": { + "name": "test", + "selfLink": "/api/v1/namespaces/test", + "uid": "7cb89199-b9a6-11e5-8d53-0050568157ec", + "resourceVersion": "447", + "creationTimestamp": "2016-01-13T03:34:01Z", + "deletionTimestamp": "2016-01-13T03:38:05Z" + }, + "spec": { + "finalizers": [ + "kubernetes" + ] + }, + "status": { + "phase": "Terminating" + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/936fe421/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces_test.json ---------------------------------------------------------------------- diff --git a/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces_test.json b/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces_test.json new file mode 100644 index 0000000..fddefa0 --- /dev/null +++ b/libcloud/test/container/fixtures/kubernetes/_api_v1_namespaces_test.json @@ -0,0 +1,19 @@ +{ + "kind": "Namespace", + "apiVersion": "v1", + "metadata": { + "name": "test", + "selfLink": "/api/v1/namespaces/test", + "uid": "7cb89199-b9a6-11e5-8d53-0050568157ec", + "resourceVersion": "419", + "creationTimestamp": "2016-01-13T03:34:01Z" + }, + "spec": { + "finalizers": [ + "kubernetes" + ] + }, + "status": { + "phase": "Active" + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/936fe421/libcloud/test/container/fixtures/kubernetes/_api_v1_nodes.json ---------------------------------------------------------------------- diff --git a/libcloud/test/container/fixtures/kubernetes/_api_v1_nodes.json b/libcloud/test/container/fixtures/kubernetes/_api_v1_nodes.json new file mode 100644 index 0000000..2664914 --- /dev/null +++ b/libcloud/test/container/fixtures/kubernetes/_api_v1_nodes.json @@ -0,0 +1,81 @@ +{ + "kind": "NodeList", + "apiVersion": "v1", + "metadata": { + "selfLink": "/api/v1/nodes", + "resourceVersion": "24" + }, + "items": [ + { + "metadata": { + "name": "127.0.0.1", + "selfLink": "/api/v1/nodes/127.0.0.1", + "uid": "45949cbb-b99d-11e5-8d53-0050568157ec", + "resourceVersion": "24", + "creationTimestamp": "2016-01-13T02:28:03Z", + "labels": { + "kubernetes.io/hostname": "127.0.0.1" + } + }, + "spec": { + "externalID": "127.0.0.1" + }, + "status": { + "capacity": { + "cpu": "2", + "memory": "4048236Ki", + "pods": "40" + }, + "allocatable": { + "cpu": "2", + "memory": "4048236Ki", + "pods": "40" + }, + "conditions": [ + { + "type": "OutOfDisk", + "status": "False", + "lastHeartbeatTime": "2016-01-13T02:28:53Z", + "lastTransitionTime": "2016-01-13T02:28:03Z", + "reason": "KubeletHasSufficientDisk", + "message": "kubelet has sufficient disk space available" + }, + { + "type": "Ready", + "status": "True", + "lastHeartbeatTime": "2016-01-13T02:28:53Z", + "lastTransitionTime": "2016-01-13T02:28:03Z", + "reason": "KubeletReady", + "message": "kubelet is posting ready status" + } + ], + "addresses": [ + { + "type": "LegacyHostIP", + "address": "127.0.0.1" + }, + { + "type": "InternalIP", + "address": "127.0.0.1" + } + ], + "daemonEndpoints": { + "kubeletEndpoint": { + "Port": 10250 + } + }, + "nodeInfo": { + "machineID": "1d9faaba9168d4b4a3416e99000002a2", + "systemUUID": "42015AA3-9AF2-D089-9105-63CEF89EFE35", + "bootID": "c31fdd67-f995-4be5-942d-10df26b40501", + "kernelVersion": "3.13.0-46-generic", + "osImage": "Ubuntu 14.04.2 LTS", + "containerRuntimeVersion": "docker://1.9.1", + "kubeletVersion": "v1.2.0-alpha.5.848+3f2e99b7e7d6d8", + "kubeProxyVersion": "v1.2.0-alpha.5.848+3f2e99b7e7d6d8" + }, + "images": null + } + } + ] +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/936fe421/libcloud/test/container/fixtures/kubernetes/_api_v1_nodes_127_0_0_1.json ---------------------------------------------------------------------- diff --git a/libcloud/test/container/fixtures/kubernetes/_api_v1_nodes_127_0_0_1.json b/libcloud/test/container/fixtures/kubernetes/_api_v1_nodes_127_0_0_1.json new file mode 100644 index 0000000..1077b73 --- /dev/null +++ b/libcloud/test/container/fixtures/kubernetes/_api_v1_nodes_127_0_0_1.json @@ -0,0 +1,73 @@ +{ + "kind": "Node", + "apiVersion": "v1", + "metadata": { + "name": "127.0.0.1", + "selfLink": "/api/v1/nodes/127.0.0.1", + "uid": "45949cbb-b99d-11e5-8d53-0050568157ec", + "resourceVersion": "184", + "creationTimestamp": "2016-01-13T02:28:03Z", + "labels": { + "kubernetes.io/hostname": "127.0.0.1" + } + }, + "spec": { + "externalID": "127.0.0.1" + }, + "status": { + "capacity": { + "cpu": "2", + "memory": "4048236Ki", + "pods": "40" + }, + "allocatable": { + "cpu": "2", + "memory": "4048236Ki", + "pods": "40" + }, + "conditions": [ + { + "type": "OutOfDisk", + "status": "False", + "lastHeartbeatTime": "2016-01-13T02:55:34Z", + "lastTransitionTime": "2016-01-13T02:28:03Z", + "reason": "KubeletHasSufficientDisk", + "message": "kubelet has sufficient disk space available" + }, + { + "type": "Ready", + "status": "True", + "lastHeartbeatTime": "2016-01-13T02:55:34Z", + "lastTransitionTime": "2016-01-13T02:28:03Z", + "reason": "KubeletReady", + "message": "kubelet is posting ready status" + } + ], + "addresses": [ + { + "type": "LegacyHostIP", + "address": "127.0.0.1" + }, + { + "type": "InternalIP", + "address": "127.0.0.1" + } + ], + "daemonEndpoints": { + "kubeletEndpoint": { + "Port": 10250 + } + }, + "nodeInfo": { + "machineID": "1d9faaba9168d4b4a3416e99000002a2", + "systemUUID": "42015AA3-9AF2-D089-9105-63CEF89EFE35", + "bootID": "c31fdd67-f995-4be5-942d-10df26b40501", + "kernelVersion": "3.13.0-46-generic", + "osImage": "Ubuntu 14.04.2 LTS", + "containerRuntimeVersion": "docker://1.9.1", + "kubeletVersion": "v1.2.0-alpha.5.848+3f2e99b7e7d6d8", + "kubeProxyVersion": "v1.2.0-alpha.5.848+3f2e99b7e7d6d8" + }, + "images": null + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/936fe421/libcloud/test/container/test_kubernetes.py ---------------------------------------------------------------------- diff --git a/libcloud/test/container/test_kubernetes.py b/libcloud/test/container/test_kubernetes.py new file mode 100644 index 0000000..2e8da4e --- /dev/null +++ b/libcloud/test/container/test_kubernetes.py @@ -0,0 +1,125 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +from libcloud.test import unittest + +from libcloud.container.drivers.kubernetes import KubernetesContainerDriver + +from libcloud.utils.py3 import httplib +from libcloud.test.secrets import CONTAINER_PARAMS_KUBERNETES +from libcloud.test.file_fixtures import ContainerFileFixtures +from libcloud.test import MockHttp + + +class KubernetesContainerDriverTestCase(unittest.TestCase): + + def setUp(self): + KubernetesContainerDriver.connectionCls.conn_classes = ( + KubernetesMockHttp, KubernetesMockHttp) + KubernetesMockHttp.type = None + KubernetesMockHttp.use_param = 'a' + self.driver = KubernetesContainerDriver(*CONTAINER_PARAMS_KUBERNETES) + + def test_list_containers(self): + containers = self.driver.list_containers() + self.assertEqual(len(containers), 1) + self.assertEqual(containers[0].id, + '127.0.0.1') + self.assertEqual(containers[0].name, '127.0.0.1') + + def test_get_container(self): + container = self.driver.get_container('127.0.0.1') + self.assertEqual(container.id, + '127.0.0.1') + self.assertEqual(container.name, '127.0.0.1') + + def test_list_clusters(self): + clusters = self.driver.list_clusters() + self.assertEqual(len(clusters), 2) + self.assertEqual(clusters[0].id, + 'default') + self.assertEqual(clusters[0].name, 'default') + + def test_get_cluster(self): + cluster = self.driver.get_cluster('default') + self.assertEqual(cluster.id, + 'default') + self.assertEqual(cluster.name, 'default') + + def test_create_cluster(self): + cluster = self.driver.create_cluster('test') + self.assertEqual(cluster.id, + 'test') + self.assertEqual(cluster.name, 'test') + + def test_destroy_cluster(self): + cluster = self.driver.get_cluster('default') + result = self.driver.destroy_cluster(cluster) + self.assertTrue(result) + + +class KubernetesMockHttp(MockHttp): + fixtures = ContainerFileFixtures('kubernetes') + + def _version( + self, method, url, body, headers): + if method == 'GET': + body = self.fixtures.load('version.json') + else: + raise AssertionError('Unsupported method') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _api_v1_nodes( + self, method, url, body, headers): + if method == 'GET': + body = self.fixtures.load('_api_v1_nodes.json') + else: + raise AssertionError('Unsupported method') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _api_v1_nodes_127_0_0_1( + self, method, url, body, headers): + if method == 'GET': + body = self.fixtures.load('_api_v1_nodes_127_0_0_1.json') + else: + raise AssertionError('Unsupported method') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _api_v1_namespaces( + self, method, url, body, headers): + if method == 'GET': + body = self.fixtures.load('_api_v1_namespaces.json') + elif method == 'POST': + body = self.fixtures.load('_api_v1_namespaces_test.json') + elif method == 'DELETE': + body = self.fixtures.load('_api_v1_namespaces_DELETE.json') + else: + raise AssertionError('Unsupported method') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _api_v1_namespaces_default( + self, method, url, body, headers): + if method == 'GET': + body = self.fixtures.load('_api_v1_namespaces_default.json') + elif method == 'DELETE': + body = self.fixtures.load('_api_v1_namespaces_default_DELETE.json') + else: + raise AssertionError('Unsupported method') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + +if __name__ == '__main__': + sys.exit(unittest.main()) http://git-wip-us.apache.org/repos/asf/libcloud/blob/936fe421/libcloud/test/secrets.py-dist ---------------------------------------------------------------------- diff --git a/libcloud/test/secrets.py-dist b/libcloud/test/secrets.py-dist index 230cef0..216a7d5 100644 --- a/libcloud/test/secrets.py-dist +++ b/libcloud/test/secrets.py-dist @@ -85,3 +85,4 @@ DNS_PARAMS_AURORADNS = ('apikey', 'secretkey') # Container CONTAINER_PARAMS_DOCKER = ('user', 'password') CONTAINER_PARAMS_ECS = ('user', 'password', 'region') +CONTAINER_PARAMS_KUBERNETES = ('user', 'password')
