def get_image(gs, docker_host, container): """Gets the information of the given image in the given host. Args: gs: global state. docker_host: Docker host name. Must not be empty. container: the container which runs the image. Returns: If image was found, returns the wrapped image object, which is the result of utilities.wrap_object(image, 'Image', ...) If the image was not found, returns None. Raises: CollectorError: in case of failure to fetch data from Docker. ValueError: in case the container does not contain a valid image ID. Other exceptions may be raised due to exectution errors. """ assert utilities.is_wrapped_object(container, 'Container') # The 'image_id' should be a long hexadecimal string. image_id = utilities.get_attribute(container, ['properties', 'Image']) if not utilities.valid_hex_id(image_id): msg = 'missing or invalid image ID in container ID=%s' % container['id'] gs.logger_error(msg) raise ValueError(msg) # The 'image_name' should be a symbolic name (not a hexadecimal string). image_name = utilities.get_attribute( container, ['properties', 'Config', 'Image']) if ((not utilities.valid_string(image_name)) or utilities.valid_hex_id(image_name)): msg = 'missing or invalid image name in container ID=%s' % container['id'] gs.logger_error(msg) raise ValueError(msg) cache_key = '%s|%s' % (docker_host, image_id) image, timestamp_secs = gs.get_images_cache().lookup(cache_key) if timestamp_secs is not None: gs.logger_info('get_image(docker_host=%s, image_id=%s) cache hit', docker_host, image_id) return image # 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. # The typical value of 'image_name' is: # brendanburns/php-redis # We convert embedded '/' and ':' characters to '-' to avoid interference with # the directory structure or file system. url = 'http://{docker_host}:{port}/images/{image_id}/json'.format( docker_host=docker_host, port=gs.get_docker_port(), image_id=image_id) fname = '{host}-image-{id}'.format( host=docker_host.split('.')[0], id=image_name.replace('/', '-').replace(':', '-')) try: image = fetch_data(gs, url, fname, expect_missing=True) except ValueError: # image not found. msg = 'image not found for image_id: %s' % image_id gs.logger_info(msg) return None except collector_error.CollectorError: raise except: msg = 'fetching %s failed with exception %s' % (url, sys.exc_info()[0]) gs.logger_exception(msg) raise collector_error.CollectorError(msg) now = time.time() # compute the two labels of the image. # The first is a 12-digit hexadecimal number shown by "docker images". # The second is the symbolic name of the image. full_hex_label = image.get('Id') if not (isinstance(full_hex_label, types.StringTypes) and full_hex_label): msg = 'Image id=%s has an invalid "Id" attribute value' % image_id gs.logger_error(msg) raise collector_error.CollectorError(msg) short_hex_label = utilities.object_to_hex_id(image) if short_hex_label is None: msg = 'Could not compute short hex ID of image %s' % image_id gs.logger_error(msg) raise collector_error.CollectorError(msg) wrapped_image = utilities.wrap_object( image, 'Image', full_hex_label, now, label=short_hex_label, alt_label=image_name) ret_value = gs.get_images_cache().update(cache_key, wrapped_image, now) gs.logger_info('get_image(docker_host=%s, image_id=%s, image_name=%s)', docker_host, image_id, image_name) return ret_value
def get_version(gs): """Returns a human-readable information of the currently running image. Args: gs: global state. Returns: A string of the form: <symbolic container name> <container hex ID> <creation date and time> Raises: CollectorError: in case of any error to compute the running image information. """ version, timestamp_secs = gs.get_version_cache().lookup('') if timestamp_secs is not None: assert utilities.valid_string(version) gs.logger_info('get_version() cache hit') return version if gs.get_testing(): fname = 'testdata/proc-self-cgroup.txt' else: fname = '/proc/self/cgroup' try: f = open(fname, 'r') cgroup = f.read() f.close() except IOError: # file not found msg = 'failed to open or read %s' % fname gs.logger_exception(msg) raise collector_error.CollectorError(msg) except: msg = 'reading %s failed with exception %s' % (fname, sys.exc_info()[0]) gs.logger_exception(msg) raise collector_error.CollectorError(msg) # The file must contain an entry for '\d+:cpu:/...'. m = re.search(r'\b\d+:cpu:/([0-9a-fA-F]+)\b', cgroup) if not m: msg = 'could not find an entry for "cpu:/docker/..." in %s' % fname gs.logger_error(msg) raise collector_error.CollectorError(msg) hex_container_id = m.group(1) if gs.get_testing(): # This pod name is guaranteed to match a pod in the testdata directory. my_pod_name = 'kube-dns-bqw5e' else: my_pod_name = os.uname()[1] assert utilities.valid_string(my_pod_name) # Find my node name from my pod. my_node_name = None for pod in kubernetes.get_pods(gs): assert utilities.is_wrapped_object(pod, 'Pod') if pod['id'] == my_pod_name: my_node_name = utilities.get_attribute( pod, ['properties', 'spec', 'nodeName']) break if not utilities.valid_string(my_node_name): msg = ('could not find pod %s or this pod does not contain a valid ' 'node name' % my_pod_name) gs.logger_error(msg) raise collector_error.CollectorError(msg) # inspect the running container. # Must specify an explicit host name (not "localhost"). url = 'http://{host}:{port}/containers/{container_id}/json'.format( host=my_node_name, port=gs.get_docker_port(), container_id=hex_container_id) container = fetch_data(gs, url, 'container-' + hex_container_id[:12]) # Fetch the image symbolic name and hex ID from the container information. symbolic_image_id = utilities.get_attribute(container, ['Config', 'Image']) hex_image_id = utilities.get_attribute(container, ['Image']) # Verify the image symbolic name and the image hex ID. if not (utilities.valid_string(symbolic_image_id) and not utilities.valid_hex_id(symbolic_image_id) and utilities.valid_hex_id(hex_image_id)): msg = 'could not find or invalid image information in container %s' % url gs.logger_error(msg) raise collector_error.CollectorError(msg) # Fetch image information. # Must specify an explicit host name (not "localhost"). url = 'http://{host}:{port}/images/{image_id}/json'.format( host=my_node_name, port=gs.get_docker_port(), image_id=hex_image_id) image = fetch_data(gs, url, 'image-' + hex_image_id[:12]) # Fetch the image creation timestamp. created = utilities.get_attribute(image, ['Created']) if not utilities.valid_string(created): msg = 'could not find image creation timestamp in %s' % url gs.logger_error(msg) raise collector_error.CollectorError(msg) # Remove the trailing subsecond part of the creation timestamp. created = re.sub(r'\.[0-9]+Z$', '', created) version = '%s %s %s' % (symbolic_image_id, hex_image_id[:12], created) ret_value = gs.get_version_cache().update('', version) gs.logger_info('get_version() returns: %s', ret_value) return ret_value
def get_version(gs): """Returns a human-readable information of the currently running image. Args: gs: global state. Returns: A string of the form: <symbolic container name> <container hex ID> <creation date and time> Raises: CollectorError: in case of any error to compute the running image information. """ version, timestamp_secs = gs.get_version_cache().lookup('') if timestamp_secs is not None: assert utilities.valid_string(version) gs.logger_info('get_version() cache hit') return version if gs.get_testing(): fname = 'testdata/proc-self-cgroup.txt' else: fname = '/proc/self/cgroup' try: f = open(fname, 'r') cgroup = f.read() f.close() except IOError: # file not found msg = 'failed to open or read %s' % fname gs.logger_exception(msg) raise collector_error.CollectorError(msg) except: msg = 'reading %s failed with exception %s' % (fname, sys.exc_info()[0]) gs.logger_exception(msg) raise collector_error.CollectorError(msg) # The file must contain an entry for 'cpu:/docker/...'. m = re.search(r'\b[0-9]+:cpu:/docker/([0-9a-fA-F]+)\b', cgroup) if not m: msg = 'could not find an entry for "cpu:/docker/..." in %s' % fname gs.logger_error(msg) raise collector_error.CollectorError(msg) hex_container_id = m.group(1) # inspect the running container. url = 'http://localhost:{port}/containers/{container_id}/json'.format( port=gs.get_docker_port(), container_id=hex_container_id) container = fetch_data(gs, url, 'container-' + hex_container_id[:12]) # Fetch the image symbolic name and hex ID from the container information. symbolic_image_id = utilities.get_attribute(container, ['Config', 'Image']) hex_image_id = utilities.get_attribute(container, ['Image']) # Verify the image symbolic name and the image hex ID. if not (utilities.valid_string(symbolic_image_id) and not utilities.valid_hex_id(symbolic_image_id) and utilities.valid_hex_id(hex_image_id)): msg = 'could not find or invalid image information in container %s' % url gs.logger_error(msg) raise collector_error.CollectorError(msg) # Fetch image information. url = 'http://localhost:{port}/images/{image_id}/json'.format( port=gs.get_docker_port(), image_id=hex_image_id) image = fetch_data(gs, url, 'image-' + hex_image_id[:12]) # Fetch the image creation timestamp. created = utilities.get_attribute(image, ['Created']) if not utilities.valid_string(created): msg = 'could not find image creation timestamp in %s' % url gs.logger_error(msg) raise collector_error.CollectorError(msg) # Remove the trailing subsecond part of the creation timestamp. created = re.sub(r'\.[0-9]+Z$', '', created) version = '%s %s %s' % (symbolic_image_id, hex_image_id[:12], created) ret_value = gs.get_version_cache().update('', version) gs.logger_info('get_version() returns: %s', ret_value) return ret_value
def get_image(gs, docker_host, container): """Gets the information of the given image in the given host. Args: gs: global state. docker_host: Docker host name. Must not be empty. container: the container which runs the image. Returns: If image was found, returns the wrapped image object, which is the result of utilities.wrap_object(image, 'Image', ...) If the image was not found, returns None. Raises: CollectorError: in case of failure to fetch data from Docker. ValueError: in case the container does not contain a valid image ID. Other exceptions may be raised due to exectution errors. """ assert utilities.is_wrapped_object(container, 'Container') # The 'image_id' should be a long hexadecimal string. image_id = utilities.get_attribute(container, ['properties', 'Image']) if not utilities.valid_hex_id(image_id): msg = 'missing or invalid image ID in container ID=%s' % container['id'] gs.logger_error(msg) raise ValueError(msg) # The 'image_name' should be a symbolic name (not a hexadecimal string). image_name = utilities.get_attribute(container, ['properties', 'Config', 'Image']) if ((not utilities.valid_string(image_name)) or utilities.valid_hex_id(image_name)): msg = 'missing or invalid image name in container ID=%s' % container[ 'id'] gs.logger_error(msg) raise ValueError(msg) cache_key = '%s|%s' % (docker_host, image_id) image, timestamp_secs = gs.get_images_cache().lookup(cache_key) if timestamp_secs is not None: gs.logger_info('get_image(docker_host=%s, image_id=%s) cache hit', docker_host, image_id) return image # 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. # The typical value of 'image_name' is: # brendanburns/php-redis # We convert embedded '/' and ':' characters to '-' to avoid interference with # the directory structure or file system. url = 'http://{docker_host}:{port}/images/{image_id}/json'.format( docker_host=docker_host, port=gs.get_docker_port(), image_id=image_id) fname = '{host}-image-{id}'.format(host=docker_host.split('.')[0], id=image_name.replace('/', '-').replace( ':', '-')) try: image = fetch_data(gs, url, fname, expect_missing=True) except ValueError: # image not found. msg = 'image not found for image_id: %s' % image_id gs.logger_info(msg) return None except collector_error.CollectorError: raise except: msg = 'fetching %s failed with exception %s' % (url, sys.exc_info()[0]) gs.logger_exception(msg) raise collector_error.CollectorError(msg) now = time.time() # compute the two labels of the image. # The first is a 12-digit hexadecimal number shown by "docker images". # The second is the symbolic name of the image. full_hex_label = image.get('Id') if not (isinstance(full_hex_label, types.StringTypes) and full_hex_label): msg = 'Image id=%s has an invalid "Id" attribute value' % image_id gs.logger_error(msg) raise collector_error.CollectorError(msg) short_hex_label = utilities.object_to_hex_id(image) if short_hex_label is None: msg = 'Could not compute short hex ID of image %s' % image_id gs.logger_error(msg) raise collector_error.CollectorError(msg) wrapped_image = utilities.wrap_object(image, 'Image', full_hex_label, now, label=short_hex_label, alt_label=image_name) ret_value = gs.get_images_cache().update(cache_key, wrapped_image, now) gs.logger_info('get_image(docker_host=%s, image_id=%s, image_name=%s)', docker_host, image_id, image_name) return ret_value