Skip to content

Commit

Permalink
Merge pull request #22 from fiaas/configmap-and-secret-improvements
Browse files Browse the repository at this point in the history
Add support for envFrom and optional configmaps and secrets
  • Loading branch information
mortenlj authored Oct 13, 2017
2 parents 66118a7 + c2fab1c commit f65ceee
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 134 deletions.
20 changes: 20 additions & 0 deletions k8s/models/pod.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ class EnvVarSource(Model):
secretKeyRef = Field(SecretKeySelector)


class SecretEnvSource(Model):
name = Field(six.text_type)
optional = Field(bool)


class ConfigMapEnvSource(Model):
name = Field(six.text_type)
optional = Field(bool)


class EnvFromSource(Model):
configMapRef = Field(ConfigMapEnvSource)
secretRef = Field(SecretEnvSource)


class EnvVar(Model):
name = Field(six.text_type)
value = Field(six.text_type)
Expand Down Expand Up @@ -97,6 +112,7 @@ class Container(Model):
image = Field(six.text_type)
ports = ListField(ContainerPort)
env = ListField(EnvVar)
envFrom = ListField(EnvFromSource)
resources = Field(ResourceRequirements)
volumeMounts = ListField(VolumeMount)
livenessProbe = Field(Probe)
Expand All @@ -106,6 +122,8 @@ class Container(Model):

class SecretVolumeSource(Model):
secretName = Field(six.text_type)
optional = Field(bool)
defaultMode = Field(int)


class KeyToPath(Model):
Expand All @@ -115,6 +133,8 @@ class KeyToPath(Model):

class ConfigMapVolumeSource(Model):
name = Field(six.text_type)
optional = Field(bool)
defaultMode = Field(int)


class EmptyDirVolumeSource(Model):
Expand Down
150 changes: 117 additions & 33 deletions tests/k8s/test_pod.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,129 @@
#!/usr/bin/env python
# -*- coding: utf-8
from __future__ import absolute_import, unicode_literals

from pprint import pformat

import mock
import pytest
from k8s.models.common import ObjectMeta
from k8s.models.pod import Pod, ContainerPort, Container, LocalObjectReference, PodSpec, Volume, VolumeMount, SecretVolumeSource
from util import get_vcr

vcr = get_vcr(__file__)
from k8s.client import NotFound
from k8s.models.common import ObjectMeta
from k8s.models.pod import Pod, ContainerPort, Container, LocalObjectReference, PodSpec, Volume, VolumeMount, \
SecretVolumeSource

NAME = "my-name"
NAMESPACE = "my-namespace"
POD_URI = Pod._meta.url_template.format(name="", namespace=NAMESPACE)


@pytest.mark.usefixtures("logger", "k8s_config")
class TestPod(object):
@vcr.use_cassette()
def teardown(self):
for name, namespace in ((NAME, "default"), (NAME, NAMESPACE)):
try:
Pod.delete(name, namespace)
except:
pass

@vcr.use_cassette()
def test_lifecycle(self, logger):
object_meta = ObjectMeta(name=NAME, namespace=NAMESPACE, labels={"test": "true", "app": NAME})
container_port = ContainerPort(name="http5000", containerPort=5000)
secrets_volume_mounts = [VolumeMount(name=NAME, readOnly=True, mountPath="/var/run/secrets/kubernetes.io/kubernetes-secrets")]
secret_volumes = [Volume(name=NAME, secret=SecretVolumeSource(secretName=NAME))]
container = Container(name="container", image="dummy_image", ports=[container_port], volumeMounts=secrets_volume_mounts)
image_pull_secret = LocalObjectReference(name="image_pull_secret")
pod_spec = PodSpec(containers=[container], imagePullSecrets=[image_pull_secret],
volumes=secret_volumes, serviceAccountName="default")
first = Pod(metadata=object_meta, spec=pod_spec)
logger.debug(pformat(first.as_dict()))
first.save()

pods = Pod.find(NAME, NAMESPACE)
assert len(pods) == 1
second = pods[0]
assert first.metadata.name == second.metadata.name
assert first.metadata.namespace == second.metadata.namespace
def test_create_blank_pod(self):
pod = _create_pod()
assert pod.metadata.name == NAME
assert pod.as_dict()["metadata"]["name"] == NAME

def test_pod_created_if_not_exists(self, post, api_get):
api_get.side_effect = NotFound()
pod = _create_pod()
call_params = pod.as_dict()
assert pod._new
pod.save()
assert not pod._new
pytest.helpers.assert_any_call(post, POD_URI, call_params)

def test_get_or_create_pod_not_new(self, put, get):
mock_response = mock.Mock()
mock_response.json.return_value = {
'apiVersion': 'v1',
'kind': 'Pod',
'metadata': {
'creationTimestamp': '2017-10-03T10:36:20Z',
'labels': {
'app': 'my-name', 'test': 'true'
},
'name': 'my-name',
'namespace': 'my-namespace',
'resourceVersion': '852',
'selfLink': '/api/v1/namespaces/my-namespace/pods/my-name',
'uid': 'b1e35ab5-a826-11e7-ba76-0800273598c9'
},
'spec': {
'containers': [{
'image': 'dummy_image',
'imagePullPolicy': 'IfNotPresent',
'name': 'container',
'ports': [{
'containerPort': 5000,
'name': 'http5000',
'protocol': 'TCP'
}],
'resources': {},
'volumeMounts': [{
'mountPath': '/var/run/secrets/kubernetes.io/kubernetes-secrets',
'name': 'my-name',
'readOnly': True
}, {
'mountPath': '/var/run/secrets/kubernetes.io/serviceaccount',
'name': 'default-token-0g73b',
'readOnly': True
}]
}],
'imagePullSecrets': [{
'name': 'image_pull_secret'
}],
'restartPolicy': 'Always',
'serviceAccount': 'default',
'serviceAccountName': 'default',
'terminationGracePeriodSeconds': 30,
'volumes': [{
'name': 'my-name',
'secret': {
'defaultMode': 420,
'secretName': 'my-name'
}}, {
'name': 'default-token-0g73b',
'secret': {
'defaultMode': 420,
'secretName': 'default-token-0g73b'
}}]
},
'status': {
'conditions': [{
'lastProbeTime': None,
'lastTransitionTime': '2017-10-03T10:36:20Z',
'status': 'True',
'type': 'PodScheduled'
}],
'phase': 'Pending',
'qosClass': 'BestEffort'
}
}
get.return_value = mock_response
pod = Pod.get(name=NAME, namespace=NAMESPACE)
assert not pod._new
assert pod.metadata.name == NAME
assert pod.spec.containers[0].ports[0].name == "http5000"
call_params = pod.as_dict()
pod.save()
pytest.helpers.assert_any_call(put, POD_URI + NAME, call_params)

def test_pod_deleted(self, delete):
Pod.delete(NAME, NAMESPACE)

# call delete with service_name
pytest.helpers.assert_any_call(delete, POD_URI + NAME)


def _create_pod():
object_meta = ObjectMeta(name=NAME, namespace=NAMESPACE, labels={"test": "true", "app": NAME})
container_port = ContainerPort(name="http5000", containerPort=5000)
secrets_volume_mounts = [
VolumeMount(name=NAME, readOnly=True, mountPath="/var/run/secrets/kubernetes.io/kubernetes-secrets")]
secret_volumes = [Volume(name=NAME, secret=SecretVolumeSource(secretName=NAME))]
container = Container(name="container", image="dummy_image", ports=[container_port],
volumeMounts=secrets_volume_mounts)
image_pull_secret = LocalObjectReference(name="image_pull_secret")
pod_spec = PodSpec(containers=[container], imagePullSecrets=[image_pull_secret],
volumes=secret_volumes, serviceAccountName="default")
first = Pod(metadata=object_meta, spec=pod_spec)
return first
44 changes: 0 additions & 44 deletions tests/k8s/test_pod/teardown.yaml

This file was deleted.

48 changes: 0 additions & 48 deletions tests/k8s/test_pod/test_lifecycle.yaml

This file was deleted.

2 changes: 1 addition & 1 deletion tests/k8s/test_replication_controller/test_lifecycle.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ interactions:
"volumes": [], "initContainers": [], "imagePullSecrets": [{"name": "image_pull_secret"}],
"containers": [{"livenessProbe": {"initialDelaySeconds": 5, "httpGet": {"path":
"/", "scheme": "HTTP", "port": "http5000"}}, "name": "container", "image": "dummy_image",
"volumeMounts": [], "env": [], "imagePullPolicy": "IfNotPresent", "readinessProbe":
"volumeMounts": [], "env": [], "envFrom": [], "imagePullPolicy": "IfNotPresent", "readinessProbe":
{"initialDelaySeconds": 5, "tcpSocket": {"port": 5000}}, "ports": [{"protocol":
"TCP", "containerPort": 5000, "name": "http5000"}]}]}, "metadata": {"labels":
{"test": "true"}, "namespace": "my-namespace", "name": "my-name"}}, "selector":
Expand Down
11 changes: 3 additions & 8 deletions tests/k8s/test_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import mock
import pytest

from k8s.client import NotFound
from k8s.models.common import ObjectMeta
from k8s.models.service import Service, ServicePort, ServiceSpec
Expand Down Expand Up @@ -33,19 +34,15 @@ def test_create_blank_object_meta(self):
}
}

@mock.patch('k8s.base.ApiMixIn.get')
@mock.patch('k8s.client.Client.post')
def test_service_created_if_not_exists(self, post, get):
get.side_effect = NotFound()
def test_service_created_if_not_exists(self, post, api_get):
api_get.side_effect = NotFound()
service = create_default_service()
call_params = service.as_dict()
assert service._new
service.save()
assert not service._new
pytest.helpers.assert_any_call(post, SERVICES_URI, call_params)

@mock.patch('k8s.client.Client.get')
@mock.patch('k8s.client.Client.put')
def test_get_or_create_service_not_new(self, put, get):
service = create_default_service()

Expand Down Expand Up @@ -88,14 +85,12 @@ def test_get_or_create_service_not_new(self, put, get):
from_api.save()
pytest.helpers.assert_any_call(put, SERVICES_URI + SERVICE_NAME, call_params)

@mock.patch('k8s.client.Client.delete')
def test_service_deleted(self, delete):
Service.delete(SERVICE_NAME, SERVICE_NAMESPACE)

# call delete with service_name
pytest.helpers.assert_any_call(delete, (SERVICES_URI + SERVICE_NAME))

@mock.patch('k8s.client.Client.get')
def test_list_services(self, get):
service_list = {
"apiVersion": "v1",
Expand Down

0 comments on commit f65ceee

Please sign in to comment.