diff --git a/doc/source/dsl.rst b/doc/source/dsl.rst index 20e6e5b6..dc37ccb4 100644 --- a/doc/source/dsl.rst +++ b/doc/source/dsl.rst @@ -85,7 +85,8 @@ service +---------------+-----------------------------------------------+----------+------------------+------------+ | kind | Kind of k8s object to use for containers | false | one of: | Deployment | | | deployment | | ["Deployment", | | -| | | | "Daemonset"] | | +| | | | "Daemonset", | | +| | | | "PetSet"] | | +---------------+-----------------------------------------------+----------+------------------+------------+ | containers | List of containers under multi-container pod | true | container_ array | | +---------------+-----------------------------------------------+----------+------------------+------------+ diff --git a/fuel_ccp/common/utils.py b/fuel_ccp/common/utils.py index 759a7d74..76cc9970 100644 --- a/fuel_ccp/common/utils.py +++ b/fuel_ccp/common/utils.py @@ -95,6 +95,9 @@ def get_deployed_components(): """Returns set of deployed components.""" deployed_daemonsets = kubernetes.list_cluster_daemonsets() deployed_deployments = kubernetes.list_cluster_deployments() + deployed_petsets = kubernetes.list_cluster_petsets() deployed_components = set(kubernetes.get_object_names( - itertools.chain(deployed_daemonsets, deployed_deployments))) + itertools.chain(deployed_daemonsets, deployed_deployments, + deployed_petsets)) + ) return deployed_components diff --git a/fuel_ccp/deploy.py b/fuel_ccp/deploy.py index 6cc524b3..7235a8ca 100644 --- a/fuel_ccp/deploy.py +++ b/fuel_ccp/deploy.py @@ -109,6 +109,10 @@ def parse_role(component, topology, configmaps): obj = templates.serialize_daemonset(service_name, cont_spec, affinity, component_name) + elif service.get("kind") == "PetSet": + replicas = replicas or 1 + obj = templates.serialize_petset(service_name, cont_spec, + affinity, replicas, component_name) else: replicas = replicas or 1 obj = templates.serialize_deployment(service_name, cont_spec, @@ -179,7 +183,8 @@ def _process_ports(service): if ingress_host: ingress_rules.append(templates.serialize_ingress_rule( service["name"], ingress_host, source_port)) - service_template = templates.serialize_service(service["name"], ports) + service_template = templates.serialize_service( + service["name"], ports, service.get("kind") == "PetSet") kubernetes.process_object(service_template) if ingress_rules: diff --git a/fuel_ccp/kubernetes.py b/fuel_ccp/kubernetes.py index 42327a18..cf2b0a69 100644 --- a/fuel_ccp/kubernetes.py +++ b/fuel_ccp/kubernetes.py @@ -161,6 +161,13 @@ def list_cluster_services(): selector="ccp=true") +def list_cluster_petsets(): + client = get_client() + return pykube.PetSet.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 c3b7b0d1..70fa587f 100644 --- a/fuel_ccp/templates.py +++ b/fuel_ccp/templates.py @@ -341,6 +341,36 @@ def serialize_daemonset(name, spec, affinity, component_name): } +def serialize_petset(name, spec, affinity, replicas, component_name): + annotations = { + "pod.alpha.kubernetes.io/initialized": "true" + } + annotations.update(affinity) + + return { + "apiVersion": "apps/v1alpha1", + "kind": "PetSet", + "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": { @@ -371,17 +401,19 @@ def serialize_affinity(service, topology): policy, sort_keys=True)} -def serialize_service(name, ports): +def serialize_service(name, ports, headless=False): ports_spec = [] for port in ports: - spec_entry = {"protocol": "TCP", - "port": port["port"], - "targetPort": port["port"], + spec_entry = {"port": port["port"], "name": port["name"]} - if port.get("node-port"): - spec_entry.update({"nodePort": port["node-port"]}) + if not headless: + spec_entry.update({"protocol": "TCP", + "targetPort": port["port"]}) + if port.get("node-port"): + spec_entry.update({"nodePort": port["node-port"]}) ports_spec.append(spec_entry) - return { + + obj = { "apiVersion": "v1", "kind": "Service", "metadata": { @@ -391,7 +423,6 @@ def serialize_service(name, ports): } }, "spec": { - "type": "NodePort", "selector": { "app": name }, @@ -399,6 +430,13 @@ def serialize_service(name, ports): } } + if not headless: + obj["spec"]["type"] = "NodePort" + else: + obj["spec"]["clusterIP"] = "None" + + return obj + def serialize_ingress_rule(service, host, port): return { diff --git a/fuel_ccp/validation/service.py b/fuel_ccp/validation/service.py index 9b10e4bd..3c14567f 100644 --- a/fuel_ccp/validation/service.py +++ b/fuel_ccp/validation/service.py @@ -183,7 +183,7 @@ SERVICE_SCHEMA = { } }, "kind": { - "enum": ["Deployment", "DaemonSet"] + "enum": ["Deployment", "DaemonSet", "PetSet"] }, "hostNetwork": { "type": "boolean"