Merge branch 'master' into patch-1
This commit is contained in:
commit
be0a752240
20
.github/ISSUE_TEMPLATE/bug.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/bug.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Share about things that are not working as expected
|
||||
labels: kind/bug
|
||||
|
||||
---
|
||||
|
||||
**What happened (please include outputs or screenshots)**:
|
||||
|
||||
**What you expected to happen**:
|
||||
|
||||
**How to reproduce it (as minimally and precisely as possible)**:
|
||||
|
||||
**Anything else we need to know?**:
|
||||
|
||||
**Environment**:
|
||||
- Kubernetes version (`kubectl version`):
|
||||
- OS (e.g., MacOS 10.13.6):
|
||||
- Python version (`python --version`)
|
||||
- Python client version (`pip list | grep kubernetes`)
|
||||
10
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Documentation
|
||||
about: Report any mistakes or missing information from the documentation or the examples
|
||||
labels: kind/documentation
|
||||
|
||||
---
|
||||
|
||||
**Link to the issue (please include a link to the specific documentation or example)**:
|
||||
|
||||
**Description of the issue (please include outputs or screenshots if possible)**:
|
||||
10
.github/ISSUE_TEMPLATE/feature.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/feature.md
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest a new feature for the project
|
||||
labels: kind/feature
|
||||
|
||||
---
|
||||
|
||||
**What is the feature and why do you need it**:
|
||||
|
||||
**Describe the solution you'd like to see**:
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@ -67,3 +67,7 @@ target/
|
||||
.idea/*
|
||||
*.iml
|
||||
.vscode
|
||||
|
||||
# created by sphinx documentation build
|
||||
doc/source/README.md
|
||||
doc/_build
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
# ref: https://docs.travis-ci.com/user/languages/python
|
||||
language: python
|
||||
dist: xenial
|
||||
sudo: true
|
||||
services:
|
||||
- docker
|
||||
|
||||
@ -13,12 +12,10 @@ matrix:
|
||||
env: TOXENV=py27-functional
|
||||
- python: 2.7
|
||||
env: TOXENV=update-pycodestyle
|
||||
- python: 2.7
|
||||
- python: 3.7
|
||||
env: TOXENV=docs
|
||||
- python: 2.7
|
||||
env: TOXENV=coverage,codecov
|
||||
- python: 3.4
|
||||
env: TOXENV=py34
|
||||
- python: 3.5
|
||||
env: TOXENV=py35
|
||||
- python: 3.5
|
||||
@ -31,6 +28,10 @@ matrix:
|
||||
env: TOXENV=py37
|
||||
- python: 3.7
|
||||
env: TOXENV=py37-functional
|
||||
- python: 3.8
|
||||
env: TOXENV=py38
|
||||
- python: 3.8
|
||||
env: TOXENV=py38-functional
|
||||
|
||||
install:
|
||||
- pip install tox
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
- Introduce RuntimeClass to NodeV1alpha1Api and NodeV1beta1Api [kubernetes/kubernetes#74433](https://github.com/kubernetes/kubernetes/pull/74433)
|
||||
- Graduate PriorityClass API to GA SchedulingV1Api [kubernetes/kubernetes#73555](https://github.com/kubernetes/kubernetes/pull/73555)
|
||||
- Introduce CSINodeInfo and CSIDriver to StorageV1beta1Api [kubernetes/kubernetes#74283](https://github.com/kubernetes/kubernetes/pull/74283)
|
||||
- The alpha Initializers feature, `admissionregistration.k8s.io/v1alpha1` API version, `Initializers` admission plugin, and use of the `metadata.initializers` API field have been removed. Discontinue use of the alpha feature and delete any existing `InitializerConfiguration` API objects before upgrading. The `metadata.initializers` field will be removed in a future release. The parameter `include_uninitialized` has been removed. [kubernetes/kubernetes#72972](https://github.com/kubernetes/kubernetes/pull/72972)
|
||||
|
||||
# v9.0.0
|
||||
**Bug Fix:**
|
||||
|
||||
9
OWNERS
9
OWNERS
@ -1,8 +1,11 @@
|
||||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
approvers:
|
||||
- mbohlool
|
||||
- roycaihw
|
||||
- yliaog
|
||||
emeritus_approvers:
|
||||
- caesarxuchao
|
||||
- lavalamp
|
||||
- yliaog
|
||||
- roycaihw
|
||||
- mbohlool
|
||||
reviewers:
|
||||
- micw523
|
||||
|
||||
46
README.md
46
README.md
@ -25,7 +25,7 @@ From [PyPi](https://pypi.python.org/pypi/kubernetes/) directly:
|
||||
pip install kubernetes
|
||||
```
|
||||
|
||||
## Example
|
||||
## Examples
|
||||
|
||||
list all pods:
|
||||
|
||||
@ -68,7 +68,7 @@ More examples can be found in [examples](examples/) folder. To run examples, run
|
||||
python -m examples.example1
|
||||
```
|
||||
|
||||
(replace example1 with the example base filename)
|
||||
(replace example1 with one of the filenames in the examples folder)
|
||||
|
||||
## Documentation
|
||||
|
||||
@ -77,24 +77,20 @@ All APIs and Models' documentation can be found at the [Generated client's READM
|
||||
## Compatibility
|
||||
|
||||
`client-python` follows [semver](http://semver.org/), so until the major version of
|
||||
client-python gets increased, your code will continue to work with explicitly
|
||||
client-python gets increased, your code will continue to work with explicitly
|
||||
supported versions of Kubernetes clusters.
|
||||
|
||||
#### Compatibility matrix
|
||||
|
||||
| | Kubernetes 1.5 | Kubernetes 1.6 | Kubernetes 1.7 | Kubernetes 1.8 | Kubernetes 1.9 | Kubernetes 1.10 | Kubernetes 1.11 | Kubernetes 1.12 | Kubernetes 1.13 | Kubernetes 1.14 |
|
||||
|--------------------|----------------|----------------|----------------|----------------|----------------|-----------------|-----------------|-----------------|-----------------|-----------------|
|
||||
| client-python 1.0 | ✓ | - | - |- |- |- |- |- |- |- |
|
||||
| client-python 2.0 | + | ✓ | - |- |- |- |- |- |- |- |
|
||||
| client-python 3.0 | + | + | ✓ |- |- |- |- |- |- |- |
|
||||
| client-python 4.0 | + | + | + |✓ |- |- |- |- |- |- |
|
||||
| client-python 5.0 | + | + | + |+ |✓ |- |- |- |- |- |
|
||||
| client-python 6.0 | + | + | + |+ |+ |✓ |- |- |- |- |
|
||||
| client-python 7.0 | + | + | + |+ |+ |+ |✓ |- |- |- |
|
||||
| client-python 8.0 | + | + | + |+ |+ |+ |+ |✓ |- |- |
|
||||
| client-python 9.0 | + | + | + |+ |+ |+ |+ |+ |✓ |- |
|
||||
| client-python 10.0 | + | + | + |+ |+ |+ |+ |+ |+ |✓ |
|
||||
| client-python HEAD | + | + | + |+ |+ |+ |+ |+ |+ |✓ |
|
||||
| | Kubernetes 1.9 | Kubernetes 1.10 | Kubernetes 1.11 | Kubernetes 1.12 | Kubernetes 1.13 | Kubernetes 1.14 |
|
||||
|--------------------|----------------|-----------------|-----------------|-----------------|-----------------|-----------------|
|
||||
| client-python 5.0 |✓ |- |- |- |- |- |
|
||||
| client-python 6.0 |+ |✓ |- |- |- |- |
|
||||
| client-python 7.0 |+ |+ |✓ |- |- |- |
|
||||
| client-python 8.0 |+ |+ |+ |✓ |- |- |
|
||||
| client-python 9.0 |+ |+ |+ |+ |✓ |- |
|
||||
| client-python 10.0 |+ |+ |+ |+ |+ |✓ |
|
||||
| client-python HEAD |+ |+ |+ |+ |+ |✓ |
|
||||
|
||||
Key:
|
||||
|
||||
@ -110,14 +106,6 @@ between client-python versions.
|
||||
|
||||
| Client version | Canonical source for OpenAPI spec | Maintenance status |
|
||||
|-----------------|--------------------------------------|-------------------------------|
|
||||
| 1.0 Alpha/Beta | Kubernetes main repo, 1.5 branch | ✗ |
|
||||
| 1.0.x | Kubernetes main repo, 1.5 branch | ✗ |
|
||||
| 2.0 Alpha/Beta | Kubernetes main repo, 1.6 branch | ✗ |
|
||||
| 2.0.x | Kubernetes main repo, 1.6 branch | ✗ |
|
||||
| 3.0 Alpha/Beta | Kubernetes main repo, 1.7 branch | ✗ |
|
||||
| 3.0 | Kubernetes main repo, 1.7 branch | ✗ |
|
||||
| 4.0 Alpha/Beta | Kubernetes main repo, 1.8 branch | ✗ |
|
||||
| 4.0 | Kubernetes main repo, 1.8 branch | ✗ |
|
||||
| 5.0 Alpha/Beta | Kubernetes main repo, 1.9 branch | ✗ |
|
||||
| 5.0 | Kubernetes main repo, 1.9 branch | ✗ |
|
||||
| 6.0 Alpha/Beta | Kubernetes main repo, 1.10 branch | ✗ |
|
||||
@ -142,18 +130,12 @@ Note: There would be no maintenance for alpha/beta releases except the latest on
|
||||
|
||||
## Community, Support, Discussion
|
||||
|
||||
If you have any problem on using the package or any suggestions, please start with reaching the [Kubernetes clients slack channel](https://kubernetes.slack.com/messages/C76GB48RK/), or filing an [issue](https://github.com/kubernetes-client/python/issues) to let us know. You can also reach the maintainers of this project at [SIG API Machinery](https://github.com/kubernetes/community/tree/master/sig-api-machinery).
|
||||
If you have any problem on using the package or any suggestions, please start with reaching the [Kubernetes clients slack channel](https://kubernetes.slack.com/messages/C76GB48RK/), or filing an [issue](https://github.com/kubernetes-client/python/issues) to let us know. You can also reach the maintainers of this project at [SIG API Machinery](https://github.com/kubernetes/community/tree/master/sig-api-machinery), where this project falls under.
|
||||
|
||||
### Code of Conduct
|
||||
|
||||
Participation in the Kubernetes community is governed by the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
||||
|
||||
## Kubernetes Incubator
|
||||
|
||||
This is a [Kubernetes Incubator project](https://github.com/kubernetes/community/blob/master/incubator.md).
|
||||
|
||||
* [SIG: sig-api-machinery](https://github.com/kubernetes/community/tree/master/sig-api-machinery)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### SSLError on macOS
|
||||
@ -184,9 +166,9 @@ Specifically check `ipaddress` and `urllib3` package versions to make sure they
|
||||
|
||||
Starting from 4.0 release, we do not support directly calling exec or attach calls. you should use stream module to call them. so instead
|
||||
of `resp = api.connect_get_namespaced_pod_exec(name, ...` you should call `resp = stream(api.connect_get_namespaced_pod_exec, name, ...`.
|
||||
See more at [exec example](examples/exec.py).
|
||||
|
||||
Using Stream will overwrite the requests protocol in _core_v1_api.CoreV1Api()_
|
||||
This will cause a failure in non-exec/attach calls. If you reuse your api client object, you will need to
|
||||
recreate it between api calls that use _stream_ and other api calls.
|
||||
|
||||
See more at [exec example](examples/pod_exec.py).
|
||||
|
||||
@ -3,7 +3,11 @@
|
||||
The Kubernetes Python client is released on an as-needed basis. The process is as follows:
|
||||
|
||||
1. An issue is proposing a new release with a changelog since the last release
|
||||
1. Update the support matrix in README.md removing the oldest version and adding the
|
||||
proposed release.
|
||||
1. All [OWNERS](OWNERS) must LGTM this release
|
||||
1. An OWNER runs `git tag -s $VERSION` and inserts the changelog and pushes the tag with `git push $VERSION`
|
||||
1. An OWNER runs `git tag -s $VERSION` and inserts the changelog and pushes
|
||||
the tag with `git push $VERSION`
|
||||
1. The release issue is closed
|
||||
1. An announcement email is sent to `kubernetes-dev@googlegroups.com` with the subject `[ANNOUNCE] kubernetes-python-client $VERSION is released`
|
||||
1. An announcement email is sent to `kubernetes-dev@googlegroups.com`
|
||||
with the subject `[ANNOUNCE] kubernetes-python-client $VERSION is released`
|
||||
|
||||
21
doc/Makefile
Normal file
21
doc/Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS = -c source
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXPROJ = kubernetes-python
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
html:
|
||||
$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@echo "\nDocs rendered successfully, open _/build/html/index.html to view"
|
||||
11
doc/README.md
Normal file
11
doc/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
Building the documentation
|
||||
==========================
|
||||
|
||||
Install the test requirements with:
|
||||
|
||||
```
|
||||
$ pip install -r ../test-requirements.txt
|
||||
```
|
||||
|
||||
Use `make html` to build the docs in html format.
|
||||
|
||||
2
doc/requirements-docs.txt
Normal file
2
doc/requirements-docs.txt
Normal file
@ -0,0 +1,2 @@
|
||||
recommonmark
|
||||
sphinx_markdown_tables
|
||||
@ -13,22 +13,37 @@
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
from recommonmark.parser import CommonMarkParser
|
||||
from recommonmark.transform import AutoStructify
|
||||
|
||||
# Work around https://github.com/readthedocs/recommonmark/issues/152
|
||||
new_readme = []
|
||||
|
||||
with open("../../README.md", "r") as r:
|
||||
lines = r.readlines()
|
||||
for l in lines:
|
||||
nl = re.sub("\[!\[[\w\s]+\]\(", "[
|
||||
new_readme.append(nl)
|
||||
|
||||
with open("README.md", "w") as n:
|
||||
n.writelines(new_readme)
|
||||
|
||||
# apparently index.rst can't search for markdown not in the same directory
|
||||
shutil.copy("../../CONTRIBUTING.md", ".")
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
# -- General configuration ----------------------------------------------------
|
||||
|
||||
source_parsers = {
|
||||
'.md': CommonMarkParser,
|
||||
}
|
||||
|
||||
source_suffix = ['.rst', '.md']
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = [
|
||||
'sphinx_markdown_tables',
|
||||
'recommonmark',
|
||||
'sphinx.ext.autodoc',
|
||||
#'sphinx.ext.intersphinx',
|
||||
]
|
||||
@ -80,3 +95,10 @@ latex_documents = [
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
#intersphinx_mapping = {'http://docs.python.org/': None}
|
||||
def setup(app):
|
||||
app.add_config_value('recommonmark_config', {
|
||||
'auto_toc_tree_section': 'Contents',
|
||||
'enable_eval_rst': True,
|
||||
}, True)
|
||||
app.add_transform(AutoStructify)
|
||||
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
============
|
||||
Contributing
|
||||
============
|
||||
.. include:: ../../CONTRIBUTING.md
|
||||
@ -11,11 +11,12 @@ Contents:
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
readme
|
||||
README <README.md>
|
||||
installation
|
||||
usage
|
||||
examples
|
||||
modules
|
||||
contributing
|
||||
contributing <CONTRIBUTING.md>
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
======
|
||||
Readme
|
||||
======
|
||||
.. include:: ../../README.md
|
||||
@ -2,6 +2,19 @@
|
||||
Usage
|
||||
========
|
||||
|
||||
To use kubernetes-python-client in a project::
|
||||
The directory ``examples`` contains a few examples on how to use the client.
|
||||
|
||||
import kubernetes
|
||||
|
||||
Deployments
|
||||
-----------
|
||||
|
||||
Here is a simple usage of creating a deployment from a yaml file:
|
||||
|
||||
|
||||
.. literalinclude:: ../../examples/create_deployment.py
|
||||
|
||||
|
||||
The following example demostrates how to create, update and delete deployments
|
||||
without the need to read a file from the disk:
|
||||
|
||||
.. literalinclude:: ../../examples/deployment_examples.py
|
||||
|
||||
17
examples/README.md
Normal file
17
examples/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Python Client Examples
|
||||
|
||||
This directory contains various examples of how to use the Python client.
|
||||
Please read the description at the top of each example for more information
|
||||
about what the script does and any prequisites. Most scripts also include
|
||||
comments throughout the code.
|
||||
|
||||
## Setup
|
||||
|
||||
These scripts require Python 2.7 or 3.5+ and the Kubernetes client which can be
|
||||
installed following the directions
|
||||
[here](https://github.com/kubernetes-client/python#installation).
|
||||
|
||||
## Contributions
|
||||
|
||||
If you find a problem please file an
|
||||
[issue](https://github.com/kubernetes-client/python/issues).
|
||||
@ -12,4 +12,4 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Empty init file to make examples folder a python module.
|
||||
# Empty init file to make examples folder a python module
|
||||
|
||||
@ -12,6 +12,11 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Reads the list of available API versions and prints them. Similar to running
|
||||
`kubectl api-versions`.
|
||||
"""
|
||||
|
||||
from kubernetes import client, config
|
||||
|
||||
|
||||
@ -22,7 +27,7 @@ def main():
|
||||
config.load_kube_config()
|
||||
|
||||
print("Supported APIs (* is preferred version):")
|
||||
print("%-20s %s" %
|
||||
print("%-40s %s" %
|
||||
("core", ",".join(client.CoreApi().get_api_versions().versions)))
|
||||
for api in client.ApisApi().get_api_versions().groups:
|
||||
versions = []
|
||||
@ -1,33 +0,0 @@
|
||||
# Copyright 2018 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
from os import path
|
||||
|
||||
from kubernetes import client, config, utils
|
||||
|
||||
|
||||
def main():
|
||||
# Configs can be set in Configuration class directly or using helper
|
||||
# utility. If no argument provided, the config will be loaded from
|
||||
# default location.
|
||||
config.load_kube_config()
|
||||
k8s_client = client.ApiClient()
|
||||
utils.create_from_yaml(k8s_client, "nginx-deployment.yaml")
|
||||
k8s_api = client.ExtensionsV1beta1Api(k8s_client)
|
||||
deps = k8s_api.read_namespaced_deployment("nginx-deployment", "default")
|
||||
print("Deployment {0} created".format(deps.metadata.name))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -1,40 +0,0 @@
|
||||
## Creating a Third Party Resource
|
||||
|
||||
```
|
||||
from __future__ import print_function
|
||||
|
||||
from pprint import pprint
|
||||
|
||||
import kubernetes
|
||||
from kubernetes import config
|
||||
from kubernetes.rest import ApiException
|
||||
|
||||
config.load_kube_config()
|
||||
api_instance = kubernetes.ThirdPartyResources()
|
||||
|
||||
namespace = 'default'
|
||||
resource = 'repos'
|
||||
fqdn = 'git.k8s.com'
|
||||
|
||||
body = {}
|
||||
body['apiVersion'] = "git.k8s.com/v1"
|
||||
body['kind'] = "RePo"
|
||||
body['metadata'] = {}
|
||||
body['metadata']['name'] = "blog-repo"
|
||||
body['repo'] = "github.com/user/my-blog"
|
||||
body['username'] = "username"
|
||||
body['password'] = "password"
|
||||
body['branch'] = "branch"
|
||||
|
||||
|
||||
|
||||
try:
|
||||
# Create a Resource
|
||||
api_response = api_instance.apis_fqdn_v1_namespaces_namespace_resource_post(
|
||||
namespace, fqdn, resource, body)
|
||||
pprint(api_response)
|
||||
except ApiException as e:
|
||||
print(
|
||||
"Exception when calling DefaultApi->apis_fqdn_v1_namespaces_namespace_resource_post: %s\n" %
|
||||
e)
|
||||
```
|
||||
94
examples/custom_object.py
Normal file
94
examples/custom_object.py
Normal file
@ -0,0 +1,94 @@
|
||||
# Copyright 2019 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
"""
|
||||
Uses a Custom Resource Definition (CRD) to create a custom object, in this case
|
||||
a CronTab. This example use an example CRD from this tutorial:
|
||||
https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/
|
||||
|
||||
The following yaml manifest has to be applied first:
|
||||
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: crontabs.stable.example.com
|
||||
spec:
|
||||
group: stable.example.com
|
||||
versions:
|
||||
- name: v1
|
||||
served: true
|
||||
storage: true
|
||||
scope: Namespaced
|
||||
names:
|
||||
plural: crontabs
|
||||
singular: crontab
|
||||
kind: CronTab
|
||||
shortNames:
|
||||
- ct
|
||||
"""
|
||||
|
||||
from pprint import pprint
|
||||
|
||||
from kubernetes import client, config
|
||||
|
||||
|
||||
def main():
|
||||
config.load_kube_config()
|
||||
|
||||
api = client.CustomObjectsApi()
|
||||
|
||||
# it's my custom resource defined as Dict
|
||||
my_resource = {
|
||||
"apiVersion": "stable.example.com/v1",
|
||||
"kind": "CronTab",
|
||||
"metadata": {"name": "my-new-cron-object"},
|
||||
"cronSpec": "* * * * */5",
|
||||
"image": "my-awesome-cron-image",
|
||||
}
|
||||
|
||||
# create the resource
|
||||
api.create_namespaced_custom_object(
|
||||
group="stable.example.com",
|
||||
version="v1",
|
||||
namespace="default",
|
||||
plural="crontabs",
|
||||
body=my_resource,
|
||||
)
|
||||
print("Resource created")
|
||||
|
||||
# get the resource and print out data
|
||||
resource = api.get_namespaced_custom_object(
|
||||
group="stable.example.com",
|
||||
version="v1",
|
||||
name="my-new-cron-object",
|
||||
namespace="default",
|
||||
plural="crontabs",
|
||||
)
|
||||
print("Resource details:")
|
||||
pprint(resource)
|
||||
|
||||
# delete it
|
||||
api.delete_namespaced_custom_object(
|
||||
group="stable.example.com",
|
||||
version="v1",
|
||||
name="my-new-cron-object",
|
||||
namespace="default",
|
||||
plural="crontabs",
|
||||
body=client.V1DeleteOptions(),
|
||||
)
|
||||
print("Resource deleted")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -27,10 +27,10 @@ def main():
|
||||
|
||||
with open(path.join(path.dirname(__file__), "nginx-deployment.yaml")) as f:
|
||||
dep = yaml.safe_load(f)
|
||||
k8s_beta = client.ExtensionsV1beta1Api()
|
||||
resp = k8s_beta.create_namespaced_deployment(
|
||||
k8s_apps_v1 = client.AppsV1Api()
|
||||
resp = k8s_apps_v1.create_namespaced_deployment(
|
||||
body=dep, namespace="default")
|
||||
print("Deployment created. status='%s'" % str(resp.status))
|
||||
print("Deployment created. status='%s'" % resp.metadata.name)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@ -12,9 +12,9 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from os import path
|
||||
|
||||
import yaml
|
||||
"""
|
||||
Creates, updates, and deletes a deployment using AppsV1Api.
|
||||
"""
|
||||
|
||||
from kubernetes import client, config
|
||||
|
||||
@ -25,19 +25,20 @@ def create_deployment_object():
|
||||
# Configureate Pod template container
|
||||
container = client.V1Container(
|
||||
name="nginx",
|
||||
image="nginx:1.7.9",
|
||||
image="nginx:1.15.4",
|
||||
ports=[client.V1ContainerPort(container_port=80)])
|
||||
# Create and configurate a spec section
|
||||
template = client.V1PodTemplateSpec(
|
||||
metadata=client.V1ObjectMeta(labels={"app": "nginx"}),
|
||||
spec=client.V1PodSpec(containers=[container]))
|
||||
# Create the specification of deployment
|
||||
spec = client.ExtensionsV1beta1DeploymentSpec(
|
||||
spec = client.V1DeploymentSpec(
|
||||
replicas=3,
|
||||
template=template)
|
||||
template=template,
|
||||
selector={'matchLabels': {'app': 'nginx'}})
|
||||
# Instantiate the deployment object
|
||||
deployment = client.ExtensionsV1beta1Deployment(
|
||||
api_version="extensions/v1beta1",
|
||||
deployment = client.V1Deployment(
|
||||
api_version="apps/v1",
|
||||
kind="Deployment",
|
||||
metadata=client.V1ObjectMeta(name=DEPLOYMENT_NAME),
|
||||
spec=spec)
|
||||
@ -55,7 +56,7 @@ def create_deployment(api_instance, deployment):
|
||||
|
||||
def update_deployment(api_instance, deployment):
|
||||
# Update container image
|
||||
deployment.spec.template.spec.containers[0].image = "nginx:1.9.1"
|
||||
deployment.spec.template.spec.containers[0].image = "nginx:1.16.0"
|
||||
# Update the deployment
|
||||
api_response = api_instance.patch_namespaced_deployment(
|
||||
name=DEPLOYMENT_NAME,
|
||||
@ -80,16 +81,16 @@ def main():
|
||||
# utility. If no argument provided, the config will be loaded from
|
||||
# default location.
|
||||
config.load_kube_config()
|
||||
extensions_v1beta1 = client.ExtensionsV1beta1Api()
|
||||
apps_v1 = client.AppsV1Api()
|
||||
# Create a deployment object with client-python API. The deployment we
|
||||
# created is same as the `nginx-deployment.yaml` in the /examples folder.
|
||||
deployment = create_deployment_object()
|
||||
|
||||
create_deployment(extensions_v1beta1, deployment)
|
||||
create_deployment(apps_v1, deployment)
|
||||
|
||||
update_deployment(extensions_v1beta1, deployment)
|
||||
update_deployment(apps_v1, deployment)
|
||||
|
||||
delete_deployment(extensions_v1beta1)
|
||||
delete_deployment(apps_v1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@ -1,97 +0,0 @@
|
||||
import time
|
||||
|
||||
from kubernetes import config
|
||||
from kubernetes.client import Configuration
|
||||
from kubernetes.client.apis import core_v1_api
|
||||
from kubernetes.client.rest import ApiException
|
||||
from kubernetes.stream import stream
|
||||
|
||||
config.load_kube_config()
|
||||
c = Configuration()
|
||||
c.assert_hostname = False
|
||||
Configuration.set_default(c)
|
||||
api = core_v1_api.CoreV1Api()
|
||||
name = 'busybox-test'
|
||||
|
||||
resp = None
|
||||
try:
|
||||
resp = api.read_namespaced_pod(name=name,
|
||||
namespace='default')
|
||||
except ApiException as e:
|
||||
if e.status != 404:
|
||||
print("Unknown error: %s" % e)
|
||||
exit(1)
|
||||
|
||||
if not resp:
|
||||
print("Pod %s does not exist. Creating it..." % name)
|
||||
pod_manifest = {
|
||||
'apiVersion': 'v1',
|
||||
'kind': 'Pod',
|
||||
'metadata': {
|
||||
'name': name
|
||||
},
|
||||
'spec': {
|
||||
'containers': [{
|
||||
'image': 'busybox',
|
||||
'name': 'sleep',
|
||||
"args": [
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
"while true;do date;sleep 5; done"
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
resp = api.create_namespaced_pod(body=pod_manifest,
|
||||
namespace='default')
|
||||
while True:
|
||||
resp = api.read_namespaced_pod(name=name,
|
||||
namespace='default')
|
||||
if resp.status.phase != 'Pending':
|
||||
break
|
||||
time.sleep(1)
|
||||
print("Done.")
|
||||
|
||||
|
||||
# calling exec and wait for response.
|
||||
exec_command = [
|
||||
'/bin/sh',
|
||||
'-c',
|
||||
'echo This message goes to stderr >&2; echo This message goes to stdout']
|
||||
resp = stream(api.connect_get_namespaced_pod_exec, name, 'default',
|
||||
command=exec_command,
|
||||
stderr=True, stdin=False,
|
||||
stdout=True, tty=False)
|
||||
print("Response: " + resp)
|
||||
|
||||
# Calling exec interactively.
|
||||
exec_command = ['/bin/sh']
|
||||
resp = stream(api.connect_get_namespaced_pod_exec, name, 'default',
|
||||
command=exec_command,
|
||||
stderr=True, stdin=True,
|
||||
stdout=True, tty=False,
|
||||
_preload_content=False)
|
||||
commands = [
|
||||
"echo test1",
|
||||
"echo \"This message goes to stderr\" >&2",
|
||||
]
|
||||
while resp.is_open():
|
||||
resp.update(timeout=1)
|
||||
if resp.peek_stdout():
|
||||
print("STDOUT: %s" % resp.read_stdout())
|
||||
if resp.peek_stderr():
|
||||
print("STDERR: %s" % resp.read_stderr())
|
||||
if commands:
|
||||
c = commands.pop(0)
|
||||
print("Running command... %s\n" % c)
|
||||
resp.write_stdin(c + "\n")
|
||||
else:
|
||||
break
|
||||
|
||||
resp.write_stdin("date\n")
|
||||
sdate = resp.readline_stdout(timeout=3)
|
||||
print("Server date command returns: %s" % sdate)
|
||||
resp.write_stdin("whoami\n")
|
||||
user = resp.readline_stdout(timeout=3)
|
||||
print("Server user is: %s" % user)
|
||||
resp.close()
|
||||
@ -12,47 +12,46 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Simple example to show loading config from the cluster
|
||||
#
|
||||
# It works only from a pod. You can start an image with Python
|
||||
# (for example python:latest), exec into the pod, install the library,
|
||||
# then try out this example.
|
||||
#
|
||||
# If you get 403 errors from API server you will have to configure
|
||||
# RBAC to add the permission to list pods.
|
||||
#
|
||||
# ---
|
||||
# kind: ClusterRole
|
||||
# apiVersion: rbac.authorization.k8s.io/v1
|
||||
# metadata:
|
||||
# name: pods-list
|
||||
# rules:
|
||||
# - apiGroups: [""]
|
||||
# resources: ["pods"]
|
||||
# verbs: ["list"]
|
||||
# ---
|
||||
# kind: ClusterRoleBinding
|
||||
# apiVersion: rbac.authorization.k8s.io/v1
|
||||
# metadata:
|
||||
# name: pods-list
|
||||
# subjects:
|
||||
# - kind: ServiceAccount
|
||||
# name: default
|
||||
# namespace: default
|
||||
# roleRef:
|
||||
# kind: ClusterRole
|
||||
# name: pods-list
|
||||
# apiGroup: rbac.authorization.k8s.io
|
||||
# ---
|
||||
#
|
||||
# Doc: https://kubernetes.io/docs/reference/access-authn-authz/rbac/
|
||||
"""
|
||||
Shows how to load a Kubernetes config from within a cluster. This script
|
||||
must be run within a pod. You can start a pod with a Python image (for
|
||||
example, `python:latest`), exec into the pod, install the library, then run
|
||||
this example.
|
||||
|
||||
If you get 403 errors from the API server you will have to configure RBAC to
|
||||
add permission to list pods by applying the following manifest:
|
||||
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: pods-list
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["list"]
|
||||
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: pods-list
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: default
|
||||
namespace: default
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: pods-list
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
Documentation: https://kubernetes.io/docs/reference/access-authn-authz/rbac/
|
||||
"""
|
||||
|
||||
from kubernetes import client, config
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
# it works only if this script is run by K8s as a POD
|
||||
config.load_incluster_config()
|
||||
|
||||
v1 = client.CoreV1Api()
|
||||
|
||||
115
examples/ingress_create.py
Normal file
115
examples/ingress_create.py
Normal file
@ -0,0 +1,115 @@
|
||||
# Copyright 2019 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
"""
|
||||
Creates deployment, service, and ingress objects. The ingress allows external
|
||||
network access to the cluster.
|
||||
"""
|
||||
|
||||
from kubernetes import client, config
|
||||
|
||||
|
||||
def create_deployment(apps_v1_api):
|
||||
container = client.V1Container(
|
||||
name="deployment",
|
||||
image="gcr.io/google-appengine/fluentd-logger",
|
||||
image_pull_policy="Never",
|
||||
ports=[client.V1ContainerPort(container_port=5678)],
|
||||
)
|
||||
# Template
|
||||
template = client.V1PodTemplateSpec(
|
||||
metadata=client.V1ObjectMeta(labels={"app": "deployment"}),
|
||||
spec=client.V1PodSpec(containers=[container]))
|
||||
# Spec
|
||||
spec = client.V1DeploymentSpec(
|
||||
replicas=1,
|
||||
template=template)
|
||||
# Deployment
|
||||
deployment = client.V1Deployment(
|
||||
api_version="apps/v1",
|
||||
kind="Deployment",
|
||||
metadata=client.V1ObjectMeta(name="deployment"),
|
||||
spec=spec)
|
||||
# Creation of the Deployment in specified namespace
|
||||
# (Can replace "default" with a namespace you may have created)
|
||||
apps_v1_api.create_namespaced_deployment(
|
||||
namespace="default", body=deployment
|
||||
)
|
||||
|
||||
|
||||
def create_service():
|
||||
core_v1_api = client.CoreV1Api()
|
||||
body = client.V1Service(
|
||||
api_version="v1",
|
||||
kind="Service",
|
||||
metadata=client.V1ObjectMeta(
|
||||
name="service-example"
|
||||
),
|
||||
spec=client.V1ServiceSpec(
|
||||
selector={"app": "deployment"},
|
||||
ports=[client.V1ServicePort(
|
||||
port=5678,
|
||||
target_port=5678
|
||||
)]
|
||||
)
|
||||
)
|
||||
# Creation of the Deployment in specified namespace
|
||||
# (Can replace "default" with a namespace you may have created)
|
||||
core_v1_api.create_namespaced_service(namespace="default", body=body)
|
||||
|
||||
|
||||
def create_ingress(networking_v1_beta1_api):
|
||||
body = client.NetworkingV1beta1Ingress(
|
||||
api_version="networking.k8s.io/v1beta1",
|
||||
kind="Ingress",
|
||||
metadata=client.V1ObjectMeta(name="ingress-example", annotations={
|
||||
"nginx.ingress.kubernetes.io/rewrite-target": "/"
|
||||
}),
|
||||
spec=client.NetworkingV1beta1IngressSpec(
|
||||
rules=[client.NetworkingV1beta1IngressRule(
|
||||
host="example.com",
|
||||
http=client.NetworkingV1beta1HTTPIngressRuleValue(
|
||||
paths=[client.NetworkingV1beta1HTTPIngressPath(
|
||||
path="/",
|
||||
backend=client.NetworkingV1beta1IngressBackend(
|
||||
service_port=5678,
|
||||
service_name="service-example")
|
||||
|
||||
)]
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
# Creation of the Deployment in specified namespace
|
||||
# (Can replace "default" with a namespace you may have created)
|
||||
networking_v1_beta1_api.create_namespaced_ingress(
|
||||
namespace="default",
|
||||
body=body
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
# Fetching and loading local Kubernetes Information
|
||||
config.load_kube_config()
|
||||
apps_v1_api = client.AppsV1Api()
|
||||
networking_v1_beta1_api = client.NetworkingV1beta1Api()
|
||||
|
||||
create_deployment(apps_v1_api)
|
||||
create_service()
|
||||
create_ingress(networking_v1_beta1_api)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
97
examples/job_crud.py
Normal file
97
examples/job_crud.py
Normal file
@ -0,0 +1,97 @@
|
||||
# Copyright 2016 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
"""
|
||||
Creates, updates, and deletes a job object.
|
||||
"""
|
||||
|
||||
from os import path
|
||||
|
||||
import yaml
|
||||
|
||||
from kubernetes import client, config
|
||||
|
||||
JOB_NAME = "pi"
|
||||
|
||||
|
||||
def create_job_object():
|
||||
# Configureate Pod template container
|
||||
container = client.V1Container(
|
||||
name="pi",
|
||||
image="perl",
|
||||
command=["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"])
|
||||
# Create and configurate a spec section
|
||||
template = client.V1PodTemplateSpec(
|
||||
metadata=client.V1ObjectMeta(labels={"app": "pi"}),
|
||||
spec=client.V1PodSpec(restart_policy="Never", containers=[container]))
|
||||
# Create the specification of deployment
|
||||
spec = client.V1JobSpec(
|
||||
template=template,
|
||||
backoff_limit=4)
|
||||
# Instantiate the job object
|
||||
job = client.V1Job(
|
||||
api_version="batch/v1",
|
||||
kind="Job",
|
||||
metadata=client.V1ObjectMeta(name=JOB_NAME),
|
||||
spec=spec)
|
||||
|
||||
return job
|
||||
|
||||
|
||||
def create_job(api_instance, job):
|
||||
api_response = api_instance.create_namespaced_job(
|
||||
body=job,
|
||||
namespace="default")
|
||||
print("Job created. status='%s'" % str(api_response.status))
|
||||
|
||||
|
||||
def update_job(api_instance, job):
|
||||
# Update container image
|
||||
job.spec.template.spec.containers[0].image = "perl"
|
||||
api_response = api_instance.patch_namespaced_job(
|
||||
name=JOB_NAME,
|
||||
namespace="default",
|
||||
body=job)
|
||||
print("Job updated. status='%s'" % str(api_response.status))
|
||||
|
||||
|
||||
def delete_job(api_instance):
|
||||
api_response = api_instance.delete_namespaced_job(
|
||||
name=JOB_NAME,
|
||||
namespace="default",
|
||||
body=client.V1DeleteOptions(
|
||||
propagation_policy='Foreground',
|
||||
grace_period_seconds=5))
|
||||
print("Job deleted. status='%s'" % str(api_response.status))
|
||||
|
||||
|
||||
def main():
|
||||
# Configs can be set in Configuration class directly or using helper
|
||||
# utility. If no argument provided, the config will be loaded from
|
||||
# default location.
|
||||
config.load_kube_config()
|
||||
batch_v1 = client.BatchV1Api()
|
||||
# Create a job object with client-python API. The job we
|
||||
# created is same as the `pi-job.yaml` in the /examples folder.
|
||||
job = create_job_object()
|
||||
|
||||
create_job(batch_v1, job)
|
||||
|
||||
update_job(batch_v1, job)
|
||||
|
||||
delete_job(batch_v1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -12,14 +12,18 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Allows you to pick a context and then lists all pods in the chosen context.
|
||||
|
||||
Please install the pick library before running this example.
|
||||
"""
|
||||
|
||||
from kubernetes import client, config
|
||||
# install pick using "pip install pick". It is not included
|
||||
# as a dependency because it only used in examples
|
||||
from pick import pick
|
||||
from kubernetes.client import configuration
|
||||
from pick import pick # install pick using `pip install pick`
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
contexts, active_context = config.list_kube_config_contexts()
|
||||
if not contexts:
|
||||
print("Cannot find any context in kube-config file.")
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@ -11,7 +16,6 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
image: nginx:1.15.4
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
|
||||
@ -12,19 +12,18 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Changes the labels of the "minikube" node. Adds the label "foo" with value
|
||||
"bar" and will overwrite the "foo" label if it already exists. Removes the
|
||||
label "baz".
|
||||
"""
|
||||
|
||||
from pprint import pprint
|
||||
|
||||
from kubernetes import client, config
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Change labels of the "minikube" node:
|
||||
- Add label "foo" with value "bar". This will overwrite the "foo" label
|
||||
if it already exists.
|
||||
- Remove the label "baz" from the node.
|
||||
"""
|
||||
|
||||
config.load_kube_config()
|
||||
|
||||
api_instance = client.CoreV1Api()
|
||||
@ -47,7 +47,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"config.load_kube_config()\n",
|
||||
"extension = client.ExtensionsV1beta1Api()"
|
||||
"apps_api = client.AppsV1beta1Api()"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -70,7 +70,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"deployment = client.ExtensionsV1beta1Deployment()"
|
||||
"deployment = client.AppsV1beta1Deployment()"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -93,7 +93,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"deployment.api_version = \"extensions/v1beta1\"\n",
|
||||
"deployment.api_version = \"apps/v1beta1\"\n",
|
||||
"deployment.kind = \"Deployment\"\n",
|
||||
"deployment.metadata = client.V1ObjectMeta(name=\"nginx-deployment\")"
|
||||
]
|
||||
@ -118,7 +118,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"spec = client.ExtensionsV1beta1DeploymentSpec()\n",
|
||||
"spec = client.AppsV1beta1DeploymentSpec()\n",
|
||||
"spec.replicas = 3"
|
||||
]
|
||||
},
|
||||
@ -207,7 +207,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"extension.create_namespaced_deployment(namespace=\"default\", body=deployment)"
|
||||
"apps_api.create_namespaced_deployment(namespace=\"default\", body=deployment)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -253,7 +253,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"extension.replace_namespaced_deployment(name=\"nginx-deployment\", namespace=\"default\", body=deployment)"
|
||||
"apps_api.replace_namespaced_deployment(name=\"nginx-deployment\", namespace=\"default\", body=deployment)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -277,10 +277,10 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"rollback = client.ExtensionsV1beta1DeploymentRollback()\n",
|
||||
"rollback.api_version = \"extensions/v1beta1\"\n",
|
||||
"rollback = client.AppsV1beta1DeploymentRollback()\n",
|
||||
"rollback.api_version = \"apps/v1beta1\"\n",
|
||||
"rollback.kind = \"DeploymentRollback\"\n",
|
||||
"rollback.rollback_to = client.ExtensionsV1beta1RollbackConfig()\n",
|
||||
"rollback.rollback_to = client.AppsV1beta1RollbackConfig()\n",
|
||||
"rollback.rollback_to.revision = 0\n",
|
||||
"rollback.name = \"nginx-deployment\""
|
||||
]
|
||||
|
||||
@ -89,9 +89,9 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"api_instance = client.ExtensionsV1beta1Api()\n",
|
||||
"dep = client.ExtensionsV1beta1Deployment()\n",
|
||||
"spec = client.ExtensionsV1beta1DeploymentSpec()"
|
||||
"api_instance = client.AppsV1beta1Api()\n",
|
||||
"dep = client.AppsV1beta1Deployment()\n",
|
||||
"spec = client.AppsV1beta1DeploymentSpec()"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@ -1,104 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from kubernetes import client, config"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"config.load_incluster_config()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"v1=client.CoreV1Api()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"default\n",
|
||||
"kube-system\n",
|
||||
"kubeless\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for ns in v1.list_namespace().items:\n",
|
||||
" print ns.metadata.name"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 2",
|
||||
"language": "python",
|
||||
"name": "python2"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 2
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython2",
|
||||
"version": "2.7.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 1
|
||||
}
|
||||
|
||||
@ -1,129 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"deletable": true,
|
||||
"editable": true
|
||||
},
|
||||
"source": [
|
||||
"How to watch changes to an object\n",
|
||||
"==================\n",
|
||||
"\n",
|
||||
"In this notebook, we learn how kubernetes API resource Watch endpoint is used to observe resource changes. It can be used to get information about changes to any kubernetes object."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": true,
|
||||
"deletable": true,
|
||||
"editable": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from kubernetes import client, config, watch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Load config from default location."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"config.load_kube_config()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"deletable": true,
|
||||
"editable": true
|
||||
},
|
||||
"source": [
|
||||
"### Create API instance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": true,
|
||||
"deletable": true,
|
||||
"editable": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"api_instance = client.CoreV1Api()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"deletable": true,
|
||||
"editable": true
|
||||
},
|
||||
"source": [
|
||||
"### Run a Watch on the Pods endpoint. \n",
|
||||
"Watch would be executed and produce output about changes to any Pod. After running the cell below, You can test this by running the Pod notebook [create_pod.ipynb](create_pod.ipynb) and observing the additional output here. You can stop the cell from running by restarting the kernel."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false,
|
||||
"deletable": true,
|
||||
"editable": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"w = watch.Watch()\n",
|
||||
"for event in w.stream(api_instance.list_pod_for_all_namespaces):\n",
|
||||
" print(\"Event: %s %s %s\" % (event['type'],event['object'].kind, event['object'].metadata.name))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": true,
|
||||
"deletable": true,
|
||||
"editable": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 2",
|
||||
"language": "python",
|
||||
"name": "python2"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 2
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython2",
|
||||
"version": "2.7.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@ -12,6 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Shows how to load a Kubernetes config from outside of the cluster.
|
||||
"""
|
||||
|
||||
from kubernetes import client, config
|
||||
|
||||
|
||||
13
examples/pi-job.yaml
Normal file
13
examples/pi-job.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: pi
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: pi
|
||||
image: perl
|
||||
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
|
||||
restartPolicy: Never
|
||||
backoffLimit: 4
|
||||
@ -12,11 +12,15 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Allows you to pick a context and then lists all pods in the chosen context.
|
||||
|
||||
Please install the pick library before running this example.
|
||||
"""
|
||||
|
||||
from kubernetes import client, config
|
||||
from kubernetes.client import configuration
|
||||
# install pick using "pip install pick". It is not included
|
||||
# as a dependency because it only used in examples
|
||||
from pick import pick
|
||||
from pick import pick # install pick using `pip install pick`
|
||||
|
||||
|
||||
def main():
|
||||
@ -32,7 +36,7 @@ def main():
|
||||
# utility
|
||||
config.load_kube_config(context=option)
|
||||
|
||||
print("Active host is %s" % configuration.host)
|
||||
print("Active host is %s" % configuration.Configuration().host)
|
||||
|
||||
v1 = client.CoreV1Api()
|
||||
print("Listing pods with their IPs:")
|
||||
54
examples/pod_config_list.py
Normal file
54
examples/pod_config_list.py
Normal file
@ -0,0 +1,54 @@
|
||||
# Copyright 2016 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
"""
|
||||
Allows you to pick a context and then lists all pods in the chosen context. A
|
||||
context includes a cluster, a user, and a namespace.
|
||||
|
||||
Please install the pick library before running this example.
|
||||
"""
|
||||
|
||||
from kubernetes import client, config
|
||||
from kubernetes.client import configuration
|
||||
from pick import pick # install pick using `pip install pick`
|
||||
|
||||
|
||||
def main():
|
||||
contexts, active_context = config.list_kube_config_contexts()
|
||||
if not contexts:
|
||||
print("Cannot find any context in kube-config file.")
|
||||
return
|
||||
contexts = [context['name'] for context in contexts]
|
||||
active_index = contexts.index(active_context['name'])
|
||||
option, _ = pick(contexts, title="Pick the context to load",
|
||||
default_index=active_index)
|
||||
# Configs can be set in Configuration class directly or using helper
|
||||
# utility
|
||||
config.load_kube_config(context=option)
|
||||
|
||||
print("Active host is %s" % configuration.Configuration().host)
|
||||
|
||||
v1 = client.CoreV1Api()
|
||||
print("Listing pods with their IPs:")
|
||||
ret = v1.list_pod_for_all_namespaces(watch=False)
|
||||
for item in ret.items:
|
||||
print(
|
||||
"%s\t%s\t%s" %
|
||||
(item.status.pod_ip,
|
||||
item.metadata.namespace,
|
||||
item.metadata.name))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
129
examples/pod_exec.py
Normal file
129
examples/pod_exec.py
Normal file
@ -0,0 +1,129 @@
|
||||
# Copyright 2019 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
"""
|
||||
Shows the functionality of exec using a Busybox container.
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from kubernetes import config
|
||||
from kubernetes.client import Configuration
|
||||
from kubernetes.client.apis import core_v1_api
|
||||
from kubernetes.client.rest import ApiException
|
||||
from kubernetes.stream import stream
|
||||
|
||||
|
||||
def exec_commands(api_instance):
|
||||
name = 'busybox-test'
|
||||
resp = None
|
||||
try:
|
||||
resp = api_instance.read_namespaced_pod(name=name,
|
||||
namespace='default')
|
||||
except ApiException as e:
|
||||
if e.status != 404:
|
||||
print("Unknown error: %s" % e)
|
||||
exit(1)
|
||||
|
||||
if not resp:
|
||||
print("Pod %s does not exist. Creating it..." % name)
|
||||
pod_manifest = {
|
||||
'apiVersion': 'v1',
|
||||
'kind': 'Pod',
|
||||
'metadata': {
|
||||
'name': name
|
||||
},
|
||||
'spec': {
|
||||
'containers': [{
|
||||
'image': 'busybox',
|
||||
'name': 'sleep',
|
||||
"args": [
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
"while true;do date;sleep 5; done"
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
resp = api_instance.create_namespaced_pod(body=pod_manifest,
|
||||
namespace='default')
|
||||
while True:
|
||||
resp = api_instance.read_namespaced_pod(name=name,
|
||||
namespace='default')
|
||||
if resp.status.phase != 'Pending':
|
||||
break
|
||||
time.sleep(1)
|
||||
print("Done.")
|
||||
|
||||
# Calling exec and waiting for response
|
||||
exec_command = [
|
||||
'/bin/sh',
|
||||
'-c',
|
||||
'echo This message goes to stderr; echo This message goes to stdout']
|
||||
resp = stream(api_instance.connect_get_namespaced_pod_exec,
|
||||
name,
|
||||
'default',
|
||||
command=exec_command,
|
||||
stderr=True, stdin=False,
|
||||
stdout=True, tty=False)
|
||||
print("Response: " + resp)
|
||||
|
||||
# Calling exec interactively
|
||||
exec_command = ['/bin/sh']
|
||||
resp = stream(api_instance.connect_get_namespaced_pod_exec,
|
||||
name,
|
||||
'default',
|
||||
command=exec_command,
|
||||
stderr=True, stdin=True,
|
||||
stdout=True, tty=False,
|
||||
_preload_content=False)
|
||||
commands = [
|
||||
"echo This message goes to stdout",
|
||||
"echo \"This message goes to stderr\" >&2",
|
||||
]
|
||||
|
||||
while resp.is_open():
|
||||
resp.update(timeout=1)
|
||||
if resp.peek_stdout():
|
||||
print("STDOUT: %s" % resp.read_stdout())
|
||||
if resp.peek_stderr():
|
||||
print("STDERR: %s" % resp.read_stderr())
|
||||
if commands:
|
||||
c = commands.pop(0)
|
||||
print("Running command... %s\n" % c)
|
||||
resp.write_stdin(c + "\n")
|
||||
else:
|
||||
break
|
||||
|
||||
resp.write_stdin("date\n")
|
||||
sdate = resp.readline_stdout(timeout=3)
|
||||
print("Server date command returns: %s" % sdate)
|
||||
resp.write_stdin("whoami\n")
|
||||
user = resp.readline_stdout(timeout=3)
|
||||
print("Server user is: %s" % user)
|
||||
resp.close()
|
||||
|
||||
|
||||
def main():
|
||||
config.load_kube_config()
|
||||
c = Configuration()
|
||||
c.assert_hostname = False
|
||||
Configuration.set_default(c)
|
||||
core_v1 = core_v1_api.CoreV1Api()
|
||||
|
||||
exec_commands(core_v1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -12,6 +12,13 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Uses watch to print the stream of events from list namespaces and list pods.
|
||||
The script will wait for 10 events related to namespaces to occur within
|
||||
the `timeout_seconds` threshold and then move on to wait for another 10 events
|
||||
related to pods to occur within the `timeout_seconds` threshold.
|
||||
"""
|
||||
|
||||
from kubernetes import client, config, watch
|
||||
|
||||
|
||||
@ -29,8 +36,18 @@ def main():
|
||||
count -= 1
|
||||
if not count:
|
||||
w.stop()
|
||||
print("Finished namespace stream.")
|
||||
|
||||
print("Ended.")
|
||||
for event in w.stream(v1.list_pod_for_all_namespaces, timeout_seconds=10):
|
||||
print("Event: %s %s %s" % (
|
||||
event['type'],
|
||||
event['object'].kind,
|
||||
event['object'].metadata.name)
|
||||
)
|
||||
count -= 1
|
||||
if not count:
|
||||
w.stop()
|
||||
print("Finished pod stream.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@ -23,7 +23,7 @@ def main():
|
||||
# Define the barer token we are going to use to authenticate.
|
||||
# See here to create the token:
|
||||
# https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/
|
||||
aToken = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
||||
aToken = "<token>"
|
||||
|
||||
# Create a configuration object
|
||||
aConfiguration = client.Configuration()
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 474e9fb32293fa05098e920967bb0e0645182d5b
|
||||
Subproject commit 9f73cc68c1a93725f89842a7cd8c595204c1b901
|
||||
@ -17,12 +17,12 @@ import uuid
|
||||
import yaml
|
||||
|
||||
from kubernetes.client import api_client
|
||||
from kubernetes.client.apis import extensions_v1beta1_api
|
||||
from kubernetes.client.apis import apps_v1_api
|
||||
from kubernetes.client.models import v1_delete_options
|
||||
from kubernetes.e2e_test import base
|
||||
|
||||
|
||||
class TestClientExtensions(unittest.TestCase):
|
||||
class TestClientApps(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
@ -30,14 +30,17 @@ class TestClientExtensions(unittest.TestCase):
|
||||
|
||||
def test_create_deployment(self):
|
||||
client = api_client.ApiClient(configuration=self.config)
|
||||
api = extensions_v1beta1_api.ExtensionsV1beta1Api(client)
|
||||
api = apps_v1_api.AppsV1Api(client)
|
||||
name = 'nginx-deployment-' + str(uuid.uuid4())
|
||||
deployment = '''apiVersion: extensions/v1beta1
|
||||
deployment = '''apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: %s
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@ -45,7 +48,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
image: nginx:1.15.4
|
||||
ports:
|
||||
- containerPort: 80
|
||||
'''
|
||||
@ -60,16 +63,19 @@ spec:
|
||||
|
||||
def test_create_daemonset(self):
|
||||
client = api_client.ApiClient(configuration=self.config)
|
||||
api = extensions_v1beta1_api.ExtensionsV1beta1Api(client)
|
||||
api = apps_v1_api.AppsV1Api(client)
|
||||
name = 'nginx-app-' + str(uuid.uuid4())
|
||||
daemonset = {
|
||||
'apiVersion': 'extensions/v1beta1',
|
||||
'apiVersion': 'apps/v1',
|
||||
'kind': 'DaemonSet',
|
||||
'metadata': {
|
||||
'labels': {'app': 'nginx'},
|
||||
'name': '%s' % name,
|
||||
},
|
||||
'spec': {
|
||||
'selector': {
|
||||
'matchLabels': {'app': 'nginx'},
|
||||
},
|
||||
'template': {
|
||||
'metadata': {
|
||||
'labels': {'app': 'nginx'},
|
||||
@ -77,7 +83,7 @@ spec:
|
||||
'spec': {
|
||||
'containers': [
|
||||
{'name': 'nginx-app',
|
||||
'image': 'nginx:1.10'},
|
||||
'image': 'nginx:1.15.4'},
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -91,4 +97,4 @@ spec:
|
||||
self.assertIsNotNone(resp)
|
||||
|
||||
options = v1_delete_options.V1DeleteOptions()
|
||||
resp = api.delete_namespaced_daemon_set(name, 'default', body=options)
|
||||
resp = api.delete_namespaced_daemon_set(name, 'default', body=options)
|
||||
@ -29,6 +29,26 @@ def short_uuid():
|
||||
return id[-12:]
|
||||
|
||||
|
||||
def manifest_with_command(name, command):
|
||||
return {
|
||||
'apiVersion': 'v1',
|
||||
'kind': 'Pod',
|
||||
'metadata': {
|
||||
'name': name
|
||||
},
|
||||
'spec': {
|
||||
'containers': [{
|
||||
'image': 'busybox',
|
||||
'name': 'sleep',
|
||||
"args": [
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
command
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
class TestClient(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
@ -40,25 +60,7 @@ class TestClient(unittest.TestCase):
|
||||
api = core_v1_api.CoreV1Api(client)
|
||||
|
||||
name = 'busybox-test-' + short_uuid()
|
||||
pod_manifest = {
|
||||
'apiVersion': 'v1',
|
||||
'kind': 'Pod',
|
||||
'metadata': {
|
||||
'name': name
|
||||
},
|
||||
'spec': {
|
||||
'containers': [{
|
||||
'image': 'busybox',
|
||||
'name': 'sleep',
|
||||
"args": [
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
"while true;do date;sleep 5; done"
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pod_manifest = manifest_with_command(name, "while true;do date;sleep 5; done")
|
||||
resp = api.create_namespaced_pod(body=pod_manifest,
|
||||
namespace='default')
|
||||
self.assertEqual(name, resp.metadata.name)
|
||||
@ -117,6 +119,45 @@ class TestClient(unittest.TestCase):
|
||||
|
||||
resp = api.delete_namespaced_pod(name=name, body={},
|
||||
namespace='default')
|
||||
def test_exit_code(self):
|
||||
client = api_client.ApiClient(configuration=self.config)
|
||||
api = core_v1_api.CoreV1Api(client)
|
||||
|
||||
name = 'busybox-test-' + short_uuid()
|
||||
pod_manifest = manifest_with_command(name, "while true;do date;sleep 5; done")
|
||||
resp = api.create_namespaced_pod(body=pod_manifest,
|
||||
namespace='default')
|
||||
self.assertEqual(name, resp.metadata.name)
|
||||
self.assertTrue(resp.status.phase)
|
||||
|
||||
while True:
|
||||
resp = api.read_namespaced_pod(name=name,
|
||||
namespace='default')
|
||||
self.assertEqual(name, resp.metadata.name)
|
||||
self.assertTrue(resp.status.phase)
|
||||
if resp.status.phase == 'Running':
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
commands_expected_values = (
|
||||
(["false", 1]),
|
||||
(["/bin/sh", "-c", "sleep 1; exit 3"], 3),
|
||||
(["true", 0]),
|
||||
(["/bin/sh", "-c", "ls /"], 0)
|
||||
)
|
||||
for command, value in commands_expected_values:
|
||||
client = stream(api.connect_get_namespaced_pod_exec, name, 'default',
|
||||
command=command,
|
||||
stderr=True, stdin=False,
|
||||
stdout=True, tty=False,
|
||||
_preload_content=False)
|
||||
|
||||
self.assertIsNone(client.returncode)
|
||||
client.run_forever(timeout=10)
|
||||
self.assertEqual(client.returncode, value)
|
||||
|
||||
resp = api.delete_namespaced_pod(name=name, body={},
|
||||
namespace='default')
|
||||
|
||||
def test_service_apis(self):
|
||||
client = api_client.ApiClient(configuration=self.config)
|
||||
|
||||
@ -14,7 +14,9 @@
|
||||
|
||||
import unittest
|
||||
|
||||
import yaml
|
||||
from kubernetes import utils, client
|
||||
from kubernetes.client.rest import ApiException
|
||||
from kubernetes.e2e_test import base
|
||||
|
||||
|
||||
@ -39,32 +41,39 @@ class TestUtils(unittest.TestCase):
|
||||
|
||||
def test_create_apps_deployment_from_yaml(self):
|
||||
"""
|
||||
Should be able to create an apps/v1beta1 deployment.
|
||||
Should be able to create an apps/v1 deployment.
|
||||
"""
|
||||
k8s_client = client.api_client.ApiClient(configuration=self.config)
|
||||
utils.create_from_yaml(
|
||||
k8s_client, self.path_prefix + "apps-deployment.yaml")
|
||||
app_api = client.AppsV1beta1Api(k8s_client)
|
||||
app_api = client.AppsV1Api(k8s_client)
|
||||
dep = app_api.read_namespaced_deployment(name="nginx-app",
|
||||
namespace="default")
|
||||
self.assertIsNotNone(dep)
|
||||
app_api.delete_namespaced_deployment(
|
||||
name="nginx-app", namespace="default",
|
||||
body={})
|
||||
while True:
|
||||
try:
|
||||
app_api.delete_namespaced_deployment(
|
||||
name="nginx-app", namespace="default",
|
||||
body={})
|
||||
break
|
||||
except ApiException:
|
||||
continue
|
||||
|
||||
def test_create_extensions_deployment_from_yaml(self):
|
||||
"""
|
||||
Should be able to create an extensions/v1beta1 deployment.
|
||||
"""
|
||||
def test_create_apps_deployment_from_yaml_obj(self):
|
||||
k8s_client = client.api_client.ApiClient(configuration=self.config)
|
||||
utils.create_from_yaml(
|
||||
k8s_client, self.path_prefix + "extensions-deployment.yaml")
|
||||
ext_api = client.ExtensionsV1beta1Api(k8s_client)
|
||||
dep = ext_api.read_namespaced_deployment(name="nginx-deployment",
|
||||
with open(self.path_prefix + "apps-deployment.yaml") as f:
|
||||
yml_obj = yaml.safe_load(f)
|
||||
|
||||
yml_obj["metadata"]["name"] = "nginx-app-3"
|
||||
|
||||
utils.create_from_dict(k8s_client, yml_obj)
|
||||
|
||||
app_api = client.AppsV1Api(k8s_client)
|
||||
dep = app_api.read_namespaced_deployment(name="nginx-app-3",
|
||||
namespace="default")
|
||||
self.assertIsNotNone(dep)
|
||||
ext_api.delete_namespaced_deployment(
|
||||
name="nginx-deployment", namespace="default",
|
||||
app_api.delete_namespaced_deployment(
|
||||
name="nginx-app-3", namespace="default",
|
||||
body={})
|
||||
|
||||
def test_create_pod_from_yaml(self):
|
||||
@ -123,6 +132,20 @@ class TestUtils(unittest.TestCase):
|
||||
rbac_api.delete_namespaced_role(
|
||||
name="pod-reader", namespace="default", body={})
|
||||
|
||||
def test_create_rbac_role_from_yaml_with_verbose_enabled(self):
|
||||
"""
|
||||
Should be able to create an rbac role with verbose enabled.
|
||||
"""
|
||||
k8s_client = client.api_client.ApiClient(configuration=self.config)
|
||||
utils.create_from_yaml(
|
||||
k8s_client, self.path_prefix + "rbac-role.yaml", verbose=True)
|
||||
rbac_api = client.RbacAuthorizationV1Api(k8s_client)
|
||||
rbac_role = rbac_api.read_namespaced_role(
|
||||
name="pod-reader", namespace="default")
|
||||
self.assertIsNotNone(rbac_role)
|
||||
rbac_api.delete_namespaced_role(
|
||||
name="pod-reader", namespace="default", body={})
|
||||
|
||||
def test_create_deployment_non_default_namespace_from_yaml(self):
|
||||
"""
|
||||
Should be able to create a namespace "dep",
|
||||
@ -134,7 +157,7 @@ class TestUtils(unittest.TestCase):
|
||||
utils.create_from_yaml(
|
||||
k8s_client, self.path_prefix + "dep-deployment.yaml")
|
||||
core_api = client.CoreV1Api(k8s_client)
|
||||
ext_api = client.ExtensionsV1beta1Api(k8s_client)
|
||||
ext_api = client.AppsV1Api(k8s_client)
|
||||
nmsp = core_api.read_namespace(name="dep")
|
||||
self.assertIsNotNone(nmsp)
|
||||
dep = ext_api.read_namespaced_deployment(name="nginx-deployment",
|
||||
@ -186,7 +209,7 @@ class TestUtils(unittest.TestCase):
|
||||
utils.create_from_yaml(
|
||||
k8s_client, self.path_prefix + "list.yaml")
|
||||
core_api = client.CoreV1Api(k8s_client)
|
||||
ext_api = client.ExtensionsV1beta1Api(k8s_client)
|
||||
ext_api = client.AppsV1Api(k8s_client)
|
||||
svc = core_api.read_namespaced_service(name="list-service-test",
|
||||
namespace="default")
|
||||
self.assertIsNotNone(svc)
|
||||
@ -266,7 +289,7 @@ class TestUtils(unittest.TestCase):
|
||||
utils.create_from_yaml(
|
||||
k8s_client, self.path_prefix + "multi-resource-with-list.yaml")
|
||||
core_api = client.CoreV1Api(k8s_client)
|
||||
app_api = client.AppsV1beta1Api(k8s_client)
|
||||
app_api = client.AppsV1Api(k8s_client)
|
||||
pod_0 = core_api.read_namespaced_pod(
|
||||
name="mock-pod-0", namespace="default")
|
||||
self.assertIsNotNone(pod_0)
|
||||
@ -317,7 +340,7 @@ class TestUtils(unittest.TestCase):
|
||||
|
||||
def test_create_from_multi_resource_yaml_with_multi_conflicts(self):
|
||||
"""
|
||||
Should create an extensions/v1beta1 deployment
|
||||
Should create an apps/v1 deployment
|
||||
and fail to create the same deployment twice.
|
||||
Should raise an exception that contains two error messages.
|
||||
"""
|
||||
@ -327,14 +350,14 @@ class TestUtils(unittest.TestCase):
|
||||
k8s_client, self.path_prefix + "triple-nginx.yaml")
|
||||
exp_error = ('Error from server (Conflict): {"kind":"Status",'
|
||||
'"apiVersion":"v1","metadata":{},"status":"Failure",'
|
||||
'"message":"deployments.extensions \\"triple-nginx\\" '
|
||||
'"message":"deployments.apps \\"triple-nginx\\" '
|
||||
'already exists","reason":"AlreadyExists",'
|
||||
'"details":{"name":"triple-nginx","group":"extensions",'
|
||||
'"details":{"name":"triple-nginx","group":"apps",'
|
||||
'"kind":"deployments"},"code":409}\n'
|
||||
)
|
||||
exp_error += exp_error
|
||||
self.assertEqual(exp_error, str(cm.exception))
|
||||
ext_api = client.ExtensionsV1beta1Api(k8s_client)
|
||||
ext_api = client.AppsV1Api(k8s_client)
|
||||
dep = ext_api.read_namespaced_deployment(name="triple-nginx",
|
||||
namespace="default")
|
||||
self.assertIsNotNone(dep)
|
||||
@ -342,14 +365,16 @@ class TestUtils(unittest.TestCase):
|
||||
name="triple-nginx", namespace="default",
|
||||
body={})
|
||||
|
||||
def test_create_namespaces_apps_deployment_from_yaml(self):
|
||||
def test_create_namespaced_apps_deployment_from_yaml(self):
|
||||
"""
|
||||
Should be able to create an apps/v1beta1 deployment.
|
||||
Should be able to create an apps/v1beta1 deployment
|
||||
in a test namespace.
|
||||
"""
|
||||
k8s_client = client.api_client.ApiClient(configuration=self.config)
|
||||
utils.create_from_yaml(
|
||||
k8s_client, self.path_prefix + "apps-deployment.yaml", namespace=self.test_namespace)
|
||||
app_api = client.AppsV1beta1Api(k8s_client)
|
||||
k8s_client, self.path_prefix + "apps-deployment.yaml",
|
||||
namespace=self.test_namespace)
|
||||
app_api = client.AppsV1Api(k8s_client)
|
||||
dep = app_api.read_namespaced_deployment(name="nginx-app",
|
||||
namespace=self.test_namespace)
|
||||
self.assertIsNotNone(dep)
|
||||
@ -357,16 +382,17 @@ class TestUtils(unittest.TestCase):
|
||||
name="nginx-app", namespace=self.test_namespace,
|
||||
body={})
|
||||
|
||||
def test_create_from_list_in_multi_resource_yaml(self):
|
||||
def test_create_from_list_in_multi_resource_yaml_namespaced(self):
|
||||
"""
|
||||
Should be able to create the items in the PodList and a deployment
|
||||
specified in the multi-resource file
|
||||
specified in the multi-resource file in a test namespace
|
||||
"""
|
||||
k8s_client = client.api_client.ApiClient(configuration=self.config)
|
||||
utils.create_from_yaml(
|
||||
k8s_client, self.path_prefix + "multi-resource-with-list.yaml", namespace=self.test_namespace)
|
||||
k8s_client, self.path_prefix + "multi-resource-with-list.yaml",
|
||||
namespace=self.test_namespace)
|
||||
core_api = client.CoreV1Api(k8s_client)
|
||||
app_api = client.AppsV1beta1Api(k8s_client)
|
||||
app_api = client.AppsV1Api(k8s_client)
|
||||
pod_0 = core_api.read_namespaced_pod(
|
||||
name="mock-pod-0", namespace=self.test_namespace)
|
||||
self.assertIsNotNone(pod_0)
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
name: nginx-app-2
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@ -11,7 +16,6 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
image: nginx:1.15.4
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
apiVersion: apps/v1beta1
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-app
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
namespace: dep
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@ -12,7 +15,6 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
image: nginx:1.15.4
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ items:
|
||||
port: 80
|
||||
selector:
|
||||
app: list-deployment-test
|
||||
- apiVersion: extensions/v1beta1
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: list-deployment-test
|
||||
@ -19,6 +19,9 @@ items:
|
||||
app: list-deployment-test
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: list-deployment-test
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@ -26,4 +29,4 @@ items:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
image: nginx:1.15.4
|
||||
@ -24,7 +24,7 @@ items:
|
||||
image: busybox
|
||||
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
|
||||
---
|
||||
apiVersion: apps/v1beta1
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mock
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: triple-nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@ -11,16 +14,19 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
image: nginx:1.15.4
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: triple-nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@ -28,16 +34,19 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
image: nginx:1.15.4
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: triple-nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@ -45,6 +54,6 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
image: nginx:1.15.4
|
||||
ports:
|
||||
- containerPort: 80
|
||||
112
kubernetes/test/test_quantity.py
Normal file
112
kubernetes/test/test_quantity.py
Normal file
@ -0,0 +1,112 @@
|
||||
# coding: utf-8
|
||||
# Copyright 2019 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
from kubernetes.utils import parse_quantity
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
class TestQuantity(unittest.TestCase):
|
||||
def test_parse(self):
|
||||
self.assertIsInstance(parse_quantity(2.2), Decimal)
|
||||
# input, expected output
|
||||
tests = [
|
||||
(0, 0),
|
||||
(2, 2),
|
||||
(2, Decimal("2")),
|
||||
(2., 2),
|
||||
(Decimal("2.2"), Decimal("2.2")),
|
||||
(2., Decimal(2)),
|
||||
(Decimal("2."), 2),
|
||||
("123", 123),
|
||||
("2", 2),
|
||||
("2n", Decimal("2") * Decimal(1000)**-3),
|
||||
("2u", Decimal("0.000002")),
|
||||
("2m", Decimal("0.002")),
|
||||
("0m", Decimal("0")),
|
||||
("0M", Decimal("0")),
|
||||
("223k", 223000),
|
||||
("002M", 2 * 1000**2),
|
||||
("2M", 2 * 1000**2),
|
||||
("4123G", 4123 * 1000**3),
|
||||
("2T", 2 * 1000**4),
|
||||
("2P", 2 * 1000**5),
|
||||
("2E", 2 * 1000**6),
|
||||
|
||||
("223Ki", 223 * 1024),
|
||||
("002Mi", 2 * 1024**2),
|
||||
("2Mi", 2 * 1024**2),
|
||||
("2Gi", 2 * 1024**3),
|
||||
("4123Gi", 4123 * 1024**3),
|
||||
("2Ti", 2 * 1024**4),
|
||||
("2Pi", 2 * 1024**5),
|
||||
("2Ei", 2 * 1024**6),
|
||||
|
||||
("2.34n", Decimal("2.34") * Decimal(1000)**-3),
|
||||
("2.34u", Decimal("2.34") * Decimal(1000)**-2),
|
||||
("2.34m", Decimal("2.34") * Decimal(1000)**-1),
|
||||
("2.34Ki", Decimal("2.34") * 1024),
|
||||
("2.34", Decimal("2.34")),
|
||||
(".34", Decimal("0.34")),
|
||||
("34.", 34),
|
||||
(".34M", Decimal("0.34") * 1000**2),
|
||||
|
||||
("2e2K", Decimal("2e2") * 1000),
|
||||
("2e2Ki", Decimal("2e2") * 1024),
|
||||
("2e-2Ki", Decimal("2e-2") * 1024),
|
||||
("2.34E1", Decimal("2.34E1")),
|
||||
(".34e-2", Decimal("0.34e-2")),
|
||||
]
|
||||
|
||||
for inp, out in tests:
|
||||
self.assertEqual(parse_quantity(inp), out)
|
||||
if isinstance(inp, (int, float, Decimal)):
|
||||
self.assertEqual(parse_quantity(-1 * inp), -out)
|
||||
else:
|
||||
self.assertEqual(parse_quantity("-" + inp), -out)
|
||||
self.assertEqual(parse_quantity("+" + inp), out)
|
||||
|
||||
def test_parse_invalid(self):
|
||||
self.assertRaises(ValueError, parse_quantity, [])
|
||||
self.assertRaises(ValueError, parse_quantity, "")
|
||||
self.assertRaises(ValueError, parse_quantity, "-")
|
||||
self.assertRaises(ValueError, parse_quantity, "i")
|
||||
self.assertRaises(ValueError, parse_quantity, "2i")
|
||||
self.assertRaises(ValueError, parse_quantity, "2mm")
|
||||
self.assertRaises(ValueError, parse_quantity, "2mmKi")
|
||||
self.assertRaises(ValueError, parse_quantity, "2KKi")
|
||||
self.assertRaises(ValueError, parse_quantity, "2e")
|
||||
self.assertRaises(ValueError, parse_quantity, "2.2i")
|
||||
self.assertRaises(ValueError, parse_quantity, "bla")
|
||||
self.assertRaises(ValueError, parse_quantity, "Ki")
|
||||
self.assertRaises(ValueError, parse_quantity, "M")
|
||||
self.assertRaises(ValueError, parse_quantity, "2ki")
|
||||
self.assertRaises(ValueError, parse_quantity, "2Ki ")
|
||||
self.assertRaises(ValueError, parse_quantity, "20Ki ")
|
||||
self.assertRaises(ValueError, parse_quantity, "20B")
|
||||
self.assertRaises(ValueError, parse_quantity, "20Bi")
|
||||
self.assertRaises(ValueError, parse_quantity, "20.2Bi")
|
||||
self.assertRaises(ValueError, parse_quantity, "2MiKi")
|
||||
self.assertRaises(ValueError, parse_quantity, "2MK")
|
||||
self.assertRaises(ValueError, parse_quantity, "2MKi")
|
||||
self.assertRaises(ValueError, parse_quantity, "234df")
|
||||
self.assertRaises(ValueError, parse_quantity, "df234")
|
||||
self.assertRaises(ValueError, parse_quantity, tuple())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@ -14,4 +14,6 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .create_from_yaml import FailToCreateError, create_from_yaml
|
||||
from .create_from_yaml import (FailToCreateError, create_from_dict,
|
||||
create_from_yaml)
|
||||
from .quantity import parse_quantity
|
||||
|
||||
@ -41,14 +41,6 @@ def create_from_yaml(
|
||||
the yaml file already contains a namespace definition
|
||||
this parameter has no effect.
|
||||
|
||||
Returns:
|
||||
An k8s api object or list of apis objects created from YAML.
|
||||
When a single object is generated, return type is dependent
|
||||
on output_list.
|
||||
|
||||
Throws a FailToCreateError exception if creation of any object
|
||||
fails with helpful messages from the server.
|
||||
|
||||
Available parameters for creating <kind>:
|
||||
:param async_req bool
|
||||
:param bool include_uninitialized: If true, partially initialized
|
||||
@ -59,47 +51,81 @@ def create_from_yaml(
|
||||
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
|
||||
"""
|
||||
|
||||
Raises:
|
||||
FailToCreateError which holds list of `client.rest.ApiException`
|
||||
instances for each object that failed to create.
|
||||
"""
|
||||
with open(path.abspath(yaml_file)) as f:
|
||||
yml_document_all = yaml.safe_load_all(f)
|
||||
api_exceptions = []
|
||||
# Load all documents from a single YAML file
|
||||
|
||||
failures = []
|
||||
for yml_document in yml_document_all:
|
||||
# If it is a list type, will need to iterate its items
|
||||
if "List" in yml_document["kind"]:
|
||||
# Could be "List" or "Pod/Service/...List"
|
||||
# This is a list type. iterate within its items
|
||||
kind = yml_document["kind"].replace("List", "")
|
||||
for yml_object in yml_document["items"]:
|
||||
# Mitigate cases when server returns a xxxList object
|
||||
# See kubernetes-client/python#586
|
||||
if kind is not "":
|
||||
yml_object["apiVersion"] = yml_document["apiVersion"]
|
||||
yml_object["kind"] = kind
|
||||
try:
|
||||
create_from_yaml_single_item(
|
||||
k8s_client, yml_object, verbose, namespace, **kwargs)
|
||||
except client.rest.ApiException as api_exception:
|
||||
api_exceptions.append(api_exception)
|
||||
else:
|
||||
# This is a single object. Call the single item method
|
||||
try:
|
||||
create_from_yaml_single_item(
|
||||
k8s_client, yml_document, verbose, namespace, **kwargs)
|
||||
except client.rest.ApiException as api_exception:
|
||||
api_exceptions.append(api_exception)
|
||||
try:
|
||||
create_from_dict(k8s_client, yml_document, verbose,
|
||||
namespace=namespace,
|
||||
**kwargs)
|
||||
except FailToCreateError as failure:
|
||||
failures.extend(failure.api_exceptions)
|
||||
if failures:
|
||||
raise FailToCreateError(failures)
|
||||
|
||||
|
||||
def create_from_dict(k8s_client, data, verbose=False, namespace='default',
|
||||
**kwargs):
|
||||
"""
|
||||
Perform an action from a dictionary containing valid kubernetes
|
||||
API object (i.e. List, Service, etc).
|
||||
|
||||
Input:
|
||||
k8s_client: an ApiClient object, initialized with the client args.
|
||||
data: a dictionary holding valid kubernetes objects
|
||||
verbose: If True, print confirmation from the create action.
|
||||
Default is False.
|
||||
namespace: string. Contains the namespace to create all
|
||||
resources inside. The namespace must preexist otherwise
|
||||
the resource creation will fail. If the API object in
|
||||
the yaml file already contains a namespace definition
|
||||
this parameter has no effect.
|
||||
|
||||
Raises:
|
||||
FailToCreateError which holds list of `client.rest.ApiException`
|
||||
instances for each object that failed to create.
|
||||
"""
|
||||
# If it is a list type, will need to iterate its items
|
||||
api_exceptions = []
|
||||
|
||||
if "List" in data["kind"]:
|
||||
# Could be "List" or "Pod/Service/...List"
|
||||
# This is a list type. iterate within its items
|
||||
kind = data["kind"].replace("List", "")
|
||||
for yml_object in data["items"]:
|
||||
# Mitigate cases when server returns a xxxList object
|
||||
# See kubernetes-client/python#586
|
||||
if kind is not "":
|
||||
yml_object["apiVersion"] = data["apiVersion"]
|
||||
yml_object["kind"] = kind
|
||||
try:
|
||||
create_from_yaml_single_item(
|
||||
k8s_client, yml_object, verbose, namespace=namespace,
|
||||
**kwargs)
|
||||
except client.rest.ApiException as api_exception:
|
||||
api_exceptions.append(api_exception)
|
||||
else:
|
||||
# This is a single object. Call the single item method
|
||||
try:
|
||||
create_from_yaml_single_item(
|
||||
k8s_client, data, verbose, namespace=namespace, **kwargs)
|
||||
except client.rest.ApiException as api_exception:
|
||||
api_exceptions.append(api_exception)
|
||||
|
||||
# In case we have exceptions waiting for us, raise them
|
||||
if api_exceptions:
|
||||
raise FailToCreateError(api_exceptions)
|
||||
|
||||
|
||||
def create_from_yaml_single_item(
|
||||
k8s_client,
|
||||
yml_object,
|
||||
verbose=False,
|
||||
namespace="default",
|
||||
**kwargs):
|
||||
k8s_client, yml_object, verbose=False, **kwargs):
|
||||
group, _, version = yml_object["apiVersion"].partition("/")
|
||||
if version == "":
|
||||
version = group
|
||||
@ -116,19 +142,24 @@ def create_from_yaml_single_item(
|
||||
kind = yml_object["kind"]
|
||||
kind = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', kind)
|
||||
kind = re.sub('([a-z0-9])([A-Z])', r'\1_\2', kind).lower()
|
||||
# Decide which namespace we are going to put the object in,
|
||||
# if any
|
||||
if "namespace" in yml_object["metadata"]:
|
||||
namespace = yml_object["metadata"]["namespace"]
|
||||
# Expect the user to create namespaced objects more often
|
||||
if hasattr(k8s_api, "create_namespaced_{0}".format(kind)):
|
||||
# Decide which namespace we are going to put the object in,
|
||||
# if any
|
||||
if "namespace" in yml_object["metadata"]:
|
||||
namespace = yml_object["metadata"]["namespace"]
|
||||
kwargs['namespace'] = namespace
|
||||
resp = getattr(k8s_api, "create_namespaced_{0}".format(kind))(
|
||||
body=yml_object, namespace=namespace, **kwargs)
|
||||
body=yml_object, **kwargs)
|
||||
else:
|
||||
kwargs.pop('namespace', None)
|
||||
resp = getattr(k8s_api, "create_{0}".format(kind))(
|
||||
body=yml_object, **kwargs)
|
||||
if verbose:
|
||||
print("{0} created. status='{1}'".format(kind, str(resp.status)))
|
||||
msg = "{0} created.".format(kind)
|
||||
if hasattr(resp, 'status'):
|
||||
msg += " status='{0}'".format(str(resp.status))
|
||||
print(msg)
|
||||
|
||||
|
||||
class FailToCreateError(Exception):
|
||||
|
||||
75
kubernetes/utils/quantity.py
Normal file
75
kubernetes/utils/quantity.py
Normal file
@ -0,0 +1,75 @@
|
||||
# Copyright 2019 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed 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.
|
||||
from decimal import Decimal, InvalidOperation
|
||||
|
||||
|
||||
def parse_quantity(quantity):
|
||||
"""
|
||||
Parse kubernetes canonical form quantity like 200Mi to a decimal number.
|
||||
Supported SI suffixes:
|
||||
base1024: Ki | Mi | Gi | Ti | Pi | Ei
|
||||
base1000: n | u | m | "" | k | M | G | T | P | E
|
||||
|
||||
See https://github.com/kubernetes/apimachinery/blob/master/pkg/api/resource/quantity.go
|
||||
|
||||
Input:
|
||||
quanity: string. kubernetes canonical form quantity
|
||||
|
||||
Returns:
|
||||
Decimal
|
||||
|
||||
Raises:
|
||||
ValueError on invalid or unknown input
|
||||
"""
|
||||
if isinstance(quantity, (int, float, Decimal)):
|
||||
return Decimal(quantity)
|
||||
|
||||
exponents = {"n": -3, "u": -2, "m": -1, "K": 1, "k": 1, "M": 2,
|
||||
"G": 3, "T": 4, "P": 5, "E": 6}
|
||||
|
||||
quantity = str(quantity)
|
||||
number = quantity
|
||||
suffix = None
|
||||
if len(quantity) >= 2 and quantity[-1] == "i":
|
||||
if quantity[-2] in exponents:
|
||||
number = quantity[:-2]
|
||||
suffix = quantity[-2:]
|
||||
elif len(quantity) >= 1 and quantity[-1] in exponents:
|
||||
number = quantity[:-1]
|
||||
suffix = quantity[-1:]
|
||||
|
||||
try:
|
||||
number = Decimal(number)
|
||||
except InvalidOperation:
|
||||
raise ValueError("Invalid number format: {}".format(number))
|
||||
|
||||
if suffix is None:
|
||||
return number
|
||||
|
||||
if suffix.endswith("i"):
|
||||
base = 1024
|
||||
elif len(suffix) == 1:
|
||||
base = 1000
|
||||
else:
|
||||
raise ValueError("{} has unknown suffix".format(quantity))
|
||||
|
||||
# handly SI inconsistency
|
||||
if suffix == "ki":
|
||||
raise ValueError("{} has unknown suffix".format(quantity))
|
||||
|
||||
if suffix[0] not in exponents:
|
||||
raise ValueError("{} has unknown suffix".format(quantity))
|
||||
|
||||
exponent = Decimal(exponents[suffix[0]])
|
||||
return number * (base ** exponent)
|
||||
@ -8,4 +8,4 @@ ipaddress>=1.0.17;python_version=="2.7" # PSF
|
||||
websocket-client>=0.32.0,!=0.40.0,!=0.41.*,!=0.42.* # LGPLv2+
|
||||
requests # Apache-2.0
|
||||
requests-oauthlib # ISC
|
||||
urllib3>=1.23 # MIT
|
||||
urllib3>=1.24.2 # MIT
|
||||
|
||||
@ -108,7 +108,8 @@ done
|
||||
# Shut down CI if minikube did not start and show logs
|
||||
if [ $MINIKUBE_OK == "false" ]; then
|
||||
sudo minikube logs
|
||||
die $LINENO "minikube did not start"
|
||||
echo "minikube did not start (line: ${LINENO})"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Dump Kubernetes Objects..."
|
||||
|
||||
@ -21,11 +21,6 @@ set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
if ! which mvn > /dev/null 2>&1; then
|
||||
echo "Maven is not installed."
|
||||
exit
|
||||
fi
|
||||
|
||||
SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")
|
||||
CLIENT_ROOT="${SCRIPT_ROOT}/../kubernetes"
|
||||
CLIENT_VERSION=$(python "${SCRIPT_ROOT}/constants.py" CLIENT_VERSION)
|
||||
|
||||
6
setup.py
6
setup.py
@ -62,9 +62,7 @@ setup(
|
||||
'kubernetes.stream', 'kubernetes.client.models',
|
||||
'kubernetes.utils'],
|
||||
include_package_data=True,
|
||||
long_description="""\
|
||||
Python client for kubernetes http://kubernetes.io/
|
||||
""",
|
||||
long_description="Python client for kubernetes http://kubernetes.io/",
|
||||
classifiers=[
|
||||
"Development Status :: %s" % DEVELOPMENT_STATUS,
|
||||
"Topic :: Utilities",
|
||||
@ -76,9 +74,9 @@ setup(
|
||||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.4",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
],
|
||||
)
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
coverage>=4.0.3
|
||||
nose>=1.3.7
|
||||
pytest
|
||||
pytest-cov
|
||||
pluggy>=0.3.1
|
||||
py>=1.4.31
|
||||
randomize>=0.13
|
||||
mock>=2.0.0
|
||||
sphinx>=1.2.1,!=1.3b1,<1.4 # BSD
|
||||
sphinx>=1.4 # BSD
|
||||
recommonmark
|
||||
sphinx_markdown_tables
|
||||
codecov>=1.4.0
|
||||
pycodestyle
|
||||
autopep8
|
||||
isort
|
||||
isort
|
||||
|
||||
29
tox.ini
29
tox.ini
@ -1,5 +1,7 @@
|
||||
[tox]
|
||||
envlist = py27, py34, py35, py36, py37
|
||||
envlist =
|
||||
py27, py3{5,6,7,8}
|
||||
py27-functional, py3{5,6,7,8}-functional
|
||||
|
||||
[testenv]
|
||||
passenv = TOXENV CI TRAVIS TRAVIS_*
|
||||
@ -9,7 +11,8 @@ deps = -r{toxinidir}/test-requirements.txt
|
||||
-r{toxinidir}/requirements.txt
|
||||
commands =
|
||||
python -V
|
||||
py.test -vvv -s --ignore=kubernetes/e2e_test
|
||||
!functional: pytest -vvv -s --ignore=kubernetes/e2e_test
|
||||
functional: {toxinidir}/scripts/kube-init.sh pytest -vvv -s []
|
||||
|
||||
[testenv:docs]
|
||||
commands =
|
||||
@ -19,30 +22,10 @@ commands =
|
||||
commands =
|
||||
{toxinidir}/scripts/update-pycodestyle.sh
|
||||
|
||||
[testenv:py27-functional]
|
||||
commands =
|
||||
python -V
|
||||
{toxinidir}/scripts/kube-init.sh py.test -vvv -s []
|
||||
|
||||
[testenv:py35-functional]
|
||||
commands =
|
||||
python -V
|
||||
{toxinidir}/scripts/kube-init.sh py.test -vvv -s []
|
||||
|
||||
[testenv:py36-functional]
|
||||
commands =
|
||||
python -V
|
||||
{toxinidir}/scripts/kube-init.sh py.test -vvv -s []
|
||||
|
||||
[testenv:py37-functional]
|
||||
commands =
|
||||
python -V
|
||||
{toxinidir}/scripts/kube-init.sh py.test -vvv -s []
|
||||
|
||||
[testenv:coverage]
|
||||
commands =
|
||||
python -V
|
||||
nosetests --with-coverage --cover-package=kubernetes.config,kubernetes.watch --cover-tests
|
||||
pytest --cov=kubernetes/watch --cov=kubernetes/config kubernetes/watch kubernetes/config
|
||||
|
||||
[testenv:codecov]
|
||||
commands =
|
||||
|
||||
Loading…
Reference in New Issue
Block a user