###################################################################### # Copyright 2015 The Kubernetes Authors All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ###################################################################### """Defines a ReplicatedService type by creating both a Service and an RC. This module creates a typical abstraction for running a service in a Kubernetes cluster, namely a replication controller and a service packaged together into a single unit. """ import yaml SERVICE_TYPE_COLLECTION = 'Service' RC_TYPE_COLLECTION = 'ReplicationController' def GenerateConfig(context): """Generates a Replication Controller and a matching Service. Args: context: Template context. See schema for context properties. Returns: A Container Manifest as a YAML string. """ # YAML config that we're going to create for both RC & Service config = {'resources': []} name = context.env['name'] container_name = context.properties.get('container_name', name) namespace = context.properties.get('namespace', 'default') # Define things that the Service cares about service_name = context.properties.get('service_name', name + '-service') service_type = SERVICE_TYPE_COLLECTION # Define things that the Replication Controller (rc) cares about rc_name = name + '-rc' rc_type = RC_TYPE_COLLECTION service = { 'name': service_name, 'type': service_type, 'properties': { 'apiVersion': 'v1', 'kind': 'Service', 'metadata': { 'name': service_name, 'namespace': namespace, 'labels': GenerateLabels(context, service_name), }, 'spec': { 'ports': [GenerateServicePorts(context, container_name)], 'selector': GenerateLabels(context, name) } } } set_up_external_lb = context.properties.get('external_service', None) if set_up_external_lb: service['properties']['spec']['type'] = 'LoadBalancer' config['resources'].append(service) rc = { 'name': rc_name, 'type': rc_type, 'properties': { 'apiVersion': 'v1', 'kind': 'ReplicationController', 'metadata': { 'name': rc_name, 'namespace': namespace, 'labels': GenerateLabels(context, rc_name), }, 'spec': { 'replicas': context.properties['replicas'], 'selector': GenerateLabels(context, name), 'template': { 'metadata': { 'labels': GenerateLabels(context, name), }, 'spec': { 'containers': [ { 'env': GenerateEnv(context), 'name': container_name, 'image': context.properties['image'], 'ports': [ { 'name': container_name, 'containerPort': context.properties['container_port'], } ] } ] } } } } } config['resources'].append(rc) return yaml.dump(config) def GenerateLabels(context, name): """Generates labels either from the context.properties['labels'] or generates a default label 'app':name We make a deep copy of the context.properties['labels'] section to avoid linking in the yaml document, which I believe reduces readability of the expanded template. If no labels are given, generate a default 'app':name. Args: context: Template context, which can contain the following properties: labels - Labels to generate Returns: A dict containing labels in a name:value format """ tmp_labels = context.properties.get('labels', None) ret_labels = {'app': name} if isinstance(tmp_labels, dict): for key, value in tmp_labels.iteritems(): ret_labels[key] = value return ret_labels def GenerateServicePorts(context, name): """Generates a ports section for a service. Args: context: Template context, which can contain the following properties: service_port - Port to use for the service target_port - Target port for the service protocol - Protocol to use. Returns: A dict containing a port definition """ service_port = context.properties.get('service_port', None) target_port = context.properties.get('target_port', None) protocol = context.properties.get('protocol') ports = {} if name: ports['name'] = name if service_port: ports['port'] = service_port if target_port: ports['targetPort'] = target_port if protocol: ports['protocol'] = protocol return ports def GenerateEnv(context): """Generates environmental variables for a pod. Args: context: Template context, which can contain the following properties: env - Environment variables to set. Returns: A list containing env variables in dict format {name: 'name', value: 'value'} """ env = [] tmp_env = context.properties.get('env', []) for entry in tmp_env: if isinstance(entry, dict): env.append({'name': entry.get('name'), 'value': entry.get('value')}) return env