This commit is contained in:
2018-09-30 04:25:39 -04:00
parent c17ed3af6f
commit 76687f806a
10 changed files with 78 additions and 128 deletions
+1
View File
@@ -9,4 +9,5 @@ RUN apt-get update
RUN apt-get install -y kubectl
COPY . /app
RUN pip3 install -e .
CMD bruce-operator watch
+2 -1
View File
@@ -12,9 +12,10 @@ click = "*"
logme = "*"
background = "*"
docopt = "*"
bruce-operator = {editable = true, path = "."}
kubeconfig = "*"
[dev-packages]
bruce-operator = {editable = true, path = "."}
"flake8" = "*"
black = "*"
Generated
+12 -5
View File
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "f234e7a7715fe7dfaf399ba918c77088dca9ee4df199158a9572fb6f83e64ace"
"sha256": "de5a76d65e30ec8c0b2bc15d1392f2bbd7b39e77f986268ff6d605b6fd5c6824"
},
"pipfile-spec": 6,
"requires": {
@@ -47,10 +47,6 @@
"markers": "python_version >= '3'",
"version": "==1.0.2"
},
"bruce-operator": {
"editable": true,
"path": "."
},
"cachetools": {
"hashes": [
"sha256:90f1d559512fc073483fe573ef5ceb39bf6ad3d39edc98dc55178a2b2b176fa3",
@@ -185,6 +181,13 @@
"index": "pypi",
"version": "==2.10"
},
"kubeconfig": {
"hashes": [
"sha256:393bdf6b03d4830c11c8d47e267fd65a3bed82518492b20b9fe554c7cd6c8111"
],
"index": "pypi",
"version": "==1.0.1"
},
"kubernetes": {
"hashes": [
"sha256:5ee6e2e949ca800ad8a73da6f67c2a637c2c803945b006e6105beae83e43b273",
@@ -341,6 +344,10 @@
"index": "pypi",
"version": "==18.9b0"
},
"bruce-operator": {
"editable": true,
"path": "."
},
"click": {
"hashes": [
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
+5
View File
@@ -2,6 +2,7 @@
Usage:
bruce-operator watch
bruce-operator fetch [--buildpack=<buildpack>]
bruce-operator (-h | --help)
Options:
@@ -20,6 +21,10 @@ def main():
if args["watch"]:
watch()
if args["fetch"]:
print("fetching")
exit()
if __name__ == "__main__":
main()
+33 -3
View File
@@ -1,14 +1,22 @@
import time
import json
from uuid import uuid4
from functools import lru_cache
import logme
import kubernetes
import background
from kubeconfig import KubeConfig
from kubernetes.client.configuration import Configuration
from kubernetes.client.api_client import ApiClient
from .env import WATCH_NAMESPACE, API_GROUP, API_VERSION
from .env import (
WATCH_NAMESPACE,
API_GROUP,
API_VERSION,
OPERATOR_IMAGE,
KUBECONFIG_PATH,
)
from .kubectl import kubectl
# https://github.com/kubernetes-client/python/blob/master/examples/create_thirdparty_resource.md
@@ -82,6 +90,27 @@ class Operator:
except kubernetes.client.rest.ApiException:
return None
def spawn_self(self, cmd, label, env=None):
if env is None:
env = {}
# TODO: ENV
_hash = uuid4().hex
return kubectl(
f"run bruce-operator-{label}-{_hash} --image={OPERATOR_IMAGE} -n {WATCH_NAMESPACE} --restart=Never --quiet=True --record=True --image-pull-policy=Always -- bruce-operator {cmd}"
)
def ensure_kube_config(self):
if IN_KUBERNETES:
host = os.environ["KUBERNETES_SERVICE_HOST"]
port = os.environ["KUBERNETES_SERVICE_PORT"]
conf = KubeConfig()
kc.set_cluster(
name='the-cluster',
server='https://{host}:{port}'
certificate_authority=CERT_LOCATION,
)
def ensure_resource_definitions(self):
# Create Buildpacks resource.
self.logger.info("Ensuring Buildpack resource definitions...")
@@ -97,12 +126,13 @@ class Operator:
self.logger.info("Ensuring Buildpack volume resource...")
kubectl(f"apply -f ./deploy/buildpacks-volume.yml -n {WATCH_NAMESPACE}")
def fetch_buildpack(self, buildpack_name):
def spawn_fetch_buildpack(self, buildpack_name):
self.logger.info(f"Pretending to fetch {buildpack_name!r} buildpack!")
self.spawn_self(f"fetch --buildpack={buildpack_name}", label="fetch")
def fetch_buildpacks(self):
for buildpack in self.installed_buildpacks:
self.fetch_buildpack(buildpack["metadata"]["name"])
self.spawn_fetch_buildpack(buildpack["metadata"]["name"])
def watch(self):
self.logger.info("Pretending to watch...")
@@ -1,117 +0,0 @@
import time
import json
from functools import lru_cache
import logme
import kubernetes
import background
from kubernetes.client.configuration import Configuration
from kubernetes.client.api_client import ApiClient
from .env import WATCH_NAMESPACE, API_GROUP, API_VERSION
from .kubectl import kubectl
# https://github.com/kubernetes-client/python/blob/master/examples/create_thirdparty_resource.md
@logme.log
class Operator:
def __init__(self, api_client=None):
# Load Kube configuration into module (ugh).
try:
kubernetes.config.load_kube_config()
except FileNotFoundError:
pass
# Setup clients.
self.client = kubernetes.client.CoreV1Api()
self.custom_client = kubernetes.client.CustomObjectsApi(self.client.api_client)
# Ensure resource definitions.
self.ensure_resource_definitions()
self.ensure_volumes()
self.fetch_buildpacks()
# print(self.installed_buildpacks)
# exit()
@property
def installed_buildpacks(self):
group = API_GROUP # str | The custom resource's group name
version = API_VERSION # str | The custom resource's version
namespace = WATCH_NAMESPACE # str | The custom resource's namespace
plural = (
"buildpacks"
) # str | The custom resource's plural name. For TPRs this would be lowercase plural kind.
pretty = (
"true"
) # str | If 'true', then the output is pretty printed. (optional)
watch = (
False
) # bool | Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. (optional)
try:
api_response = self.custom_client.list_namespaced_custom_object(
group, version, namespace, plural, pretty=pretty, watch=watch
)
return api_response["items"]
except kubernetes.client.rest.ApiException:
return None
@property
def installed_apps(self):
group = "bruce.kennethreitz.org" # str | The custom resource's group name
version = "v1alpha1" # str | The custom resource's version
namespace = WATCH_NAMESPACE # str | The custom resource's namespace
plural = (
"apps"
) # str | The custom resource's plural name. For TPRs this would be lowercase plural kind.
pretty = (
"true"
) # str | If 'true', then the output is pretty printed. (optional)
watch = (
False
) # bool | Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. (optional)
try:
api_response = self.custom_client.list_namespaced_custom_object(
group, version, namespace, plural, pretty=pretty, watch=watch
)
return api_response["items"]
except kubernetes.client.rest.ApiException:
return None
def ensure_resource_definitions(self):
# Create Buildpacks resource.
self.logger.info("Ensuring Buildpack resource definitions...")
kubectl(
f"apply -f ./deploy/buildpack-resource-definition.yml -n {WATCH_NAMESPACE}"
)
# Create Apps resource.
self.logger.info("Ensuring App resource definitions...")
kubectl(f"apply -f ./deploy/app-resource-definition.yml -n {WATCH_NAMESPACE}")
def ensure_volumes(self):
self.logger.info("Ensuring Buildpack volume resource...")
kubectl(f"apply -f ./deploy/buildpacks-volume.yml -n {WATCH_NAMESPACE}")
def fetch_buildpack(self, buildpack_name):
self.logger.info(f"Pretending to fetch {buildpack_name!r} buildpack!")
def fetch_buildpacks(self):
for buildpack in self.installed_buildpacks:
self.fetch_buildpack(buildpack["metadata"]["name"])
def watch(self):
self.logger.info("Pretending to watch...")
time.sleep(5)
operator = Operator()
def watch():
while True:
operator.watch()
+8
View File
@@ -3,3 +3,11 @@ import os
WATCH_NAMESPACE = os.environ.get("WATCH_NAMESPACE", "bruce")
API_VERSION = "v1alpha1"
API_GROUP = "bruce.kennethreitz.org"
BUILDPACKS_DIR = "/opt/buildpacks"
APPCACHE_DIR = "/opt/caches"
OPERATOR_IMAGE = "bruceproject/operator:latest"
TOKEN_LOCATION = "/var/run/secrets/kubernetes.io/serviceaccount/token"
CERT_LOCATION = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
IN_KUBERNETES = os.path.isfile(TOKEN_LOCATION)
KUBECONFIG_PATH = os.path.expanduser("~/.kube/config")
+12 -1
View File
@@ -1,13 +1,24 @@
import os
import json
import logme
import delegator
from .env import TOKEN_LOCATION, IN_KUBERNETES, CERT_LOCATION
@logme.log
def kubectl(cmd, as_json=True, raise_on_error=True, logger=None):
json_flag = "-o=json" if as_json else ""
cmd = f"kubectl {cmd} {json_flag}"
if IN_KUBERNETES:
host = os.environ["KUBERNETES_SERVICE_HOST"]
port = os.environ["KUBERNETES_SERVICE_PORT"]
with open(TOKEN_LOCATION, "r") as f:
token = f.read()
server_flag = f"--server=https://{host}:{port} --token={token} --certificate-authority={CERT_LOCATION}"
else:
server_flag = ""
cmd = f"kubectl {json_flag} {server_flag} {cmd}"
logger.debug(f"$ {cmd}")
c = delegator.run(cmd)
+1 -1
View File
@@ -17,7 +17,7 @@ DESCRIPTION = "My short description for my project."
URL = "https://github.com/bruce-project/bruce-operator"
EMAIL = "me@example.com"
AUTHOR = "Kenneth Reitz"
REQUIRES_PYTHON = ">=3.7.0"
REQUIRES_PYTHON = ">=3.6.0"
VERSION = None
# What packages are required for this module to be executed?
+4
View File
@@ -0,0 +1,4 @@
docker build --tag bruceproject/operator .
docker push bruceproject/operator
kubectl delete -f .\deploy\operator.yml -n bruce
kubectl create -f .\deploy\operator.yml -n bruce