Skip to content

Commit b8cb6e8

Browse files
authored
Merge pull request #795 from oz123/util-improvements
Allow creation from dict
2 parents 693f115 + e867c29 commit b8cb6e8

File tree

4 files changed

+120
-46
lines changed

4 files changed

+120
-46
lines changed

kubernetes/e2e_test/test_utils.py

+25-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414

1515
import unittest
1616

17+
import yaml
1718
from kubernetes import utils, client
19+
from kubernetes.client.rest import ApiException
1820
from kubernetes.e2e_test import base
1921

2022

@@ -48,8 +50,30 @@ def test_create_apps_deployment_from_yaml(self):
4850
dep = app_api.read_namespaced_deployment(name="nginx-app",
4951
namespace="default")
5052
self.assertIsNotNone(dep)
53+
while True:
54+
try:
55+
app_api.delete_namespaced_deployment(
56+
name="nginx-app", namespace="default",
57+
body={})
58+
break
59+
except ApiException:
60+
continue
61+
62+
def test_create_apps_deployment_from_yaml_obj(self):
63+
k8s_client = client.api_client.ApiClient(configuration=self.config)
64+
with open(self.path_prefix + "apps-deployment.yaml") as f:
65+
yml_obj = yaml.safe_load(f)
66+
67+
yml_obj["metadata"]["name"] = "nginx-app-3"
68+
69+
utils.create_from_dict(k8s_client, yml_obj)
70+
71+
app_api = client.AppsV1beta1Api(k8s_client)
72+
dep = app_api.read_namespaced_deployment(name="nginx-app-3",
73+
namespace="default")
74+
self.assertIsNotNone(dep)
5175
app_api.delete_namespaced_deployment(
52-
name="nginx-app", namespace="default",
76+
name="nginx-app-3", namespace="default",
5377
body={})
5478

5579
def test_create_extensions_deployment_from_yaml(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
apiVersion: apps/v1beta1
2+
kind: Deployment
3+
metadata:
4+
name: nginx-app-2
5+
labels:
6+
app: nginx
7+
spec:
8+
replicas: 3
9+
selector:
10+
matchLabels:
11+
app: nginx
12+
template:
13+
metadata:
14+
labels:
15+
app: nginx
16+
spec:
17+
containers:
18+
- name: nginx
19+
image: nginx:1.15.4
20+
ports:
21+
- containerPort: 80

kubernetes/utils/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@
1414

1515
from __future__ import absolute_import
1616

17-
from .create_from_yaml import FailToCreateError, create_from_yaml
17+
from .create_from_yaml import (FailToCreateError, create_from_dict,
18+
create_from_yaml)

kubernetes/utils/create_from_yaml.py

+72-44
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,6 @@ def create_from_yaml(
4141
the yaml file already contains a namespace definition
4242
this parameter has no effect.
4343
44-
Returns:
45-
An k8s api object or list of apis objects created from YAML.
46-
When a single object is generated, return type is dependent
47-
on output_list.
48-
49-
Throws a FailToCreateError exception if creation of any object
50-
fails with helpful messages from the server.
51-
5244
Available parameters for creating <kind>:
5345
:param async_req bool
5446
:param bool include_uninitialized: If true, partially initialized
@@ -59,47 +51,81 @@ def create_from_yaml(
5951
directive will result in an error response and no further
6052
processing of the request.
6153
Valid values are: - All: all dry run stages will be processed
62-
"""
6354
55+
Raises:
56+
FailToCreateError which holds list of `client.rest.ApiException`
57+
instances for each object that failed to create.
58+
"""
6459
with open(path.abspath(yaml_file)) as f:
6560
yml_document_all = yaml.safe_load_all(f)
66-
api_exceptions = []
67-
# Load all documents from a single YAML file
61+
62+
failures = []
6863
for yml_document in yml_document_all:
69-
# If it is a list type, will need to iterate its items
70-
if "List" in yml_document["kind"]:
71-
# Could be "List" or "Pod/Service/...List"
72-
# This is a list type. iterate within its items
73-
kind = yml_document["kind"].replace("List", "")
74-
for yml_object in yml_document["items"]:
75-
# Mitigate cases when server returns a xxxList object
76-
# See kubernetes-client/python#586
77-
if kind is not "":
78-
yml_object["apiVersion"] = yml_document["apiVersion"]
79-
yml_object["kind"] = kind
80-
try:
81-
create_from_yaml_single_item(
82-
k8s_client, yml_object, verbose, namespace, **kwargs)
83-
except client.rest.ApiException as api_exception:
84-
api_exceptions.append(api_exception)
85-
else:
86-
# This is a single object. Call the single item method
87-
try:
88-
create_from_yaml_single_item(
89-
k8s_client, yml_document, verbose, namespace, **kwargs)
90-
except client.rest.ApiException as api_exception:
91-
api_exceptions.append(api_exception)
64+
try:
65+
create_from_dict(k8s_client, yml_document, verbose,
66+
namespace=namespace,
67+
**kwargs)
68+
except FailToCreateError as failure:
69+
failures.extend(failure.api_exceptions)
70+
if failures:
71+
raise FailToCreateError(failures)
72+
73+
74+
def create_from_dict(k8s_client, data, verbose=False, namespace='default',
75+
**kwargs):
76+
"""
77+
Perform an action from a dictionary containing valid kubernetes
78+
API object (i.e. List, Service, etc).
79+
80+
Input:
81+
k8s_client: an ApiClient object, initialized with the client args.
82+
data: a dictionary holding valid kubernetes objects
83+
verbose: If True, print confirmation from the create action.
84+
Default is False.
85+
namespace: string. Contains the namespace to create all
86+
resources inside. The namespace must preexist otherwise
87+
the resource creation will fail. If the API object in
88+
the yaml file already contains a namespace definition
89+
this parameter has no effect.
90+
91+
Raises:
92+
FailToCreateError which holds list of `client.rest.ApiException`
93+
instances for each object that failed to create.
94+
"""
95+
# If it is a list type, will need to iterate its items
96+
api_exceptions = []
97+
98+
if "List" in data["kind"]:
99+
# Could be "List" or "Pod/Service/...List"
100+
# This is a list type. iterate within its items
101+
kind = data["kind"].replace("List", "")
102+
for yml_object in data["items"]:
103+
# Mitigate cases when server returns a xxxList object
104+
# See kubernetes-client/python#586
105+
if kind is not "":
106+
yml_object["apiVersion"] = data["apiVersion"]
107+
yml_object["kind"] = kind
108+
try:
109+
create_from_yaml_single_item(
110+
k8s_client, yml_object, verbose, namespace=namespace,
111+
**kwargs)
112+
except client.rest.ApiException as api_exception:
113+
api_exceptions.append(api_exception)
114+
else:
115+
# This is a single object. Call the single item method
116+
try:
117+
create_from_yaml_single_item(
118+
k8s_client, data, verbose, namespace=namespace, **kwargs)
119+
except client.rest.ApiException as api_exception:
120+
api_exceptions.append(api_exception)
121+
92122
# In case we have exceptions waiting for us, raise them
93123
if api_exceptions:
94124
raise FailToCreateError(api_exceptions)
95125

96126

97127
def create_from_yaml_single_item(
98-
k8s_client,
99-
yml_object,
100-
verbose=False,
101-
namespace="default",
102-
**kwargs):
128+
k8s_client, yml_object, verbose=False, **kwargs):
103129
group, _, version = yml_object["apiVersion"].partition("/")
104130
if version == "":
105131
version = group
@@ -116,15 +142,17 @@ def create_from_yaml_single_item(
116142
kind = yml_object["kind"]
117143
kind = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', kind)
118144
kind = re.sub('([a-z0-9])([A-Z])', r'\1_\2', kind).lower()
119-
# Decide which namespace we are going to put the object in,
120-
# if any
121-
if "namespace" in yml_object["metadata"]:
122-
namespace = yml_object["metadata"]["namespace"]
123145
# Expect the user to create namespaced objects more often
124146
if hasattr(k8s_api, "create_namespaced_{0}".format(kind)):
147+
# Decide which namespace we are going to put the object in,
148+
# if any
149+
if "namespace" in yml_object["metadata"]:
150+
namespace = yml_object["metadata"]["namespace"]
151+
kwargs['namespace'] = namespace
125152
resp = getattr(k8s_api, "create_namespaced_{0}".format(kind))(
126-
body=yml_object, namespace=namespace, **kwargs)
153+
body=yml_object, **kwargs)
127154
else:
155+
kwargs.pop('namespace', None)
128156
resp = getattr(k8s_api, "create_{0}".format(kind))(
129157
body=yml_object, **kwargs)
130158
if verbose:

0 commit comments

Comments
 (0)