Skip to content

Commit

Permalink
Workload support update for push-deploy
Browse files Browse the repository at this point in the history
Signed-off-by: mdgreenwald <[email protected]>
mdgreenwald committed May 20, 2020
1 parent a8d38ef commit e05eea7
Showing 6 changed files with 221 additions and 36 deletions.
27 changes: 22 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -21,17 +21,34 @@ This is pre-release software and is very limited. It will have bugs and lacks ma

```bash
TOKEN=$(
curl -H "Content-Type: application/json" -X POST \
-d '{"username":"service_account_name","password":"as8djareallylongstring9asdj8a8sdj"}' \
http://pushdeploy.domain.com/api/v1/auth
curl --header "Content-Type: application/json" \
--request POST \
-d '{"username":"${USERNAME}","password":"${PASSWORD}"}' \
https://pushdeploy.domain.com/api/v1/auth
)
```

```bash
curl -H "Authorization: Bearer $TOKEN" \
http://pushdeploy.domain.com/api/v1/deploy?image_name=my_image&image_tag=v1.0.3
curl --header "Authorization: Bearer ${TOKEN}" \
--request POST \
"https://pushdeploy.domain.com/api/v1/deployment?name=${NAME}&namespace=${NAMESPACE}&image_name=${IMAGE_NAME}&image_tag=${IMAGE_TAG}"
```

### v1 Endpoints

- /api/v1/cronjob -> v1beta1/cronjob
- /api/v1/daemonset -> apps/v1/daemonset
- /api/v1/deployment -> apps/v1/deployment

- /api/v1/deploy -> apps/v1/deployments (**Deprecated**)

### v1 Parameters

- name=name of object
- namespace=namespace of object
- image_name=image name
- image_tag=image tag

## Contributing

#### Dependencies
5 changes: 1 addition & 4 deletions pushdeploy/__init__.py
Original file line number Diff line number Diff line change
@@ -2,10 +2,7 @@
import os
from kubernetes import client, config
from flask import Flask, jsonify
from flask_jwt_extended import (
JWTManager, jwt_required, jwt_optional, create_access_token,
get_jwt_identity
)
from flask_jwt_extended import JWTManager, jwt_required, jwt_optional, create_access_token, get_jwt_identity
from pushdeploy import apiv1

def create_app(test_config=None):
132 changes: 107 additions & 25 deletions pushdeploy/apiv1.py
Original file line number Diff line number Diff line change
@@ -2,21 +2,8 @@
import json
from kubernetes import client, config
from datetime import timedelta
from flask import Blueprint
from flask import current_app
from flask import flash
from flask import g
from flask import redirect
from flask import render_template
from flask import request
from flask import session
from flask import url_for
from flask import jsonify

from flask_jwt_extended import (
JWTManager, jwt_required, jwt_optional, create_access_token,
get_jwt_identity
)
from flask import Blueprint, current_app, flash, g, redirect, render_template, request, session, url_for, jsonify
from flask_jwt_extended import JWTManager, jwt_required, jwt_optional, create_access_token, get_jwt_identity

bp = Blueprint("apiv1", __name__, url_prefix="/api/v1")

@@ -25,26 +12,71 @@ def init_api():
"""Creates instances of the incluster config and client API
and stores them in global"""
g.configuration = config.load_incluster_config()
g.api_instance = client.AppsV1Api(client.ApiClient(g.configuration))
g.apps_v1_api_instance = client.AppsV1Api(client.ApiClient(g.configuration))
g.batch_v1beta1_instance = client.BatchV1beta1Api(client.ApiClient(g.configuration))
g.PD_REGISTRY = current_app.config['PD_REGISTRY']

def read_deployment(deployment, namespace):
def list_cron_job(name, namespace):
namespace = "%s" % str(namespace)
name = "metadata.name=%s" % str(name)
api_response = g.batch_v1beta1_instance.list_namespaced_cron_job(
namespace=namespace,
field_selector=name
)
if len(api_response.items) == 1:
return api_response.items[0]
else:
return "CronJob selector not unique enough."

def list_daemon_set(name, namespace):
namespace = "%s" % str(namespace)
field = "metadata.name=%s" % str(deployment)
api_response = g.api_instance.list_namespaced_deployment(
name = "metadata.name=%s" % str(name)
api_response = g.apps_v1_api_instance.list_namespaced_daemon_set(
namespace=namespace,
field_selector=field
field_selector=name
)
if len(api_response.items) == 1:
return api_response.items[0]
else:
return "DaemonSet selector not unique enough."

def list_deployment(name, namespace):
namespace = "%s" % str(namespace)
name = "metadata.name=%s" % str(name)
api_response = g.apps_v1_api_instance.list_namespaced_deployment(
namespace=namespace,
field_selector=name
)
if len(api_response.items) == 1:
return api_response.items[0]
else:
return "Deployment selector not unique enough."

def update_deployment(deployment_object, image_name, image_tag, deployment, namespace):
def patch_cron_job(cron_job_object, image_name, image_tag, name, namespace):
image = "%s/%s:%s" % (g.PD_REGISTRY, image_name, image_tag)
cron_job_object.spec.job_template.spec.template.spec.containers[0].image = image
api_response = g.batch_v1beta1_instance.patch_namespaced_cron_job(
name=name,
namespace=namespace,
body=cron_job_object,
field_manager="push-deploy")
print("CronJob updated. status='%s'" % str(api_response.status))

def patch_daemon_set(daemon_set_object, image_name, image_tag, name, namespace):
image = "%s/%s:%s" % (g.PD_REGISTRY, image_name, image_tag)
daemon_set_object.spec.template.spec.containers[0].image = image
api_response = g.apps_v1_api_instance.patch_namespaced_daemon_set(
name=name,
namespace=namespace,
body=daemon_set_object,
field_manager="push-deploy")
print("DaemonSet updated. status='%s'" % str(api_response.status))

def patch_deployment(deployment_object, image_name, image_tag, name, namespace):
image = "%s/%s:%s" % (g.PD_REGISTRY, image_name, image_tag)
deployment_object.spec.template.spec.containers[0].image = image
api_response = g.api_instance.patch_namespaced_deployment(
name=deployment,
api_response = g.apps_v1_api_instance.patch_namespaced_deployment(
name=name,
namespace=namespace,
body=deployment_object,
field_manager="push-deploy")
@@ -74,12 +106,62 @@ def login():
access_token = create_access_token(identity=username, expires_delta=timedelta(seconds=90))
return jsonify(access_token=access_token), 200

@bp.route('/cronjob', methods=['POST'])
@jwt_required
def cronjob():
image_tag = request.args['image_tag']
image_name = request.args['image_name']
name = request.args['name']
namespace = request.args['namespace']
cronjob = patch_cron_job(
cron_job_object=list_cron_job(name=name, namespace=namespace),
image_name=image_name,
image_tag=image_tag,
name=name,
namespace=namespace)
return jsonify(msg=cronjob), 201

@bp.route('/daemonset', methods=['POST'])
@jwt_required
def daemonset():
image_tag = request.args['image_tag']
image_name = request.args['image_name']
name = request.args['name']
namespace = request.args['namespace']
daemonset = patch_daemon_set(
daemon_set_object=list_daemon_set(name=name, namespace=namespace),
image_name=image_name,
image_tag=image_tag,
name=name,
namespace=namespace)
return jsonify(msg=daemonset), 201

@bp.route('/deploy', methods=['GET'])
@jwt_required
def deploy():
image_tag = request.args['image_tag']
image_name = request.args['image_name']
deployment = request.args['deployment']
name = request.args['deployment']
namespace = request.args['namespace']
deploy = update_deployment(deployment_object=read_deployment(deployment=deployment, namespace=namespace), image_name=image_name, image_tag=image_tag, deployment=deployment, namespace=namespace)
deploy = patch_deployment(
deployment_object=list_deployment(name=name, namespace=namespace),
image_name=image_name,
image_tag=image_tag,
name=name,
namespace=namespace)
return jsonify(msg=deploy), 201

@bp.route('/deployment', methods=['POST'])
@jwt_required
def deployment():
image_tag = request.args['image_tag']
image_name = request.args['image_name']
name = request.args['name']
namespace = request.args['namespace']
deployment = patch_deployment(
deployment_object=list_deployment(name=name, namespace=namespace),
image_name=image_name,
image_tag=image_tag,
name=name,
namespace=namespace)
return jsonify(msg=deployment), 201
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
kubernetes==11.0.0
Flask==1.1.1
Flask==1.1.2
Flask-API==2.0
flask-jwt-extended==3.24.1
gunicorn==20.0.4
89 changes: 89 additions & 0 deletions tests/setup.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: cronjob-test
labels:
environment: test
spec:
schedule: "*/1 * * * *"
concurrencyPolicy: "Forbid"
failedJobsHistoryLimit: 1
successfulJobsHistoryLimit: 1
jobTemplate:
spec:
backoffLimit: 6
ttlSecondsAfterFinished: 30
template:
spec:
containers:
- name: busybox
image: busybox:1.24.0
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
resources:
limits:
cpu: 20m
memory: 32Mi
requests:
cpu: 10m
memory: 16Mi
restartPolicy: OnFailure

---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: daemonset-test
labels:
environment: test
spec:
selector:
matchLabels:
name: daemonset-test
template:
metadata:
labels:
name: daemonset-test
spec:
containers:
- name: busybox
image: busybox:1.24.0
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
resources:
limits:
cpu: 20m
memory: 32Mi
requests:
cpu: 10m
memory: 16Mi

---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-test
labels:
environment: test
spec:
selector:
matchLabels:
app: deployment-test
template:
metadata:
labels:
app: deployment-test
spec:
containers:
- name: busybox
image: busybox:1.24.0
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
resources:
limits:
cpu: 20m
memory: 32Mi
requests:
cpu: 10m
memory: 16Mi
2 changes: 1 addition & 1 deletion version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

__version__ = '0.0.4'
__version__ = '0.0.5'

0 comments on commit e05eea7

Please sign in to comment.