Skip to content

Latest commit

 

History

28 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Harbor

Julia package for managing docker images and containers, with an aim to make testing with external resources simple and easy.

GitHub Actions : Build Status

Installation

using Pkg
Pkg.add("Harbor")

Usage

using Harbor

# pull an image ("name", "name:tag", and "name@sha256:..." all work)
Harbor.pull("alpine")

# list images
Harbor.images()

# run a (long-lived) container
container = Harbor.run!("alpine"; command=["sleep", "600"])

# exec in a container (throws a DockerError with exit code + stderr on failure)
output = Harbor.exec(container, ["sh", "-c", "echo -n hi"])

# fetch its logs
Harbor.logs(container)

# list containers (observed containers are never auto-removed by Harbor)
Harbor.ps()

# stop / start / restart / kill a container
Harbor.stop!(container)
Harbor.start!(container)
Harbor.restart!(container)
Harbor.kill!(container)

# remove a container
Harbor.remove!(container; force=true)

# lifecycle-managed container block: the container is force-removed
# synchronously when the block exits (even on error)
Harbor.with_container("alpine") do container
    # ...
end

Docker treats rmi name@digest as removal of the underlying image and all local tags that point to it. Harbor.remove keeps this behavior for digest-pinned images.

Environment variables

run! and exec pass environment values through a mode-0600 temporary --env-file. Values do not appear in the Docker command line or alter the Docker CLI process environment. Container display redacts them. Docker's line-based env-file format does not support NUL, CR, or LF in names or values.

Ports

Map container ports to host ports with ports; use host port 0 to get an OS-assigned ephemeral port (recommended for CI, where fixed ports collide):

Harbor.with_container("nginx"; ports=Dict(80 => 0)) do container
    port = Harbor.host_port(container, 80)  # the assigned host port
    # connect to 127.0.0.1:port ...
end

When ports is given, no wait_strategy is specified, and the run is detached, run! waits until the lowest mapped container port is genuinely ready before returning (a connection must survive docker's userland proxy, not merely be accepted by it).

Wait strategies

Control readiness via wait_strategy (with wait_timeout/wait_interval):

# wait for a mapped container port to accept connections
Harbor.run!("nginx"; ports=Dict(80 => 0), wait_strategy=(port=80,))

# wait for a log line (String or Regex)
Harbor.run!("postgres:16"; environment=Dict("POSTGRES_PASSWORD" => "pw"),
            wait_strategy=(pattern=r"database system is ready",))

# wait for an HTTP response status
Harbor.run!("nginx"; ports=Dict(80 => 8080),
            wait_strategy=(url="http://127.0.0.1:8080/", expected_status=200))

# wait for the image's HEALTHCHECK to report healthy
Harbor.run!("myimage"; wait_strategy=(healthy=true,))

# custom check
Harbor.run!("myimage"; wait_strategy=c -> Harbor.is_running(c))

If the strategy is not satisfied within wait_timeout seconds, Harbor removes the container and throws WaitTimeoutError with its logs. If the container exits first, Harbor removes it and throws ContainerExitedError with its logs.

Cleanup

Containers started by Harbor are labeled org.juliaservices.harbor=true and force-removed by a GC finalizer as a safety net. For deterministic cleanup use with_container. A cleanup failure after a successful block is thrown. If the block already failed, Harbor preserves that error and logs the cleanup failure. To reap containers leaked by crashed processes:

Harbor.prune()  # force-removes all Harbor-started containers on the host

About

No description, website, or topics provided.

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

Generated from quinnj/Example.jl