Example #1
0
    def __init__(self, source=None):
        """Create a new Dockerfile.

        Optionally parse a source Dockerfile and populate the new Dockerfile
        instance with the source Dockerfile commands.

        Args:
            source: optional string naming a path to a source Dockerfile on
                disk used to initialize the `dockerphile.Dockerfile` commands.

        Returns:
            Nothing. Instantiates `self` attributes for class instance created.

        Raises:
            Nothing.

        """
        self.sequence = []
        if source is not None:
            escape_directive = parse_escape_directive(source)
            if escape_directive is not None:
                self.sequence.append(escape_directive)
            for command in parse_file(source):
                instruction = to_instruction(command)
                if instruction is None:
                    continue
                elif isinstance(instruction, list):
                    self.sequence.extend(instruction)
                else:
                    self.sequence.append(instruction)
Example #2
0
def test_parse_file_success():
    ret = dockerfile.parse_file('testfiles/Dockerfile.ok')
    assert ret == (
        dockerfile.Command(
            cmd='from', sub_cmd=None, json=False, value=('ubuntu:xenial',),
            start_line=1, original='FROM ubuntu:xenial',
        ),
        dockerfile.Command(
            cmd='cmd', sub_cmd=None, json=True, value=('echo', 'hi'),
            start_line=2, original='CMD ["echo", "hi"]',
        ),
    )
def convert(filepath):
    environment = []
    working_directory = []
    entry_cmd = {"entrypoint": "", "cmd": ""}

    hostname = "building-" + randomname(10)
    for command in dockerfile.parse_file(filepath):
        if command.cmd == "from":
            pass
            #from_command_lxd(hostname, command.value)

        elif command.cmd == "run":
            pass
            #run_command_lxd(hostname, command.value)

        elif command.cmd == "copy":
            pass
            #copy_command_lxd(hostname, command.value)

        elif command.cmd == "env":
            pass
            environment.append(env_command_lxd(hostname, command.value))

        elif command.cmd == "volume":
            volume_command_lxd(hostname, command.value)

        elif command.cmd == "entrypoint":
            entry_cmd["entrypoint"] = command.value
            entry_cmd["entrypoint_json"] = command.json

        elif command.cmd == "cmd":
            entry_cmd["cmd"] = command.value
            entry_cmd["cmd_json"] = command.json

        else:
            print(command.cmd)
    lxdcontroller.stop_machine(hostname)
    lxdcontroller.delete_machine(hostname)

    print(make_startup_command(entry_cmd))
Example #4
0
 def from_file(cls, dockerfile: str) -> ParsedDockerfile:
     return cls(dockerfile, parse_file(dockerfile))
Example #5
0
def test_parse_file_ioerror():
    with pytest.raises(dockerfile.GoIOError) as excinfo:
        dockerfile.parse_file('Dockerfile.dne')
    assert 'Dockerfile.dne' in excinfo.value.args[0]
Example #6
0
def GenerateMap(debug: bool = False):
    """
    Parse all the dockerfiles in a collection and extract the stages and the dependencies (images) of each stage;
    cross-relate them with the declarations of default images; and build a map of all the images in the collection.
    """

    cmap = CollectionMap()

    for dfilename in [Path(x.name).stem for x in CDIR.glob("*.dockerfile")]:
        if debug:
            print("·", dfilename)

        dfile = Dockerfile()

        stg = None

        for item in dockerfile.parse_file(str(CDIR /
                                              f"{dfilename}.dockerfile")):

            if item.cmd == "arg":
                _val = item.value[0]
                if not _val.startswith("REGISTRY="):
                    if _val.startswith("IMAGE="):
                        if dfile.argimg is not None:
                            raise Exception(
                                f"ARG IMAGE was already defined in <{dfilename}> [{_val}]!"
                            )
                        # Extract image name from IMAGE="name"
                        dfile.argimg = _val[7:-1]
                    else:
                        raise Exception(f"Unknown ARG <{_val}>!")

                continue

            if item.cmd == "from":
                if stg is not None:
                    # This was not the first stage in this dockerfile, save the previous one
                    dfile.addStage(stg)

                stg = Stage()

                _val = item.value[0]
                stg.value = dfile.markOrigin(_val)
                if len(item.value) != 1:
                    # Second argument must be 'AS', between the image and the tag
                    if item.value[1].upper() != "AS":
                        raise Exception("Second item should be 'AS'!")
                    stg.tag = item.value[2]

                continue

            if item.cmd == "copy":
                if "--from=" not in item.flags[0].lower():
                    raise Exception(
                        f"Second item of <{item.flags}> should be '--from=*'!")
                stg.addDep(dfile.markOrigin(item.flags[0][7:]))

                continue

            if item.cmd == "run" and len(item.flags) > 0:
                _val = item.flags[0]
                if _val.startswith("--mount=type="):
                    stg.addDep(
                        dfile.markOrigin(
                            _val.split(",from=")[1].split(",")[0]))
                else:
                    raise Exception(f"Unknown RUN flag <{_val}>!")

        if stg is None:
            raise Exception(f"No stages found in dockerfile <{dfilename}>!")

        dfile.addStage(stg)

        cmap.addDockerfile(dfilename, dfile)

    for key, args in DefaultOpts.items():
        if args[0] not in cmap.data:
            raise Exception(f"Dockerfile <{args[0]}> not found in data!")
        cmap.data[args[0]].addArtifact((f"!R|{key}", args[1], args[2]))

    return cmap
Example #7
0
 def reload(self):
     """
     Re-parse Dockerfile.
     """
     self._commands = dockerfile.parse_file(self.dockerfile_location)
Example #8
0
def main(dockerfile_in_name: str, dockerfile_out_name: str) -> None:
    commands: List[DockerCommand] = dockerfile.parse_file(dockerfile_in_name)
    optimized_commands = optimize_docker_commands(commands)

    with open(dockerfile_out_name, 'w', encoding='utf-8') as f:
        write_docker_commands(f, optimized_commands)
Example #9
0
def planner(filename):
    """generates a plan. which will be executed at later point.

    Parameters
    ---------
    filename: string
        Dockerfile name 
    """
    config = Config()
    commands = dockerfile.parse_file(filename)
    argdict = dict()
    from_image = ""
    from_tag = "latest"
    for command in commands:
        value = command.value
        original = command.original
        cmd = command.cmd
        if cmd == 'arg':
            # ARG command only accepts single argument
            value = command.value[0]
            name, value = value.split("=")
            # TODO arg replace arg with value its set
            argdict.update(name, value)
        elif cmd == 'from':
            rep = _substitute(original, argdict)
            command = dockefile.parse_string(rep)
            value = command.value
            if len(value) > 1:
                # multi build
                # TODO handle multi build
                # do we need to?
                # for example `from python:3 as base`
                pass
            else:
                sp = rep.split(":")
                if len(sp) != 2:
                    raise SimplePushException("from should only have one tag")
                elif len(sp) > 2:
                    from_tag = sp[1]
                    from_image = sp[0]
                else:
                    from_image = rep
            # TODO get config form registry
        elif cmd == "run":
            raise SimplePushException("will not handle run command")
        elif cmd == "workdir":
            # workdir expects single argument
            config.working_dir = value[0]
        elif cmd == "copy" or cmd == "add":
            # TODO generate layer and add layer to manifest
            pass
        elif cmd == 'env':
            # refer https://docs.docker.com/engine/reference/builder/#env
            # two forms

            # first form
            if len(value) > 1:
                env_key = value[0]
                env_value = " ".join(value[1:])
                # TODO instead of appending, it should be maintained as dictionary
                # and update
                config.config.append(f"{env_key}={env_value}")
            else:

                if "=" not in original:
                    raise SimplePushException(
                        "syntax error at {cmd.start_line}. check https://docs.docker.com/engine/reference/builder/#env"
                    )
                # second form (already desired form)
                # just add to config
                config.config.append(value)
        elif cmd == "entrypoint":
            ## entrypoint will not be updated
            if config.config.entrypoint is None:
                config.config.entrypoint = value
        elif cmd == "label":
            # annotations
            # TODO fill it up later
            pass
        elif cmd == "cmd":
            config.config.cmd = value
        elif cmd == "expose":
            config.config.exposed_ports.update({port: {} for port in value})
        elif cmd == "volume":
            config.config.volumes.update({volume: {} for volume in value})
        elif cmd == "user":
            config.config.user = value[0]
        elif cmd == "onbuild":
            ## will not be used
            # TODO did not find any info on image spec
            # ignoring for now
            config.config.on_build = value[0]
        elif cmd == "stopsignal":
            config.config.stopsignal = value[0]
        elif cmd == "shell":
            # ignoring now
            # TODO
            pass
Example #10
0
    def ParseDockerfile(self, dfilepath: Path, debug: bool = False):
        dfilename = Path(dfilepath.name).stem
        dkey = dfilename
        if dfilename == 'Dockerfile':
            dkey = dfilepath.parent.name
            dfilename = f"{dkey}/{dfilename}"

        if debug:
            print(f"  · {dfilename!s}")

        dfile = Dockerfile()

        stg = None

        for item in dockerfile.parse_file(str(dfilepath)):

            if item.cmd.upper() == "ARG":
                _val = item.value[0]
                if not _val.startswith("REGISTRY="):
                    if _val.startswith("IMAGE="):
                        if dfile.argimg is not None:
                            raise Exception(
                                f"ARG IMAGE was already defined in <{dfilename}> [{_val}]!"
                            )
                        # Extract image name from IMAGE="name"
                        dfile.argimg = _val[7:-1]
                    elif _val.startswith("ARCHITECTURE="):
                        print("    ! Field ARCHITECTURE not handled yet")
                    else:
                        raise Exception(f"Unknown ARG <{_val}>!")

                continue

            if item.cmd.upper() == "FROM":
                if stg is not None:
                    # This was not the first stage in this dockerfile, save the previous one
                    dfile.addStage(stg)

                stg = Stage()

                _val = item.value[0]
                stg.value = dfile.markOrigin(_val)
                if len(item.value) != 1:
                    # Second argument must be 'AS', between the image and the tag
                    if item.value[1].upper() != "AS":
                        raise Exception("Second item should be 'AS'!")
                    stg.tag = item.value[2]

                continue

            if item.cmd.upper() == "COPY" and len(item.flags) > 0:
                if "--from=" in item.flags[0].lower():
                    stg.addDep(dfile.markOrigin(item.flags[0][7:]))

                continue

            if item.cmd.upper() == "RUN" and len(item.flags) > 0:
                _val = item.flags[0]
                if _val.startswith("--mount=type=cache"):
                    stg.addDep(
                        dfile.markOrigin(
                            _val.split(",from=")[1].split(",")[0]))

                continue

        if stg is None:
            raise Exception(f"No stages found in dockerfile <{dfilename}>!")

        dfile.addStage(stg)

        self.AddDockerfile(dkey, dfile)