import re LINE_CONTINUATION_REGEX = re.compile(r'(\s)*\\(\s)*\n') COMMAND_REGEX = re.compile('([A-Za-z]+)\s(.*)') COMMENT_CHARACTER = '#' LATEST_TAG = 'latest' class ParsedDockerfile(object): def __init__(self, commands): self.commands = commands def _get_commands_of_kind(self, kind): return [command for command in self.commands if command['command'] == kind] def _get_from_image_identifier(self): from_commands = self._get_commands_of_kind('FROM') if not from_commands: return None return from_commands[-1]['parameters'] @staticmethod def parse_image_identifier(image_identifier): """ Parses a docker image identifier, and returns a tuple of image name and tag, where the tag is filled in with "latest" if left unspecified. """ # Note: # Dockerfile images references can be of multiple forms: # server:port/some/path # somepath # server/some/path # server/some/path:tag # server:port/some/path:tag parts = image_identifier.strip().split(':') if len(parts) == 1: # somepath return (parts[0], LATEST_TAG) # Otherwise, determine if the last part is a port # or a tag. if parts[-1].find('/') >= 0: # Last part is part of the hostname. return (image_identifier, LATEST_TAG) # Remaining cases: # server/some/path:tag # server:port/some/path:tag return (':'.join(parts[0:-1]), parts[-1]) def get_base_image(self): """ Return the base image without the tag name. """ return self.get_image_and_tag()[0] def get_image_and_tag(self): """ Returns the image and tag from the FROM line of the dockerfile. """ image_identifier = self._get_from_image_identifier() if image_identifier is None: return (None, None) return self.parse_image_identifier(image_identifier) def strip_comments(contents): lines = [line for line in contents.split('\n') if not line.startswith(COMMENT_CHARACTER)] return '\n'.join(lines) def join_continued_lines(contents): return LINE_CONTINUATION_REGEX.sub('', contents) def parse_dockerfile(contents): contents = join_continued_lines(strip_comments(contents)) lines = [line for line in contents.split('\n') if len(line) > 0] commands = [] for line in lines: match_command = COMMAND_REGEX.match(line) if match_command: command = match_command.group(1).decode('utf-8').upper() parameters = match_command.group(2).decode('utf-8') commands.append({ 'command': command, 'parameters': parameters }) return ParsedDockerfile(commands) def serialize_dockerfile(parsed_dockerfile): return '\n'.join([' '.join([command['command'].encode('utf-8'), command['parameters'].encode('utf-8')]) for command in parsed_dockerfile.commands])