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

Reply via email to