Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mccalluc/first steps #2

Merged
merged 21 commits into from Oct 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
@@ -0,0 +1,7 @@
.idea/

*.pyc

build/
dist/
kompatible.egg-info/
60 changes: 60 additions & 0 deletions .travis.yml
@@ -0,0 +1,60 @@
language: python
cache: pip
python:
#- 2.7 TODO
- 3.6
sudo: required

env:
global:
# This moves Kubernetes specific config files.
- CHANGE_MINIKUBE_NONE_USER=true
- JSONPATH='{range .items[*]}{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status};{end}{end}'
- KUBERNETES_VERSION=v1.9.0 # TODO: Test other versions
matrix:
- KUBERNETES_SDK_VERSION='7.0.0'

before_install:
# Tips for Kubernetes on Travis come from:
# https://blog.travis-ci.com/2017-10-26-running-kubernetes-on-travis-ci-with-minikube
# Later updated by:
# https://github.com/LiliC/travis-minikube/blob/master/.travis.yml

# Download kubectl, which is a requirement for using minikube.
- curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/$KUBERNETES_VERSION/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/
# Download minikube.
- curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.25.2/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
- sudo minikube start --vm-driver=none --kubernetes-version=$KUBERNETES_VERSION
# Fix the kubectl context, as it's often stale.
- minikube update-context
# Wait for Kubernetes to be up and ready.
- until kubectl get nodes -o jsonpath="$JSONPATH" 2>&1 | grep -q "Ready=True"; do sleep 1; echo "waiting for kubernetes"; done

- kubectl cluster-info
# Verify kube-addon-manager.
# kube-addon-manager is responsible for managing other kubernetes components, such as kube-dns, dashboard, storage-provisioner..
- until kubectl -n kube-system get pods -lcomponent=kube-addon-manager -o jsonpath="$JSONPATH" 2>&1 | grep -q "Ready=True"; do sleep 1; echo "waiting for kube-addon-manager"; kubectl get pods --all-namespaces; done
# Wait for kube-dns to be ready.
- until kubectl -n kube-system get pods -lk8s-app=kube-dns -o jsonpath="$JSONPATH" 2>&1 | grep -q "Ready=True"; do sleep 1; echo "waiting for kube-dns"; kubectl get pods --all-namespaces; done
## Create example Redis deployment on Kubernetes.
#- kubectl run travis-example --image=redis --labels="app=travis-example"
## Make sure created pod is scheduled and running.
#- until kubectl -n default get pods -lapp=travis-example -o jsonpath="$JSONPATH" 2>&1 | grep -q "Ready=True"; do sleep 1; echo "waiting for travis-example deploymentavailable"; kubectl get pods -n default; done

install:
- pip install -r requirements-dev.txt
- pip install -r requirements.txt
- pip install kubernetes==$KUBERNETES_SDK_VERSION

script: "./test.sh"

deploy:
provider: pypi
distributions: sdist bdist_wheel
user: mccalluc
password:
secure: iczHLDyUFOgdJepUajb3GeXyQIlrzMqtOPd+sVnflFbJQaOTOC3rNQOxO6cP1+TIimCkrCEcyH5DdAFBEQWTZcwC0wC0Wj3z1org8f4ZsljoeTtzPaDR2z0vg6DBeKcVBndVdRPGh0rXGNUckfh/wKiy/G/qeqKTqL2CaDcZYBmvecjnA35E3IklSvTAgK0WhI6zMBgrNObI4i3iTZ3sAGo7HhLe7Ew13VT1zSY6k12r9dz1RqoRBAgcsbzYFihfCMbUQkq0DSy62pMoLznEp3nZnSsVjr1DkmEMMh8PIKSKae+zJmgfMU+m5y9rfO7nszWfeQE2t9A/IRrEUXM6EzRFFqQ3NApz6FhsjoLBkQJFPhQbKUnKi9xOp1CVtelNMpx1n0wQjR3CrsFSR0u5bwU/skuLovWq58dx+CNIdxmcj9zzABS0s58GI9MRbsRh0JM7vSzYyPiFiXEK2A372dLclvVlGGmXbL1GeG5C13yyL8BsNUfq2LAlCZ2PNmLJhn8h7TeSYzVC5JcA/R2LdA/VR9AdTwZVXuMSSrwYCM/RE5FWQN0O9c9J8IZ/YCOyYe94SBvOTe/8dvzatd9MzMNy1XxNu9ZMF4w7phoeqAlov6wfWCbEu3+nFV1l/N2iOJg8B5e7m/xjLcIHQIqBgztdzYv9fFdZsROrp2XxdO0=
on:
branch: master
python: 3.6
condition: "$KUBERNETES_SDK_VERSION = '7.0.0'"
25 changes: 24 additions & 1 deletion README.md
@@ -1,2 +1,25 @@
# kompatible
Python wrapper for Kubernetes which matches the interface of Docker

This package exposes a subset of the
[Kubernetes Python Client](https://github.com/kubernetes-client/python/)
with an interface which matches that of the
[Docker SDK for Python](https://docker-py.readthedocs.io/en/stable/).

## Examples

First, checkout this project, and install dependencies:
`pip install -r requirements-dev.txt`.
Then, make sure you have installed and started
[Docker](https://docs.docker.com/docker-for-mac/install/)
and [Minikube](https://kubernetes.io/docs/tutorials/hello-minikube/#create-a-minikube-cluster)

With those tools in place, the following lines will produce the same output,
regardless of the underlying technology. Either `import docker as sdk`
or `import kompatible as sdk` and then

```
>>> client = sdk.from_env()
>>> client.containers.run("alpine", "echo hello world")
b'hello world\n'

```
19 changes: 19 additions & 0 deletions doctest_runner.py
@@ -0,0 +1,19 @@
import argparse
import doctest

parser = argparse.ArgumentParser(description='Run doc tests')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--docker', action='store_true')
group.add_argument('--kompatible', action='store_true')

args = parser.parse_args()
if args.docker:
import docker as sdk
else:
import kompatible as sdk

(failure_count, test_count) = doctest.testfile('README.md', globs={'sdk': sdk})
print('{}: fail / total = {} / {}'.format(
sdk.__name__, failure_count, test_count))
if failure_count > 0:
exit(1)
1 change: 1 addition & 0 deletions kompatible/VERSION.txt
@@ -0,0 +1 @@
0.0.1
5 changes: 5 additions & 0 deletions kompatible/__init__.py
@@ -0,0 +1,5 @@
from .client import Client


def from_env():
return Client()
55 changes: 55 additions & 0 deletions kompatible/client.py
@@ -0,0 +1,55 @@
import time

from kubernetes import client, config
from kubernetes.stream import stream

config.load_kube_config()


class Client():

@property
def containers(self):
return _Containers()


class _Containers():
def run(self, image, command):
api = client.CoreV1Api()
name = 'todo-foobar'

pod_manifest = {
'apiVersion': 'v1',
'kind': 'Pod',
'metadata': {
'name': name
},
'spec': {
'containers': [{
'image': image,
'name': name,
"args": [ # TODO: Is this necessary?
"/bin/sh",
"-c",
"while true;do date;sleep 5; done"
]
}]
}
}

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

exec_command = ['/bin/sh', '-c', command]
resp = stream(api.connect_get_namespaced_pod_exec, name, 'default',
command=exec_command,
stderr=True, stdin=False,
stdout=True, tty=False)
# Return bytes just to match behavior of Docker client.
return resp.encode()
10 changes: 10 additions & 0 deletions requirements-dev.txt
@@ -0,0 +1,10 @@
kubernetes==7.0.0
docker==3.5.0

# Code style:
flake8==3.5.0
autopep8==1.4
isort==4.3.4

# Read .travis.yml so that our pypi version metadata matches our tests
PyYAML==3.13
Empty file added requirements.txt
Empty file.
11 changes: 11 additions & 0 deletions setup.cfg
@@ -0,0 +1,11 @@
[bdist_wheel]
universal = 1

[metadata]
license_file = LICENSE

[coverage:run]
source = kompatible

[coverage:report]
show_missing = true
44 changes: 44 additions & 0 deletions setup.py
@@ -0,0 +1,44 @@
import os
from os.path import abspath, dirname, join, normpath

# isort has different behavior for different versions of python here.
import yaml # isort:skip
from setuptools import find_packages, setup # isort:skip

with open(join(dirname(__file__), 'README.md')) as f:
readme_md = f.read()

# allow setup.py to be run from any path
os.chdir(normpath(join(abspath(__file__), os.pardir)))

version = open(join('kompatible', 'VERSION.txt')).read().strip()
travis = yaml.load(open('.travis.yml').read())

python_classifiers = ['Programming Language :: Python :: {}'.format(v)
for v in travis['python']]
assert len(python_classifiers) > 0

setup(
name='kompatible',
version=version,
install_requires=[
'kubernetes', # TODO: determine lower bound
],
packages=find_packages(exclude=['tests']),
include_package_data=True,
license='MIT License',
description='Python wrapper for Kubernetes '
'which matches the interface of Docker',
long_description=readme_md,
long_description_content_type='text/markdown',
url='https://github.com/refinery-platform/kompatible/',
author='Chuck McCallum',
classifiers=[
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python'],
zip_safe=False
# TODO: This fixes "ValueError: bad marshal data (unknown type code)"
# ... but I don't understand why it broke, or whether this is a good fix.
)
63 changes: 63 additions & 0 deletions test.sh
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
set -o errexit

start() { echo travis_fold':'start:$1; echo $1; }
end() { echo travis_fold':'end:$1; }
die() { set +v; echo "$*" 1>&2 ; sleep 1; exit 1; }
# Race condition truncates logs on Travis: "sleep" might help.
# https://github.com/travis-ci/travis-ci/issues/6018

start preflight

docker info
docker info | grep 'Operating System' \
|| die 'Make sure Docker is running'

kubectl cluster-info
# TODO: What is a good check for kubernetes?

# Running locally, Kubernetes housekeeping containers are inside the VM.
# On Travis, there is no VM, so Docker will see Kubernetes containers.
# For now, "grep -v kube" seems to exclude them all,
# and "tail" excludes the header.
docker ps -a
[ -z "`docker ps -a | grep -v kube | tail -n +2`" ] \
|| die 'Kill containers before running tests: "docker ps -qa | xargs docker stop | xargs docker rm"'

kubectl get pods
[ -z "`kubectl get pods`" ] \
|| die 'Kill pods before running tests: "kubectl delete pods --all"' # Can take a while...

end preflight

start doctest
python doctest_runner.py --docker
python doctest_runner.py --kompatible
end doctest

#start coverage
#echo; echo 'Tests:'
#COVERAGE_FILE=.coverage.test coverage report
#echo; echo 'Doctests:'
#COVERAGE_FILE=.coverage.doctest coverage report
#echo; echo 'Union:'
#coverage combine
#coverage report --fail-under 100
#end coverage

start docker
docker system df
# TODO: Make assertions about the disk usage we would expect to see.
end docker

start format
flake8 --exclude build . || die "Run 'autopep8 --in-place -r .'"
end format

start isort
isort --recursive . --verbose --check-only || die "See ERRORs: Run 'isort --recursive .'"
end isort

start wheel
python setup.py sdist bdist_wheel
end wheel