def _get_container_labels(container, parent_pod): """Returns key/value pairs identifying all metrics of this container. Args: container: the container object to annotate. parent_pod: the parent pod of 'container'. Returns: A dictionary of key/value pairs. If any error was detected, returns None. """ if not utilities.is_wrapped_object(container, "Container"): return None if not utilities.is_wrapped_object(parent_pod, "Pod"): return None pod_id = utilities.get_attribute(parent_pod, ["properties", "metadata", "uid"]) if not utilities.valid_string(pod_id): return None hostname = utilities.get_attribute(parent_pod, ["properties", "spec", "host"]) if not utilities.valid_string(hostname): return None short_container_name = utilities.get_short_container_name(container, parent_pod) if not utilities.valid_string(short_container_name): return None return {"pod_id": pod_id, "hostname": hostname, "container_name": short_container_name}
def test_get_short_container_name(self): """Tests get_short_container_name().""" container = { 'annotations': { 'label': 'php-redis/c6bf48e9b60c', }, 'id': 'k8s_php-redis.526c9b3e_guestbook-controller-14zj2_default', 'properties': { 'Id': 'deadbeef', 'Image': '01234567', 'Name': '/k8s_php-redis.526c9b3e_guestbook-controller-14zj2_default' }, 'timestamp': '2015-05-29T18:42:52.217499', 'type': 'Container' } parent_pod = { 'annotations': { 'label': 'guestbook-controller-14zj2' }, 'id': 'guestbook-controller-14zj2', 'properties': { 'status': { 'containerStatuses': [ { 'containerID': 'docker://deadbeef', 'image': 'brendanburns/php-redis', 'name': 'php-redis' } ], 'hostIP': '104.154.34.132', 'phase': 'Running', 'podIP': '10.64.0.5' } }, 'timestamp': '2015-05-29T18:37:04.852412', 'type': 'Pod' } self.assertEqual( 'php-redis', utilities.get_short_container_name(container, parent_pod))
def _get_container_labels(container, parent_pod): """Returns key/value pairs identifying all metrics of this container. Args: container: the container object to annotate. parent_pod: the parent pod of 'container'. Returns: A dictionary of key/value pairs. If any error was detected, returns None. """ if not utilities.is_wrapped_object(container, 'Container'): return None if not utilities.is_wrapped_object(parent_pod, 'Pod'): return None pod_id = utilities.get_attribute( parent_pod, ['properties', 'metadata', 'uid']) if not utilities.valid_string(pod_id): return None hostname = utilities.get_attribute( parent_pod, ['properties', 'spec', 'host']) if not utilities.valid_string(hostname): return None short_container_name = utilities.get_short_container_name( container, parent_pod) if not utilities.valid_string(short_container_name): return None return { 'pod_id': pod_id, 'hostname': hostname, 'container_name': short_container_name }
def _get_container_labels(container, parent_pod): """Returns key/value pairs identifying all metrics of this container. Args: container: the container object to annotate. parent_pod: the parent pod of 'container'. Returns: A dictionary of key/value pairs. If any error was detected, returns None. """ if not utilities.is_wrapped_object(container, 'Container'): return None if not utilities.is_wrapped_object(parent_pod, 'Pod'): return None pod_id = utilities.get_attribute(parent_pod, ['properties', 'metadata', 'uid']) if not utilities.valid_string(pod_id): return None hostname = utilities.get_attribute(parent_pod, ['properties', 'spec', 'host']) if not utilities.valid_string(hostname): return None short_container_name = utilities.get_short_container_name( container, parent_pod) if not utilities.valid_string(short_container_name): return None return { 'pod_id': pod_id, 'hostname': hostname, 'container_name': short_container_name }
def get_containers(gs, docker_host): """Gets the list of all containers in 'docker_host'. Args: gs: global state. docker_host: the Docker host running the containers. Returns: list of wrapped container objects. Each element in the list is the result of utilities.wrap_object(container, 'Container', ...) Raises: CollectorError: in case of failure to fetch data from Docker. Other exceptions may be raised due to exectution errors. """ containers, timestamp = gs.get_containers_cache().lookup(docker_host) if timestamp is not None: gs.logger_info( 'get_containers(docker_host=%s) cache hit returns ' '%d containers', docker_host, len(containers)) return containers url = 'http://{docker_host}:{port}/containers/json'.format( docker_host=docker_host, port=gs.get_docker_port()) # A typical value of 'docker_host' is: # k8s-guestbook-node-3.c.rising-apricot-840.internal # Use only the first period-seperated element for the test file name. fname = '{host}-containers'.format(host=docker_host.split('.')[0]) try: containers_list = fetch_data(gs, url, fname) except collector_error.CollectorError: raise except: msg = ('fetching %s or %s failed with exception %s' % (url, fname, sys.exc_info()[0])) gs.logger_exception(msg) raise collector_error.CollectorError(msg) if not isinstance(containers_list, types.ListType): msg = 'invalid response from fetching %s' % url gs.logger_exception(msg) raise collector_error.CollectorError(msg) containers = [] timestamps = [] for container_info in containers_list: # NOTE: container 'Name' is stable across container re-starts whereas # container 'Id' is not. # This may be because Kubernertes assigns the Name while Docker assigns # the Id (?) # The container Name is the only element of the array 'Names' - # why is Names an array here? # skip the leading / in the Name if not (isinstance(container_info.get('Names'), types.ListType) and container_info['Names'] and utilities.valid_string(container_info['Names'][0]) and container_info['Names'][0][0] == '/'): msg = 'invalid containers data format. docker_host=%s' % docker_host gs.logger_error(msg) raise collector_error.CollectorError(msg) container_id = container_info['Names'][0][1:] container, ts = _inspect_container(gs, docker_host, container_id) if container is None: continue if not utilities.valid_string(container.get('Name')): msg = ('missing or invalid Name attribute in container %s' % container_id) gs.logger_error(msg) raise collector_error.CollectorError(msg) if container['Name'] != ('/' + container_id): msg = ('container %s\'s Name attribute is "%s"; expecting "%s"' % (container_id, container['Name'], '/' + container_id)) gs.logger_error(msg) raise collector_error.CollectorError(msg) short_hex_id = utilities.object_to_hex_id(container) if short_hex_id is None: msg = 'Could not compute short hex ID of container %s' % container_id gs.logger_error(msg) raise collector_error.CollectorError(msg) wrapped_container = utilities.wrap_object( container, 'Container', container_id, ts, label=short_hex_id) containers.append(wrapped_container) timestamps.append(ts) # Modify the container's label after the wrapped container was added # to the containers list. # Compute the container's short name to create a better container label: # short_container_name/short_hex_id. # For example: "cassandra/d85b599c17d8". parent_pod_id = utilities.get_parent_pod_id(wrapped_container) if parent_pod_id is None: continue parent_pod = kubernetes.get_one_pod(gs, docker_host, parent_pod_id) if parent_pod is None: continue short_container_name = utilities.get_short_container_name( wrapped_container, parent_pod) if not utilities.valid_string(short_container_name): continue wrapped_container['annotations']['label'] = (short_container_name + '/' + short_hex_id) ret_value = gs.get_containers_cache().update( docker_host, containers, min(timestamps) if timestamps else time.time()) gs.logger_info( 'get_containers(docker_host=%s) returns %d containers', docker_host, len(containers)) return ret_value
def test_get_short_container_name(self): """Tests get_short_container_name().""" self.assertEqual( 'php-redis', utilities.get_short_container_name(CONTAINER, PARENT_POD))
def get_containers(gs, docker_host): """Gets the list of all containers in 'docker_host'. Args: gs: global state. docker_host: the Docker host running the containers. Returns: list of wrapped container objects. Each element in the list is the result of utilities.wrap_object(container, 'Container', ...) Raises: CollectorError: in case of failure to fetch data from Docker. Other exceptions may be raised due to exectution errors. """ containers, timestamp = gs.get_containers_cache().lookup(docker_host) if timestamp is not None: gs.logger_info( 'get_containers(docker_host=%s) cache hit returns ' '%d containers', docker_host, len(containers)) return containers url = 'http://{docker_host}:{port}/containers/json'.format( docker_host=docker_host, port=gs.get_docker_port()) # A typical value of 'docker_host' is: # k8s-guestbook-node-3.c.rising-apricot-840.internal # Use only the first period-seperated element for the test file name. fname = '{host}-containers'.format(host=docker_host.split('.')[0]) try: containers_list = fetch_data(gs, url, fname) except collector_error.CollectorError: raise except: msg = ('fetching %s or %s failed with exception %s' % (url, fname, sys.exc_info()[0])) gs.logger_exception(msg) raise collector_error.CollectorError(msg) if not isinstance(containers_list, types.ListType): msg = 'invalid response from fetching %s' % url gs.logger_exception(msg) raise collector_error.CollectorError(msg) containers = [] timestamps = [] for container_info in containers_list: # NOTE: container 'Name' is stable across container re-starts whereas # container 'Id' is not. # This may be because Kubernertes assigns the Name while Docker assigns # the Id (?) # The container Name is the only element of the array 'Names' - # why is Names an array here? # skip the leading / in the Name if not (isinstance(container_info.get('Names'), types.ListType) and container_info['Names'] and utilities.valid_string(container_info['Names'][0]) and container_info['Names'][0][0] == '/'): msg = 'invalid containers data format. docker_host=%s' % docker_host gs.logger_error(msg) raise collector_error.CollectorError(msg) container_id = container_info['Names'][0][1:] container, ts = _inspect_container(gs, docker_host, container_id) if container is None: continue if not utilities.valid_string(container.get('Name')): msg = ('missing or invalid Name attribute in container %s' % container_id) gs.logger_error(msg) raise collector_error.CollectorError(msg) if container['Name'] != ('/' + container_id): msg = ('container %s\'s Name attribute is "%s"; expecting "%s"' % (container_id, container['Name'], '/' + container_id)) gs.logger_error(msg) raise collector_error.CollectorError(msg) short_hex_id = utilities.object_to_hex_id(container) if short_hex_id is None: msg = 'Could not compute short hex ID of container %s' % container_id gs.logger_error(msg) raise collector_error.CollectorError(msg) wrapped_container = utilities.wrap_object(container, 'Container', container_id, ts, label=short_hex_id) containers.append(wrapped_container) timestamps.append(ts) # Modify the container's label after the wrapped container was added # to the containers list. # Compute the container's short name to create a better container label: # short_container_name/short_hex_id. # For example: "cassandra/d85b599c17d8". parent_pod_id = utilities.get_parent_pod_id(wrapped_container) if parent_pod_id is None: continue parent_pod = kubernetes.get_one_pod(gs, docker_host, parent_pod_id) if parent_pod is None: continue short_container_name = utilities.get_short_container_name( wrapped_container, parent_pod) if not utilities.valid_string(short_container_name): continue wrapped_container['annotations']['label'] = (short_container_name + '/' + short_hex_id) ret_value = gs.get_containers_cache().update( docker_host, containers, min(timestamps) if timestamps else time.time()) gs.logger_info('get_containers(docker_host=%s) returns %d containers', docker_host, len(containers)) return ret_value