Add support for rolling upgrade action
Depends-On: I8700d499168b560e1cfeb309b4eaae1bd9ddb995 Change-Id: Ifb3b2f36735932bafa5fb8a71892b0638eb11a2e
This commit is contained in:
parent
4f96245635
commit
2b15322c5f
|
@ -15,6 +15,7 @@ import etcd
|
||||||
import jinja2
|
import jinja2
|
||||||
import json
|
import json
|
||||||
import netifaces
|
import netifaces
|
||||||
|
import pykube
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
|
||||||
|
@ -345,6 +346,80 @@ def run_daemon(cmd, user=None):
|
||||||
raise RuntimeError("Process exited with code: %d" % proc.returncode)
|
raise RuntimeError("Process exited with code: %d" % proc.returncode)
|
||||||
|
|
||||||
|
|
||||||
|
def get_pykube_client():
|
||||||
|
os.environ['KUBERNETES_SERVICE_HOST'] = 'kubernetes.default'
|
||||||
|
config = pykube.KubeConfig.from_service_account()
|
||||||
|
return pykube.HTTPClient(config)
|
||||||
|
|
||||||
|
|
||||||
|
def _reload_obj(obj, updated_dict):
|
||||||
|
obj.reload()
|
||||||
|
obj.obj = updated_dict
|
||||||
|
|
||||||
|
|
||||||
|
def get_pykube_object(object_dict, namespace, client):
|
||||||
|
obj_class = getattr(pykube, object_dict["kind"], None)
|
||||||
|
if obj_class is None:
|
||||||
|
raise RuntimeError('"%s" object is not supported, skipping.'
|
||||||
|
% object_dict['kind'])
|
||||||
|
|
||||||
|
if not object_dict['kind'] == 'Namespace':
|
||||||
|
object_dict['metadata']['namespace'] = namespace
|
||||||
|
|
||||||
|
return obj_class(client, object_dict)
|
||||||
|
|
||||||
|
UPDATABLE_OBJECTS = ('ConfigMap', 'Deployment', 'Service')
|
||||||
|
|
||||||
|
|
||||||
|
def process_pykube_object(object_dict, namespace, client):
|
||||||
|
LOG.debug("Deploying %s: \"%s\"",
|
||||||
|
object_dict["kind"], object_dict["metadata"]["name"])
|
||||||
|
|
||||||
|
obj = get_pykube_object(object_dict, namespace, client)
|
||||||
|
|
||||||
|
if obj.exists():
|
||||||
|
LOG.debug('%s "%s" already exists', object_dict['kind'],
|
||||||
|
object_dict['metadata']['name'])
|
||||||
|
if object_dict['kind'] in UPDATABLE_OBJECTS:
|
||||||
|
if object_dict['kind'] == 'Service':
|
||||||
|
# Reload object and merge new and old fields
|
||||||
|
_reload_obj(obj, object_dict)
|
||||||
|
obj.update()
|
||||||
|
LOG.debug('%s "%s" has been updated', object_dict['kind'],
|
||||||
|
object_dict['metadata']['name'])
|
||||||
|
else:
|
||||||
|
obj.create()
|
||||||
|
LOG.debug('%s "%s" has been created', object_dict['kind'],
|
||||||
|
object_dict['metadata']['name'])
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_deployment(obj):
|
||||||
|
while True:
|
||||||
|
generation = obj.obj['metadata']['generation']
|
||||||
|
observed_generation = obj.obj['status']['observedGeneration']
|
||||||
|
if observed_generation >= generation:
|
||||||
|
break
|
||||||
|
LOG.info("Waiting for deployment %s to move to new generation")
|
||||||
|
time.sleep(4.2)
|
||||||
|
obj.reload()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
desired = obj.obj['spec']['replicas']
|
||||||
|
status = obj.obj['status']
|
||||||
|
updated = status['updatedReplicas']
|
||||||
|
available = status['availableReplicas']
|
||||||
|
current = status['replicas']
|
||||||
|
if desired == updated == available == current:
|
||||||
|
break
|
||||||
|
LOG.info("Waiting for deployment %s: desired=%s, updated=%s,"
|
||||||
|
" available=%s, current=%s",
|
||||||
|
obj.obj['metadata']['name'],
|
||||||
|
desired, updated, available, current)
|
||||||
|
time.sleep(4.2)
|
||||||
|
obj.reload()
|
||||||
|
|
||||||
|
|
||||||
def get_workflow(role_name):
|
def get_workflow(role_name):
|
||||||
workflow_path = WORKFLOW_PATH_TEMPLATE % role_name
|
workflow_path = WORKFLOW_PATH_TEMPLATE % role_name
|
||||||
LOG.info("Getting workflow from %s", workflow_path)
|
LOG.info("Getting workflow from %s", workflow_path)
|
||||||
|
@ -420,10 +495,13 @@ def do_provision(role_name):
|
||||||
|
|
||||||
job = workflow.get("job")
|
job = workflow.get("job")
|
||||||
daemon = workflow.get("daemon")
|
daemon = workflow.get("daemon")
|
||||||
|
roll = workflow.get("roll")
|
||||||
if job:
|
if job:
|
||||||
execute_job(workflow, job)
|
execute_job(workflow, job)
|
||||||
elif daemon:
|
elif daemon:
|
||||||
execute_daemon(workflow, daemon)
|
execute_daemon(workflow, daemon)
|
||||||
|
elif roll is not None:
|
||||||
|
execute_roll(workflow, roll)
|
||||||
else:
|
else:
|
||||||
LOG.error("Job or daemon is not specified in workflow")
|
LOG.error("Job or daemon is not specified in workflow")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
@ -460,5 +538,20 @@ def execute_job(workflow, job):
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_roll(workflow, roll):
|
||||||
|
LOG.info("Running rolling upgrade of service %s", workflow["name"])
|
||||||
|
namespace = VARIABLES["namespace"]
|
||||||
|
client = get_pykube_client()
|
||||||
|
deployments = []
|
||||||
|
for object_dict in roll:
|
||||||
|
obj = process_pykube_object(object_dict, namespace, client)
|
||||||
|
if object_dict['kind'] == 'Deployment':
|
||||||
|
deployments.append(obj)
|
||||||
|
for obj in deployments:
|
||||||
|
wait_for_deployment(obj)
|
||||||
|
set_status_ready(workflow["name"])
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
pbr>=1.6 # Apache-2.0
|
pbr>=1.6 # Apache-2.0
|
||||||
Jinja2>=2.8 # BSD License (3 clause)
|
Jinja2>=2.8 # BSD License (3 clause)
|
||||||
|
pykube
|
||||||
python-etcd>=0.4.3 # MIT License
|
python-etcd>=0.4.3 # MIT License
|
||||||
PyYAML>=3.10.0 # MIT
|
PyYAML>=3.10.0 # MIT
|
||||||
six>=1.9.0 # MIT
|
six>=1.9.0 # MIT
|
||||||
|
|
Loading…
Reference in New Issue