#Build dockerfile from file content

1 messages · Page 1 of 1 (latest)

silver rampart
#

Hi, I'm looking for a way to build an image from a dockerfile that is outside of the current dagger workspace. Using absolute path to the Dockerfile doesn't work, but I can read the Dockerfile content from python using that absolute path. For example:

import dagger
import anyio


async def build():
    async with dagger.Connection() as client:
        dockerfile_path = "/workspaces/dagger-importlib/Dockerfile"
        workspace = client.host().directory(".")
        ctr = (
            client.pipeline("test")
            .container()
            .build(context=workspace, dockerfile=dockerfile_path)
            .with_exec(["apk", "add", "curl"])
            .with_exec(["curl", "https://dagger.io"])
        )
        output = await ctr.stdout()
        print(output[:300])

if __name__ == "__main__":
    anyio.run(build)

I'm not sure how dagger build function interact with the host machine, I expected it to run the build command like I do locally. If dealing with absolute path is not possible, is there a way to build from the file content instead? The dockerfile parameter currently only support a string of path to the dockerfile afaik

silver rampart
#

As a workaround, I write the Dockerfile to the working directory then delete it afterward. I still want to understand why the Dagger build commands differ from the build command made on the host machine. Is the build running on the Dagger engine, which can only access the working directory?

echo yoke
#

👋 the dockerfile argument expects the Dockerfile content, not the path

#

so you can read the Dockerfile content with python, and send it over to the build function

cunning estuary
#

@silver rampart, try this (untested, but you get the idea):

import dagger
import anyio


async def build():
    async with dagger.Connection() as client:
        dockerfile = client.host().file("/workspaces/dagger-importlib/Dockerfile")
        workspace = client.host().directory(".").with_new_file("Dockerfile", dockerfile)
        ctr = (
            client.pipeline("test")
            .container()
            .build(workspace)
            .with_exec(["apk", "add", "curl"])
            .with_exec(["curl", "https://dagger.io"])
        )
        output = await ctr.stdout()
        print(output[:300])

if __name__ == "__main__":
    anyio.run(build)
#

@wispy siren this could be a cookbook entry: "Build with Dockerfile outside of context directory".

silver rampart
silver rampart
# cunning estuary <@341590105074565121>, try this (untested, but you get the idea): ```python impo...

Here's a working sample (It's .with_file rather than with_new_file)

import dagger
import anyio


async def build():
    async with dagger.Connection() as client:
        dockerfile_path = "/workspaces/dagger-importlib/custom.Dockerfile"
        dockerfile = client.host().file(dockerfile_path)
        workspace = client.host().directory(".").with_file("custom.Dockerfile", dockerfile)
        ctr = (
            client.pipeline("test")
            .container()
            .build(context=workspace, dockerfile="custom.Dockerfile")
            .with_exec(["apk", "add", "curl"])
            .with_exec(["curl", "https://dagger.io"])
        )
        output = await ctr.stdout()
        print(output[:300])

if __name__ == "__main__":
    anyio.run(build)
echo yoke
wispy siren
#

@echo yoke @silver rampart wouldn't it also work to set the context directory to the required location and then call docker_build() in the usual way? Is there a reason to prefer the with_file() approach shown above?

import random
import sys

import anyio

import dagger


async def main():
    config = dagger.Config(log_output=sys.stdout)

    async with dagger.Connection(config) as client:
        # set build context
        context_dir = client.host().directory("/workspaces/dagger-importlib/")

        # build using Dockerfile
        # publish the resulting container to a registry
        image_ref = await context_dir.docker_build(dockerfile="custom.Dockerfile").publish(
            f"ttl.sh/hello-dagger-{random.randrange(10 ** 8)}"
        )

    print(f"Published image to: {image_ref}")


anyio.run(main)
echo yoke
wispy siren
#

Hmm, but in the previous version, only the Dockerfile is being mounted in the container, whereas in my version my understanding is that the entire context directory will be provided with the Dockerfile. So the commands like COPY, ADD etc which are relative should continue to work in my version above. However please lmk if I am misunderstanding this.

echo yoke
wispy siren
echo yoke
stable hatch
#

I just want to add that there are 2 very different ways to do this:

  1. Create a new Dockerfile from scratch, copying the contents

  2. Copy or mount the Dockerfile itself via host.File or host.Directory

#

Option 2 would look like this:

import dagger
import anyio

async def build():
    async with dagger.Connection() as client:
        dockerfile = client.host().file("/workspaces/dagger-importlib/Dockerfile")
        workspace = client.host().directory(".")
        ctr = (
            client.pipeline("test")
            .container()   .build(context=workspace.with_file("Dockerfile", dockerfile))
            .with_exec(["apk", "add", "curl"])
            .with_exec(["curl", "https://dagger.io"])
        )
        output = await ctr.stdout()
        print(output[:300])

if __name__ == "__main__":
    anyio.run(build)