Merge pull request #1056 from aluzzardi/docker-load

Support loading artifacts into a Docker Engine
This commit is contained in:
Andrea Luzzardi 2021-10-12 17:51:51 -07:00 committed by GitHub
commit 16b14524d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 246 additions and 24 deletions

View File

@ -144,6 +144,16 @@ _No input._
_No output._
## op.#SaveImage
### op.#SaveImage Inputs
_No input._
### op.#SaveImage Outputs
_No output._
## op.#Subdir
### op.#Subdir Inputs

View File

@ -31,15 +31,29 @@ A container image that can run any docker command
### docker.#Command Inputs
| Name | Type | Description |
| ------------- |:-------------: |:-------------: |
|*command* | `string` |Command to execute |
|*registries* | `[]` |Image registries |
_No input._
### docker.#Command Outputs
_No output._
## docker.#Load
Load a docker image into a docker engine
### docker.#Load Inputs
| Name | Type | Description |
| ------------- |:-------------: |:-------------: |
|*tag* | `string` |Name and optionally a tag in the 'name:tag' format |
|*source* | `dagger.#Artifact` |Image source |
### docker.#Load Outputs
| Name | Type | Description |
| ------------- |:-------------: |:-------------: |
|*id* | `string` |Image ID |
## docker.#Pull
Pull a docker container
@ -79,9 +93,7 @@ Push a docker image to a remote registry
| Name | Type | Description |
| ------------- |:-------------: |:-------------: |
|*ref* | `string` |Image reference (e.g: nginx:alpine) |
|*run.command* | `"""\n # Run detach container\n OPTS=""\n \n if [ ! -z "$CONTAINER_NAME" ]; then\n \tOPTS="$OPTS --name $CONTAINER_NAME"\n fi\n \n if [ ! -z "$CONTAINER_PORTS" ]; then\n \tOPTS="$OPTS -p $CONTAINER_PORTS"\n fi\n \n docker container run -d $OPTS "$IMAGE_REF"\n """` |Command to execute |
|*run.env.IMAGE_REF* | `string` |- |
|*run.registries* | `[]` |Image registries |
### docker.#Run Outputs

View File

@ -14,14 +14,10 @@ import "alpha.dagger.io/docker/compose"
### compose.#App Inputs
| Name | Type | Description |
| ------------- |:-------------: |:-------------: |
|*name* | `*"source" \| string` |App name (use as COMPOSE_PROJECT_NAME) |
|*registries* | `[]` |Image registries |
|*run.command* | `"""\n if [ -n "$DOCKER_HOSTNAME" ]; then\n \tssh -i /key -fNT -o "StreamLocalBindUnlink=yes" -L "$(pwd)"/docker.sock:/var/run/docker.sock -p "$DOCKER_PORT" "$DOCKER_USERNAME"@"$DOCKER_HOSTNAME"\n \texport DOCKER_HOST="unix://$(pwd)/docker.sock"\n fi\n \n # Extend session duration\n echo "Host *\\nServerAliveInterval 240" \>\> "$HOME"/.ssh/config\n chmod 600 "$HOME"/.ssh/config\n \n # Move compose\n if [ -d "$SOURCE_DIR" ]; then\n \tif [ -f docker-compose.yaml ]; then\n \t\tcp docker-compose.yaml "$SOURCE_DIR"/docker-compose.yaml\n \tfi\n \tcd "$SOURCE_DIR"\n fi\n \n docker-compose build\n docker-compose up -d\n """` |Command to execute |
|*run.env.COMPOSE_PROJECT_NAME* | `*"source" \| string` |- |
|*run.package."docker-compose"* | `true` |- |
|*run.registries* | `[]` |Image registries |
| Name | Type | Description |
| ------------- |:-------------: |:-------------: |
|*name* | `*"source" \| string` |App name (use as COMPOSE_PROJECT_NAME) |
|*registries* | `[]` |Image registries |
### compose.#App Outputs

View File

@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"io/fs"
"net"
"net/url"
@ -261,6 +262,8 @@ func (p *Pipeline) doOp(ctx context.Context, op *compiler.Value, st llb.State) (
return p.FetchContainer(ctx, op, st)
case "push-container":
return p.PushContainer(ctx, op, st)
case "save-image":
return p.SaveImage(ctx, op, st)
case "fetch-git":
return p.FetchGit(ctx, op, st)
case "fetch-http":
@ -872,6 +875,63 @@ func (p *Pipeline) PushContainer(ctx context.Context, op *compiler.Value, st llb
return st, err
}
func (p *Pipeline) SaveImage(ctx context.Context, op *compiler.Value, st llb.State) (llb.State, error) {
tag, err := op.Lookup("tag").String()
if err != nil {
return st, err
}
dest, err := op.Lookup("dest").String()
if err != nil {
return st, err
}
pipeR, pipeW := io.Pipe()
var (
errCh = make(chan error)
image []byte
)
go func() {
var err error
image, err = io.ReadAll(pipeR)
errCh <- err
}()
resp, err := p.s.Export(ctx, p.State(), &p.image, bk.ExportEntry{
Type: bk.ExporterDocker,
Attrs: map[string]string{
"name": tag,
},
Output: func(_ map[string]string) (io.WriteCloser, error) {
return pipeW, nil
},
})
if err != nil {
return st, err
}
if err := <-errCh; err != nil {
return st, err
}
if id, ok := resp.ExporterResponse["containerimage.config.digest"]; ok {
st = st.File(
llb.Mkdir("/dagger", fs.FileMode(0755)),
llb.WithCustomName(p.vertexNamef("Mkdir /dagger")),
).File(
llb.Mkfile("/dagger/image_id", fs.FileMode(0644), []byte(id)),
llb.WithCustomName(p.vertexNamef("Storing image id to /dagger/image_id")),
)
}
return st.File(
llb.Mkfile(dest, 0644, image),
llb.WithCustomName(p.vertexNamef("SaveImage %s", dest)),
), nil
}
func getSecretID(secretField *compiler.Value) (string, error) {
if !secretField.HasAttr("secret") {
return "", fmt.Errorf("invalid secret %q: not a secret", secretField.Path().String())

View File

@ -0,0 +1,2 @@
# dagger state
state/**

View File

@ -0,0 +1,30 @@
plan:
package: ./docker/tests/load
name: docker-load
inputs:
dockersocket:
socket:
unix: /var/run/docker.sock
source:
dir:
path: ./docker/tests/load/testdata
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1gxwmtwahzwdmrskhf90ppwlnze30lgpm056kuesrxzeuyclrwvpsupwtpk
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzZCt6S3VGQkM4ZU9TamhQ
c2J0RDZpdzNQZUJ6V1BBdHh3M1NhTmJLeXpFCldaOVNUYVhWQW5ma3JGUk9XZWZ2
YjZIM0tMUGRoSk1QYTFkalA4S2N3UzAKLS0tIFhMeXMvaGM4UFllYWFCNWVUZFd5
U09jNHNlK094NGoyZnRlSk56T1N6K1EKJs5D3S2zPWNrGCyLWDDjq7Iif0m2JoL6
gqEjofnPSD7SjgfNKIpeOWcQ1sI7wmI4GGgaTpdhd431XxOn/fU44w==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2021-10-12T22:18:14Z"
mac: ENC[AES256_GCM,data:HjlY0FzB5hsg/VfyBbVTNWDCYV41lhPeyHOVrMdRWiOKJnV1aKGV1826fMXKcmgJK39kovcEXaXmVYOv3qtpDTlIMcOdoEflbDY/fhZmaDxxNq6QSkYfmadO84YWvS1FyouCPVZzRDe9tBdNyIT5cCx8CrgZ5bebh9aNem3lqRs=,iv:juMn1S06I5mZP8fWytn2eUkOvjNQepn6MAzDWvLxrWM=,tag:0BxrbK6EaoB44RgUHD+dpQ==,type:str]
pgp: []
encrypted_suffix: secret
version: 3.7.1

View File

@ -81,6 +81,12 @@ package op
ref: string
}
#SaveImage: {
do: "save-image"
tag: string
dest: string
}
#FetchGit: {
do: "fetch-git"
remote: string

View File

@ -31,11 +31,11 @@ import (
}
// Command to execute
command: string @dagger(input)
command: string
// Environment variables shared by all commands
env: {
[string]: string @dagger(input)
[string]: string
}
// Mount content from other artifacts
@ -44,17 +44,17 @@ import (
from: dagger.#Artifact
} | {
secret: dagger.#Secret
} @dagger(input)
}
}
// Mount persistent cache directories
cache: {
[string]: true @dagger(input)
[string]: true
}
// Mount temporary directories
tmpfs: {
[string]: true @dagger(input)
[string]: true
}
// Mount docker socket
@ -62,7 +62,7 @@ import (
// Additional packages to install
package: {
[string]: true | false | string @dagger(input)
[string]: true | false | string
}
// Image registries
@ -70,7 +70,7 @@ import (
target?: string
username: string
secret: dagger.#Secret
}] @dagger(input)
}]
// Copy contents from other artifacts
copy: [string]: from: dagger.#Artifact
@ -98,7 +98,7 @@ import (
ssh-add /key > /dev/null
if [ "$?" != 0 ]; then
exit 1
fi
fi
fi
if [[ ! -z $FINGERPRINT ]]; then
@ -131,7 +131,7 @@ import (
for registry in registries {
op.#Exec & {
args: ["/bin/bash", "-c", #"""
echo "$TARGER_HOST" | docker login --username "$DOCKER_USERNAME" --password-stdin "$(cat /password)"
echo "$TARGER_HOST" | docker login --username "$DOCKER_USERNAME" --password-stdin "$(cat /password)"
"""#,
]
env: {

View File

@ -101,6 +101,74 @@ import (
} & dagger.#Output
}
// Load a docker image into a docker engine
#Load: {
// Connect to a remote SSH server
ssh?: {
// ssh host
host: dagger.#Input & {string}
// ssh user
user: dagger.#Input & {string}
// ssh port
port: dagger.#Input & {*22 | int}
// private key
key: dagger.#Input & {dagger.#Secret}
// fingerprint
fingerprint?: dagger.#Input & {string}
// ssh key passphrase
keyPassphrase?: dagger.#Input & {dagger.#Secret}
}
// Mount local docker socket
socket?: dagger.#Stream & dagger.#Input
// Name and optionally a tag in the 'name:tag' format
tag: dagger.#Input & {string}
// Image source
source: dagger.#Input & {dagger.#Artifact}
save: #up: [
op.#Load & {from: source},
op.#SaveImage & {
"tag": tag
dest: "/image.tar"
},
]
load: #Command & {
if ssh != _|_ {
"ssh": ssh
}
if socket != _|_ {
"socket": socket
}
copy: "/src": from: save
command: "docker load -i /src/image.tar"
}
// Image ID
id: {
string
#up: [
op.#Load & {from: save},
op.#Export & {
source: "/dagger/image_id"
},
]
} & dagger.#Output
}
#Run: {
// Connect to a remote SSH server
ssh?: {

View File

@ -0,0 +1,32 @@
package docker
import (
"alpha.dagger.io/dagger"
"alpha.dagger.io/random"
)
dockersocket: dagger.#Stream & dagger.#Input
source: dagger.#Artifact & dagger.#Input
TestLoad: {
suffix: random.#String & {
seed: ""
}
image: #Build & {
"source": source
}
load: #Load & {
tag: "daggerci-image-load-\(suffix.out)"
source: image
socket: dockersocket
}
run: #Run & {
name: "daggerci-container-load-\(suffix.out)"
ref: load.id
socket: dockersocket
}
}

View File

@ -0,0 +1,2 @@
FROM alpine
RUN echo test >> /test.txt

View File

@ -126,6 +126,10 @@ setup() {
assert_failure
}
@test "docker load" {
dagger -e docker-load up
}
@test "docker compose" {
dagger -e docker-compose up
}
@ -177,7 +181,7 @@ setup() {
}
@test "google cloud: gke" {
dagger -e google-gke up
dagger -e google-gke up
}
@test "google cloud: secretmanager" {