Skip to content

Commit 2dafb4b

Browse files
authored
Merge pull request #655 from micw523/master
Add kubectl create -f like feature
2 parents 1efbab9 + 5a92ba6 commit 2dafb4b

14 files changed

+343
-1
lines changed
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright 2018 The Kubernetes Authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from os import path
16+
17+
from kubernetes import client, config, utils
18+
19+
20+
def main():
21+
# Configs can be set in Configuration class directly or using helper
22+
# utility. If no argument provided, the config will be loaded from
23+
# default location.
24+
config.load_kube_config()
25+
k8s_client = client.ApiClient()
26+
k8s_api = utils.create_from_yaml(k8s_client, "nginx-deployment.yaml")
27+
deps = k8s_api.read_namespaced_deployment("nginx-deployment", "default")
28+
print("Deployment {0} created".format(deps.metadata.name))
29+
30+
31+
if __name__ == '__main__':
32+
main()

kubernetes/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@
2020
import kubernetes.config
2121
import kubernetes.watch
2222
import kubernetes.stream
23+
import kubernetes.utils

kubernetes/e2e_test/test_utils.py

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
import unittest
16+
17+
from kubernetes import utils, client
18+
from kubernetes.e2e_test import base
19+
20+
class TestUtils(unittest.TestCase):
21+
22+
@classmethod
23+
def setUpClass(cls):
24+
cls.config = base.get_e2e_configuration()
25+
26+
def test_app_yaml(self):
27+
k8s_client = client.api_client.ApiClient(configuration=self.config)
28+
k8s_api = utils.create_from_yaml(k8s_client,
29+
"kubernetes/e2e_test/test_yaml/apps-deployment.yaml")
30+
self.assertEqual("apps/v1beta1",
31+
k8s_api.get_api_resources().group_version)
32+
dep = k8s_api.read_namespaced_deployment(name="nginx-app",
33+
namespace="default")
34+
self.assertIsNotNone(dep)
35+
resp = k8s_api.delete_namespaced_deployment(
36+
name="nginx-app", namespace="default",
37+
body={})
38+
39+
def test_extension_yaml(self):
40+
k8s_client = client.api_client.ApiClient(configuration=self.config)
41+
k8s_api = utils.create_from_yaml(k8s_client,
42+
"kubernetes/e2e_test/test_yaml/extensions-deployment.yaml")
43+
self.assertEqual("extensions/v1beta1",
44+
k8s_api.get_api_resources().group_version)
45+
dep = k8s_api.read_namespaced_deployment(name="nginx-deployment",
46+
namespace="default")
47+
self.assertIsNotNone(dep)
48+
resp = k8s_api.delete_namespaced_deployment(
49+
name="nginx-deployment", namespace="default",
50+
body={})
51+
52+
def test_core_pod_yaml(self):
53+
k8s_client = client.api_client.ApiClient(configuration=self.config)
54+
k8s_api = utils.create_from_yaml(k8s_client,
55+
"kubernetes/e2e_test/test_yaml/core-pod.yaml")
56+
self.assertEqual("v1",
57+
k8s_api.get_api_resources().group_version)
58+
pod = k8s_api.read_namespaced_pod(name="myapp-pod",
59+
namespace="default")
60+
self.assertIsNotNone(pod)
61+
resp = k8s_api.delete_namespaced_pod(
62+
name="myapp-pod", namespace="default",
63+
body={})
64+
65+
def test_core_service_yaml(self):
66+
k8s_client = client.api_client.ApiClient(configuration=self.config)
67+
k8s_api = utils.create_from_yaml(k8s_client,
68+
"kubernetes/e2e_test/test_yaml/core-service.yaml")
69+
self.assertEqual("v1",
70+
k8s_api.get_api_resources().group_version)
71+
svc = k8s_api.read_namespaced_service(name="my-service",
72+
namespace="default")
73+
self.assertIsNotNone(svc)
74+
resp = k8s_api.delete_namespaced_service(
75+
name="my-service", namespace="default",
76+
body={})
77+
78+
def test_core_namespace_yaml(self):
79+
k8s_client = client.api_client.ApiClient(configuration=self.config)
80+
k8s_api = utils.create_from_yaml(k8s_client,
81+
"kubernetes/e2e_test/test_yaml/core-namespace.yaml")
82+
self.assertEqual("v1",
83+
k8s_api.get_api_resources().group_version)
84+
nmsp = k8s_api.read_namespace(name="development")
85+
self.assertIsNotNone(nmsp)
86+
resp = k8s_api.delete_namespace(name="development", body={})
87+
88+
def test_deployment_in_namespace(self):
89+
k8s_client = client.ApiClient(configuration=self.config)
90+
core_api = utils.create_from_yaml(k8s_client,
91+
"kubernetes/e2e_test/test_yaml/core-namespace-dep.yaml")
92+
self.assertEqual("v1",
93+
core_api.get_api_resources().group_version)
94+
nmsp = core_api.read_namespace(name="dep")
95+
self.assertIsNotNone(nmsp)
96+
dep_api = utils.create_from_yaml(k8s_client,
97+
"kubernetes/e2e_test/test_yaml/extensions-deployment-dep.yaml")
98+
dep = dep_api.read_namespaced_deployment(name="nginx-deployment",
99+
namespace="dep")
100+
self.assertIsNotNone(dep)
101+
resp = dep_api.delete_namespaced_deployment(
102+
name="nginx-deployment", namespace="dep",
103+
body={})
104+
resp = core_api.delete_namespace(name="dep", body={})
105+
106+
def test_api_service(self):
107+
k8s_client = client.api_client.ApiClient(configuration=self.config)
108+
k8s_api = utils.create_from_yaml(k8s_client,
109+
"kubernetes/e2e_test/test_yaml/api-service.yaml")
110+
self.assertEqual("apiregistration.k8s.io/v1beta1",
111+
k8s_api.get_api_resources().group_version)
112+
svc = k8s_api.read_api_service(
113+
name="v1alpha1.wardle.k8s.io")
114+
self.assertIsNotNone(svc)
115+
resp = k8s_api.delete_api_service(
116+
name="v1alpha1.wardle.k8s.io", body={})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apiVersion: apiregistration.k8s.io/v1beta1
2+
kind: APIService
3+
metadata:
4+
name: v1alpha1.wardle.k8s.io
5+
spec:
6+
insecureSkipTLSVerify: true
7+
group: wardle.k8s.io
8+
groupPriorityMinimum: 1000
9+
versionPriority: 15
10+
service:
11+
name: api
12+
namespace: wardle
13+
version: v1alpha1
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
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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: v1
2+
kind: Namespace
3+
metadata:
4+
name: dep
5+
labels:
6+
name: dep
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: v1
2+
kind: Namespace
3+
metadata:
4+
name: development
5+
labels:
6+
name: development
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: v1
2+
kind: Pod
3+
metadata:
4+
name: myapp-pod
5+
labels:
6+
app: myapp
7+
spec:
8+
containers:
9+
- name: myapp-container
10+
image: busybox
11+
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
kind: Service
2+
apiVersion: v1
3+
metadata:
4+
name: my-service
5+
spec:
6+
selector:
7+
app: MyApp
8+
ports:
9+
- protocol: TCP
10+
port: 80
11+
targetPort: 9376
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: extensions/v1beta1
2+
kind: Deployment
3+
metadata:
4+
name: nginx-deployment
5+
namespace: dep
6+
spec:
7+
replicas: 3
8+
template:
9+
metadata:
10+
labels:
11+
app: nginx
12+
spec:
13+
containers:
14+
- name: nginx
15+
image: nginx:1.7.9
16+
ports:
17+
- containerPort: 80
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
apiVersion: extensions/v1beta1
2+
kind: Deployment
3+
metadata:
4+
name: nginx-deployment
5+
spec:
6+
replicas: 3
7+
template:
8+
metadata:
9+
labels:
10+
app: nginx
11+
spec:
12+
containers:
13+
- name: nginx
14+
image: nginx:1.7.9
15+
ports:
16+
- containerPort: 80
17+

kubernetes/utils/__init__.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2018 The Kubernetes Authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from .create_from_yaml import create_from_yaml

kubernetes/utils/create_from_yaml.py

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Copyright 2018 The Kubernetes Authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from os import path
16+
import re
17+
import sys
18+
19+
from six import iteritems
20+
21+
import yaml
22+
23+
from kubernetes import client
24+
25+
def create_from_yaml(k8s_client, yaml_file, verbose=False, **kwargs):
26+
"""
27+
Perform an action from a yaml file. Pass True for verbose to
28+
print confirmation information.
29+
30+
Input:
31+
yaml_file: string. Contains the path to yaml file.
32+
k8s_cline: an ApiClient object, initialized with the client args.
33+
34+
Available parameters for performing the subsequent action:
35+
:param async_req bool
36+
:param bool include_uninitialized: If true, partially initialized resources are included in the response.
37+
:param str pretty: If 'true', then the output is pretty printed.
38+
:param str dry_run: When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed
39+
"""
40+
41+
with open(path.abspath(yaml_file)) as f:
42+
yml_object = yaml.load(f)
43+
#TODO: case of yaml file containing multiple objects
44+
group, _, version = yml_object["apiVersion"].partition("/")
45+
if version == "":
46+
version = group
47+
group = "core"
48+
# Take care for the case e.g. api_type is "apiextensions.k8s.io"
49+
# Only replace the last instance
50+
group = "".join(group.rsplit(".k8s.io", 1))
51+
fcn_to_call = "{0}{1}Api".format(group.capitalize(),
52+
version.capitalize())
53+
k8s_api = getattr(client, fcn_to_call)(k8s_client)
54+
# Replace CamelCased action_type into snake_case
55+
kind = yml_object["kind"]
56+
kind = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', kind)
57+
kind = re.sub('([a-z0-9])([A-Z])', r'\1_\2', kind).lower()
58+
# Decide which namespace we are going to put the object in,
59+
# if any
60+
if "namespace" in yml_object["metadata"]:
61+
namespace = yml_object["metadata"]["namespace"]
62+
else:
63+
namespace = "default"
64+
# Expect the user to create namespaced objects more often
65+
if hasattr(k8s_api, "create_namespaced_{0}".format(kind)):
66+
resp = getattr(k8s_api, "create_namespaced_{0}".format(kind))(
67+
body=yml_object, namespace=namespace, **kwargs)
68+
else:
69+
resp = getattr(k8s_api, "create_{0}".format(kind))(
70+
body=yml_object, **kwargs)
71+
if verbose:
72+
print("{0} created. status='{1}'".format(kind, str(resp.status)))
73+
return k8s_api
74+

setup.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@
5757
extras_require=EXTRAS,
5858
packages=['kubernetes', 'kubernetes.client', 'kubernetes.config',
5959
'kubernetes.watch', 'kubernetes.client.apis',
60-
'kubernetes.stream', 'kubernetes.client.models'],
60+
'kubernetes.stream', 'kubernetes.client.models',
61+
'kubernetes.utils'],
6162
include_package_data=True,
6263
long_description="""\
6364
Python client for kubernetes http://kubernetes.io/

0 commit comments

Comments
 (0)