diff --git a/Dockerfile b/Dockerfile index 534a443e..c4736a73 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,9 @@ FROM ubuntu:16.04 -MAINTAINER bjozsa@att.com +MAINTAINER Armada Team ENV USER=armada \ - VERSION=master + VERSION=master \ + REPO=https://github.com/att-comdev/armada.git RUN apt-get update && \ apt-get install -y \ @@ -15,20 +16,18 @@ RUN apt-get update && \ gcc \ libssl-dev \ libffi-dev \ - libgit2-dev + libgit2-dev \ -RUN useradd -ms /bin/bash $USER -USER $USER -WORKDIR /home/$USER +RUN git clone -b $VERSION $REPO ${HOME}/armada +WORKDIR /root/armada +RUN pip install -r requirements.txt \ + && sh scripts/libgit2.sh \ + && pip install --upgrade urllib3 \ + && pip install pygit2 \ + && python setup.py install -RUN git clone -b $VERSION https://github.com/att-comdev/armada.git +ENTRYPOINT ["armada"] -WORKDIR /home/$USER/armada -RUN virtualenv --no-site-packages /home/$USER/.armada && \ - . /home/$USER/.armada/bin/activate +EXPOSE 8000 -RUN /home/$USER/.armada/bin/pip install -r /home/$USER/armada/requirements.txt -RUN /home/$USER/.armada/bin/python ./setup.py install - -ENTRYPOINT ["/home/$USER/.armada/bin/armada"] -CMD ["-h"] +CMD gunicorn server:api -b :8000 diff --git a/README.md b/README.md index 1a305943..346861bc 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,12 @@ Its functionality may extend beyond helm, assisting in interacting with kubernet To use this container, use these simple instructions: ``` -docker run quay.io/attcomdev/armada:latest +docker build -t armada . +``` + + +``` +docker run -v ~/.kube/config:/root/.kube/config -v $(pwd)/examples/:/examples -v /tmp:/dev/log quay.io/attcomdev/armada:latest ``` ## Development diff --git a/armada/armada.py b/armada/armada.py index 5e456470..5182d9e2 100644 --- a/armada/armada.py +++ b/armada/armada.py @@ -3,8 +3,8 @@ import yaml from supermutes.dot import dotify -from handlers.chartbuilder import ChartBuilder -from handlers.tiller import Tiller +from chartbuilder import ChartBuilder +from tiller import Tiller from logutil import LOG class Armada(object): @@ -13,13 +13,20 @@ class Armada(object): workflows ''' - def __init__(self, args): + def __init__(self, config, + disable_update_pre=False, + disable_update_post=False, + enable_chart_cleanup=False, + dry_run=False): ''' Initialize the Armada Engine and establish a connection to Tiller ''' - self.args = args - self.config = yaml.load(open(args.config).read()) + self.disable_update_pre = disable_update_pre + self.disable_update_post = disable_update_post + self.enable_chart_cleanup = enable_chart_cleanup + self.dry_run = dry_run + self.config = yaml.load(config) self.tiller = Tiller() def find_release_chart(self, known_releases, name): @@ -34,6 +41,11 @@ class Armada(object): ''' Syncronize Helm with the Armada Config(s) ''' + def release_prefix(prefix, chart): + ''' + how to attach prefix to chart + ''' + return "{}-{}".format(prefix, chart) # extract known charts on tiller right now known_releases = self.tiller.list_charts() @@ -63,19 +75,21 @@ class Armada(object): # determine install or upgrade by examining known releases LOG.debug("RELEASE: %s", chart.release_name) - if chart.release_name in [x[0] for x in known_releases]: + if release_prefix(prefix, chart.release_name) in [x[0] + for x in + known_releases]: # indicate to the end user what path we are taking LOG.info("Upgrading release %s", chart.release_name) # extract the installed chart and installed values from the # latest release so we can compare to the intended state installed_chart, installed_values = self.find_release_chart( - known_releases, chart.release_name) + known_releases, release_prefix(prefix, chart.release_name)) - if not self.args.disable_update_pre: + if not self.disable_update_pre: pre_actions = getattr(chart.upgrade, 'pre', {}) - if not self.args.disable_update_post: + if not self.disable_update_post: post_actions = getattr(chart.upgrade, 'post', {}) # show delta for both the chart templates and the chart values @@ -91,34 +105,33 @@ class Armada(object): continue # do actual update - self.tiller.update_release(protoc_chart, self.args.dry_run, - chart.release_name, chart.namespace, - prefix, pre_actions, post_actions, + self.tiller.update_release(protoc_chart, + self.dry_run, + chart.release_name, + chart.namespace, + prefix, pre_actions, + post_actions, disable_hooks=chart. upgrade.no_hooks, values=yaml.safe_dump(values)) # process install else: - try: - LOG.info("Installing release %s", chart.release_name) - self.tiller.install_release(protoc_chart, - self.args.dry_run, - chart.release_name, - chart.namespace, - prefix, - values=yaml.safe_dump(values)) - except Exception: - LOG.error("Install failed, continuing.") + LOG.info("Installing release %s", chart.release_name) + self.tiller.install_release(protoc_chart, + self.dry_run, + chart.release_name, + chart.namespace, + prefix, + values=yaml.safe_dump(values)) LOG.debug("Cleaning up chart source in %s", chartbuilder.source_directory) chartbuilder.source_cleanup() - if self.args.enable_chart_cleanup: - self.tiller.chart_cleanup(prefix, self.config['armada']['charts'], - known_releases) + if self.enable_chart_cleanup: + self.tiller.chart_cleanup(prefix, self.config['armada']['charts']) def show_diff(self, chart, installed_chart, installed_values, target_chart, target_values): diff --git a/armada/handlers/chartbuilder.py b/armada/chartbuilder.py similarity index 96% rename from armada/handlers/chartbuilder.py rename to armada/chartbuilder.py index 1a3e23da..b6bca33b 100644 --- a/armada/handlers/chartbuilder.py +++ b/armada/chartbuilder.py @@ -7,8 +7,8 @@ from hapi.chart.metadata_pb2 import Metadata from hapi.chart.config_pb2 import Config from supermutes.dot import dotify -from armada.logutil import LOG -from armada.utils.git import git_clone, source_cleanup +from logutil import LOG +from git import git_clone, source_cleanup class ChartBuilder(object): @@ -65,7 +65,8 @@ class ChartBuilder(object): self._source_tmp_dir = git_clone(self.chart.source.location, self.chart.source.reference) - return os.path.join(self._source_tmp_dir, self.chart.source.subpath) + return os.path.join(self._source_tmp_dir, + self.chart.source.subpath) else: LOG.exception("Unknown source type %s for chart %s", diff --git a/armada/utils/git.py b/armada/git.py similarity index 100% rename from armada/utils/git.py rename to armada/git.py diff --git a/armada/handlers/__init__.py b/armada/handlers/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/armada/handlers/k8s.py b/armada/k8s.py similarity index 97% rename from armada/handlers/k8s.py rename to armada/k8s.py index f6a289af..3372c729 100644 --- a/armada/handlers/k8s.py +++ b/armada/k8s.py @@ -1,7 +1,7 @@ from kubernetes import client, config from kubernetes.client.rest import ApiException -from armada.logutil import LOG +from logutil import LOG class K8s(object): ''' diff --git a/armada/logutil.py b/armada/logutil.py index 937f8559..4eec84b6 100644 --- a/armada/logutil.py +++ b/armada/logutil.py @@ -6,13 +6,13 @@ LOG = logging.getLogger('armada') LOG_FORMAT = '%(asctime)s %(name)-12s %(levelname)-8s %(message)s' LOG_DATE = '%m-%d %H:%M' -def setup_logging(args): +def setup_logging(debug): ''' :params args - logging information arguments in cli various utilties to support logging ''' - level = logging.DEBUG if args.debug else logging.INFO + level = logging.DEBUG if debug else logging.INFO logging.basicConfig(level=level, format=LOG_FORMAT, date_fmt=LOG_DATE) handler = SysLogHandler(address='/dev/log') syslog_formatter = logging.Formatter('%(name)s: %(levelname)s %(message)s') diff --git a/armada/handlers/tiller.py b/armada/tiller.py similarity index 97% rename from armada/handlers/tiller.py rename to armada/tiller.py index 59383474..c9870619 100644 --- a/armada/handlers/tiller.py +++ b/armada/tiller.py @@ -3,8 +3,8 @@ from hapi.services.tiller_pb2 import ReleaseServiceStub, ListReleasesRequest, \ InstallReleaseRequest, UpdateReleaseRequest, UninstallReleaseRequest from hapi.chart.config_pb2 import Config -from armada.handlers.k8s import K8s -from armada.logutil import LOG +from k8s import K8s +from logutil import LOG TILLER_PORT = 44134 TILLER_VERSION = b'2.1.3' @@ -65,6 +65,15 @@ class Tiller(object): '''Stub method to support arbitrary ports in the future''' return TILLER_PORT + def tiller_status(self): + ''' + return if tiller exist or not + ''' + if self._get_tiller_ip: + return True + + return False + def list_releases(self): ''' List Helm Releases diff --git a/armada/utils/__init__.py b/armada/utils/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/armada.yaml b/examples/armada.yaml deleted file mode 100644 index 2ec008c0..00000000 --- a/examples/armada.yaml +++ /dev/null @@ -1,77 +0,0 @@ -endpoints: &endpoints - glance: - this: is an example - -armada: - - # results in "armada-keystone" release name below - # to avoid manipulating releases managed directtly - # with helm or other armadas - release_prefix: armada - - charts: - - # silent dependency - - chart: &helm-toolkit - name: helm-toolkit - release_name: null - namespace: null - values: {} - source: - type: git - location: git://github.com/att-comdev/openstack-helm - subpath: helm-toolkit - reference: master - dependencies: [] - - - chart: &mariadb - name: mariadb - release_name: mariadb - namespace: openstack - install: - no_hooks: false - upgrade: - no_hooks: false - pre: - delete: [] - create: [] - post: - delete: [] - create: [] - values: - endpoints: *endpoints - source: - type: git - location: git://github.com/att-comdev/openstack-helm - subpath: mariadb - reference: master - dependencies: - - *helm-toolkit - - - chart: &keystone - name: keystone - release_name: keystone - namespace: openstack - install: - no_hooks: false - upgrade: - no_hooks: false - pre: - delete: - - name: keystone-db-sync - type: job - - name: keystone-db-init - type: job - create: [] - post: - delete: [] - create: [] - values: - endpoints: *endpoints - source: - type: git - location: git://github.com/att-comdev/openstack-helm - subpath: keystone - reference: master - dependencies: - - *helm-toolkit diff --git a/examples/openstack-helm.yaml b/examples/openstack-helm.yaml new file mode 100644 index 00000000..fb00ba3b --- /dev/null +++ b/examples/openstack-helm.yaml @@ -0,0 +1,176 @@ +endpoints: &endpoints + hello-world: + this: is an example + +armada: + + # results in "armada-keystone" release name below + # to avoid manipulating releases managed directtly + # with helm or other armadas + release_prefix: armada + + charts: + + # silent dependency + - chart: &helm-toolkit + name: helm-toolkit + release_name: null + namespace: null + values: {} + source: + type: git + location: git://github.com/openstack/openstack-helm + subpath: helm-toolkit + reference: master + dependencies: [] + + - chart: &mariadb + name: mariadb + release_name: mariadb + namespace: openstack + install: + no_hooks: false + upgrade: + no_hooks: false + pre: + delete: [] + create: [] + post: + delete: [] + create: [] + values: + endpoints: *endpoints + replicas: 1 + volume: + size: 1Gi + source: + type: git + location: git://github.com/openstack/openstack-helm + subpath: mariadb + reference: master + dependencies: + - *helm-toolkit + + - chart: &memcached + name: memcached + release_name: memcached + namespace: openstack + install: + no_hooks: false + upgrade: + no_hooks: false + pre: + delete: [] + create: [] + post: + delete: [] + create: [] + values: + endpoints: *endpoints + source: + type: git + location: git://github.com/openstack/openstack-helm + subpath: memcached + reference: master + dependencies: + - *helm-toolkit + + - chart: &etcd + name: etcd + release_name: etcd + namespace: openstack + install: + no_hooks: false + upgrade: + no_hooks: false + pre: + delete: [] + create: [] + post: + delete: [] + create: [] + values: + endpoints: *endpoints + source: + type: git + location: git://github.com/openstack/openstack-helm + subpath: etcd + reference: master + dependencies: + - *helm-toolkit + + - chart: &rabbitmq + name: rabbitmq + release_name: rabbitmq + namespace: openstack + install: + no_hooks: false + upgrade: + no_hooks: false + pre: + delete: [] + create: [] + post: + delete: [] + create: [] + values: + endpoints: *endpoints + replicas: 1 + source: + type: git + location: git://github.com/openstack/openstack-helm + subpath: rabbitmq + reference: master + dependencies: + - *helm-toolkit + + - chart: &keystone + name: keystone + release_name: keystone + namespace: openstack + install: + no_hooks: false + upgrade: + no_hooks: false + post: + delete: [] + create: [] + pre: + delete: + - name: keystone-db-sync + type: job + - name: keystone-db-init + type: job + values: + endpoints: *endpoints + source: + type: git + location: git://github.com/openstack/openstack-helm + subpath: keystone + reference: master + dependencies: + - *helm-toolkit + + - chart: &horizon + name: horizon + release_name: horizon + namespace: openstack + install: + no_hooks: false + upgrade: + no_hooks: false + pre: + delete: [] + create: [] + post: + delete: [] + create: [] + values: + endpoints: *endpoints + source: + type: git + location: git://github.com/openstack/openstack-helm + subpath: horizon + reference: master + dependencies: + - *helm-toolkit diff --git a/requirements.txt b/requirements.txt index 119b689b..d84c316e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,6 @@ kubernetes==1.0.0 protobuf==3.2.0 PyYAML==3.12 supermutes==0.2.5 + +falcon==1.1.0 +gunicorn==19.7.1 diff --git a/scripts/armada b/scripts/armada index 41ebdb42..e295f853 100755 --- a/scripts/armada +++ b/scripts/armada @@ -30,14 +30,18 @@ def parse_args(): def run(args): - armada = Armada(args) + armada = Armada(open(args.config).read(), + args.disable_update_pre, + args.disable_update_post, + args.enable_chart_cleanup, + args.dry_run) armada.sync() if __name__ == '__main__': args = parse_args() - setup_logging(args) + setup_logging(args.debug) try: run(args) diff --git a/scripts/libgit2.sh b/scripts/libgit2.sh index 173f7aa7..09caae4c 100644 --- a/scripts/libgit2.sh +++ b/scripts/libgit2.sh @@ -2,9 +2,9 @@ # Ubuntu 16.04 Install only -sudo apt install git cmake make -y -sudo apt-get install -y python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libssh2-1 libgit2-dev python-pip libgit2-24 -sudo apt-get install -y pkg-config libssh2-1-dev libhttp-parser-dev libssl-dev libz-dev +apt install git cmake make wget -y +apt-get install -y python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libssh2-1 libgit2-dev python-pip libgit2-24 +apt-get install -y pkg-config libssh2-1-dev libhttp-parser-dev libssl-dev libz-dev LIBGIT_VERSION='0.25.0' @@ -13,6 +13,6 @@ tar xzf v${LIBGIT_VERSION}.tar.gz cd libgit2-${LIBGIT_VERSION}/ cmake . make -sudo make install -sudo pip install pygit2==${LIBGIT_VERSION} -sudo ldconfig +make install +pip install pygit2==${LIBGIT_VERSION} +ldconfig diff --git a/scripts/libgit2_docker.sh b/scripts/libgit2_docker.sh new file mode 100644 index 00000000..e49a66b5 --- /dev/null +++ b/scripts/libgit2_docker.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +# Ubuntu 16.04 Install only + +apt install git cmake make -y +apt-get install -y python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libssh2-1 libgit2-dev python-pip libgit2-24 +apt-get install -y pkg-config libssh2-1-dev libhttp-parser-dev libssl-dev libz-dev + +LIBGIT_VERSION='0.25.0' + +wget https://github.com/libgit2/libgit2/archive/v${LIBGIT_VERSION}.tar.gz +tar xzf v${LIBGIT_VERSION}.tar.gz +cd libgit2-${LIBGIT_VERSION}/ +cmake . +make +make install +pip install pygit2==${LIBGIT_VERSION} +ldconfig diff --git a/server.py b/server.py new file mode 100644 index 00000000..db8a0163 --- /dev/null +++ b/server.py @@ -0,0 +1,56 @@ + +import falcon +import json +from falcon import HTTP_200 + +from armada.tiller import Tiller as tillerHandler +from armada.armada import Armada as armadaHandler +from armada.logutil import setup_logging + +class Tiller(object): + ''' + tiller service endpoints + ''' + + def on_get(self, req, resp): + ''' + get tiller status + ''' + message = "Tiller Server is {}" + if tillerHandler().tiller_status(): + resp.data = json.dumps({'message': message.format('Active')}) + else: + resp.data = json.dumps({'message': message.format('Not Present')}) + + resp.content_type = 'application/json' + resp.status = HTTP_200 + +class Armada(object): + ''' + apply armada endpoint service + ''' + + def on_post(self, req, resp): + armada = armadaHandler(req.stream.read()) + print armada.tiller.k8s.get_namespace_pod() + armada.sync() + + resp.data = json.dumps({'message': 'Success'}) + resp.content_type = 'application/json' + resp.status = HTTP_200 + + +wsgi_app = api = falcon.API() + +# Routing + +url_routes = ( + ('/tiller/status', Tiller()), + ('/apply', Armada()), +) + +for route, service in url_routes: + api.add_route(route, service) + + +setup_logging(False)