diff --git a/doc/source/dsl.rst b/doc/source/dsl.rst index b98c368d..323b3799 100644 --- a/doc/source/dsl.rst +++ b/doc/source/dsl.rst @@ -101,7 +101,7 @@ service * - kind - Kind of k8s object to use for containers deployment. - false - - one of: ["Deployment", "DaemonSet"] + - one of: ["Deployment", "DaemonSet", "StatefulSet"] - Deployment * - containers - List of containers under multi-container pod. diff --git a/fuel_ccp/common/utils.py b/fuel_ccp/common/utils.py index b7fc7545..d3a86dee 100644 --- a/fuel_ccp/common/utils.py +++ b/fuel_ccp/common/utils.py @@ -1,3 +1,4 @@ +import itertools import logging import os import pkg_resources @@ -158,7 +159,8 @@ def get_deploy_components_info(rendering_context=None): def get_deployed_components(): """Returns set of deployed components.""" deployed_deployments = kubernetes.list_cluster_deployments() - deployed_components = set( - kubernetes.get_object_names(deployed_deployments) + deployed_statefulsets = kubernetes.list_cluster_statefulsets() + deployed_components = set(kubernetes.get_object_names( + itertools.chain(deployed_deployments, deployed_statefulsets)) ) return deployed_components diff --git a/fuel_ccp/deploy.py b/fuel_ccp/deploy.py index ff903e1d..f931483d 100644 --- a/fuel_ccp/deploy.py +++ b/fuel_ccp/deploy.py @@ -134,8 +134,15 @@ def parse_role(component, topology, configmaps, jinja_imports): 'Affinity is in conflict with annotations with key: %s' .format(same_keywords)) annotations.update(affinity) - obj = templates.serialize_deployment(service_name, cont_spec, annotations, - replicas, component_name, strategy) + + if service.get("kind") != "StatefulSet": + obj = templates.serialize_deployment(service_name, cont_spec, + annotations, replicas, + component_name, strategy) + else: + obj = templates.serialize_statefulset(service_name, cont_spec, + annotations, replicas, + component_name) yield [obj] yield _process_ports(service) @@ -203,6 +210,7 @@ def _process_ports(service): service["name"], ingress_host, source_port)) service_template = templates.serialize_service( service["name"], ports, + headless=service.get("kind") == "StatefulSet", annotations=service.get('annotations', {}).get('service')) yield service_template diff --git a/fuel_ccp/kubernetes.py b/fuel_ccp/kubernetes.py index 90995e56..278c7b89 100644 --- a/fuel_ccp/kubernetes.py +++ b/fuel_ccp/kubernetes.py @@ -10,7 +10,13 @@ CONF = config.CONF LOG = logging.getLogger(__name__) -UPDATABLE_OBJECTS = ('ConfigMap', 'Deployment', 'Service', 'Ingress') +UPDATABLE_OBJECTS = ( + 'ConfigMap', + 'Deployment', + 'Service', + 'Ingress', + 'StatefulSet', +) def get_client(kube_apiserver=None, key_file=None, cert_file=None, @@ -177,6 +183,13 @@ def list_cluster_ingress(): namespace=CONF.kubernetes.namespace) +def list_cluster_statefulsets(): + client = get_client() + return pykube.StatefulSet.objects(client).filter( + namespace=CONF.kubernetes.namespace, + selector="ccp=true") + + def get_object_names(items): names = [] for item in items: diff --git a/fuel_ccp/templates.py b/fuel_ccp/templates.py index edd2a320..b130a3ce 100644 --- a/fuel_ccp/templates.py +++ b/fuel_ccp/templates.py @@ -352,6 +352,31 @@ def serialize_deployment(name, spec, annotations, replicas, component_name, return deployment +def serialize_statefulset(name, spec, annotations, replicas, component_name): + return { + "apiVersion": "apps/v1beta1", + "kind": "StatefulSet", + "metadata": { + "name": name + }, + "spec": { + "serviceName": name, + "replicas": replicas, + "template": { + "metadata": { + "annotations": annotations, + "labels": { + "ccp": "true", + "app": name, + "ccp-component": component_name + } + }, + "spec": spec + } + } + } + + def serialize_affinity(service, topology): policy = { "nodeAffinity": { diff --git a/fuel_ccp/validation/service.py b/fuel_ccp/validation/service.py index fa373821..6429e538 100644 --- a/fuel_ccp/validation/service.py +++ b/fuel_ccp/validation/service.py @@ -179,6 +179,13 @@ SERVICE_SCHEMA = { "additionalProperties": False, "required": ["name", "containers"], + "not": { # strategy needs to be absent for StatefulSet's + "properties": { + "kind": {"enum": ["StatefulSet"]}, + }, + "required": ["kind", "strategy"], + }, + "properties": { "name": NOT_EMPTY_STRING_SCHEMA, "ports": { @@ -207,7 +214,7 @@ SERVICE_SCHEMA = { } }, "kind": { - "enum": ["Deployment", "DaemonSet"] + "enum": ["Deployment", "DaemonSet", "StatefulSet"] }, "hostNetwork": { "type": "boolean"