def test_contains_long_hex_number(self): """Tests contains_long_hex_number().""" self.assertFalse(utilities.contains_long_hex_number('')) self.assertFalse(utilities.contains_long_hex_number('cluster-insight')) self.assertFalse(utilities.contains_long_hex_number('abc-123')) self.assertFalse(utilities.contains_long_hex_number(u'hello01234567')) self.assertTrue(utilities.contains_long_hex_number(u'hello.01234567')) self.assertTrue(utilities.contains_long_hex_number( 'k8s_php-redis.b317029a_guestbook-controller-ls6k1.default.api_' 'f991d53e-b949-11e4-8246-42010af0c3dd_8dcdfec8'))
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) # The 'container_id' is most often unique, because it contains long # unique hex numbers. However, in some cases the 'container_id' is simply # the image name, such as "cluster-insight". In this case 'container_id' # is not unique in the context graph, so we make it unique by appending # the a prefix of the Docker ID of the container. hex_id = utilities.object_to_hex_id(container) if 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) if utilities.contains_long_hex_number(container_id): short_label = hex_id unique_id = container_id else: # The short label is descriptive when 'container_id' does not contain # long hex numbers. short_label = container_id unique_id = '{container_id}-{hex_id}'.format( container_id=container_id, hex_id=hex_id) wrapped_container = utilities.wrap_object( container, 'Container', unique_id, ts, label=short_label) containers.append(wrapped_container) timestamps.append(ts) # If the container's label does not contain long hex fields, it is # good enough. It should not be replaced with anything else. if not utilities.contains_long_hex_number(short_label): continue # 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 + '/' + 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