Europa: integrate core packages, separate universe

Signed-off-by: Solomon Hykes <solomon@dagger.io>
This commit is contained in:
Solomon Hykes
2021-12-14 00:16:12 +00:00
parent dd4c360a7b
commit c1c585bcd5
57 changed files with 612 additions and 142 deletions

View File

@@ -0,0 +1,97 @@
package docker
import (
"dagger.io/dagger"
"dagger.io/dagger/engine/spec/engine"
)
// Modular build API for Docker containers
#Build: {
steps: [#Step, ...#Step]
output: #Image
// Generate build DAG from linerar steps
dag: {
for idx, step in steps {
// As a special case, wrap #Run into a valid step
if step.run != _|_ {
"\(idx)": {
input: _
run: step & {
image: input
output: rootfs: _
}
output: {
config: input.config
rootfs: run.output.rootfs
}
}
}
// Otherwise, just use the step as is
if step.run == _|_ {
"\(idx)": {
run: false
step
}
}
// Either way, connect input to previous output
if idx > 0 {
"\(idx)": input: dag["\(idx-1)"].output
}
}
}
if len(dag) > 0 {
output: dag["\(len(dag)-1)"].output
}
}
// A build step is anything that produces a docker image
#Step: {
input?: #Image
output: #Image
...
} | #Run
// Build step that copies files into the container image
#Copy: {
input: #Image
contents: dagger.#FS
source: string | *"/"
dest: string | *"/"
// Execute copy operation
copy: engine.#Copy & {
"input": input.rootfs
"source": {
root: contents
path: source
}
dest: copy.dest
}
output: #Image & {
config: input.config
rootfs: copy.output
}
}
// Build step that executes a Dockerfile
#Dockerfile: {
// Source directory
source: dagger.#FS
// FIXME: not yet implemented
*{
// Look for Dockerfile in source at default path
path: "Dockerfile"
} | {
// Look for Dockerfile in source at a custom path
path: string
} | {
// Custom dockerfile contents
contents: string
}
}

View File

@@ -0,0 +1,205 @@
// Build, ship and run Docker containers in Dagger
package docker
import (
"list"
"dagger.io/dagger/engine/spec/engine"
"dagger.io/dagger"
)
// A container image
#Image: {
// Root filesystem of the image.
rootfs: dagger.#FS
// Image config
config: engine.#ImageConfig
}
// Run a command in a container
#Run: {
run: true // FIXME
image: #Image
always: bool | *false
// Filesystem mounts
mounts: [name=string]: engine.#Mount
// Expose network ports
ports: [name=string]: {
frontend: dagger.#Service
backend: {
protocol: *"tcp" | "udp"
address: string
}
}
// Command to execute
cmd: {
// Name of the command to execute
// Examples: "ls", "/bin/bash"
name: string
// Positional arguments to the command
// Examples: ["/tmp"]
args: [...string]
// Command-line flags represented in a civilized form
// Example: {"-l": true, "-c": "echo hello world"}
flags: [string]: (string | true)
_flatFlags: list.FlattenN([
for k, v in flags {
if (v & bool) != _|_ {
[k]
}
if (v & string) != _|_ {
[k, v]
}
},
], 1)
}
// Optionally pass a script to interpret
// Example: "echo hello\necho world"
script?: string
if script != _|_ {
// Default interpreter is /bin/sh -c
cmd: *{
name: "/bin/sh"
flags: "-c": script
} | {}
}
// Environment variables
// Example: {"DEBUG": "1"}
env: [string]: string
// Working directory for the command
// Example: "/src"
workdir: string | *"/"
// Username or UID to ad
// User identity for this command
// Examples: "root", "0", "1002"
user: string
// Optionally attach to command standard streams
stdin: dagger.#Stream | *null
stdout: dagger.#Stream | *null
stderr: dagger.#Stream | *null
// Output fields
{
// Has the command completed?
completed: bool & (_exec.exit != _|_)
// Was completion successful?
success: bool & (_exec.exit == 0)
// Details on error, if any
error: {
// Error code
code: _exec.exit
// Error message
message: string | *null
}
output: {
rootfs?: dagger.#FS & _exec.output
files: [path=string]: {
contents: string
contents: _read.contents
_read: engine.#ReadFile & {
input: _exec.output
"path": path
}
}
directories: [path=string]: {
contents: dagger.#FS
contents: (dagger.#Subdir & {
input: _exec.output
"path": path
}).output
}
}
}
// Actually execute the command
_exec: engine.#Exec & {
args: [cmd.name] + cmd._flatFlags + cmd.args
input: image.rootfs
"mounts": [ for mnt in mounts {mnt}]
environ: [ for k, v in env {"\(k)=\(v)"}]
"workdir": workdir
"stdin": stdin
// FIXME: user
}
}
// A ref is an address for a remote container image
// Examples:
// - "index.docker.io/dagger"
// - "dagger"
// - "index.docker.io/dagger:latest"
// - "index.docker.io/dagger:latest@sha256:a89cb097693dd354de598d279c304a1c73ee550fbfff6d9ee515568e0c749cfe"
#Ref: engine.#Ref
// Download an image from a remote registry
#Pull: {
// Source ref.
source: #Ref
// Registry authentication
// Key must be registry address, for example "index.docker.io"
auth: [registry=string]: {
username: string
secret: dagger.#Secret
}
_op: engine.#Pull & {
"source": source
"auth": [ for target, creds in auth {
"target": target
creds
}]
}
// Downloaded image
image: #Image & {
rootfs: _op.output
config: _op.config
}
// FIXME: compat with Build API
output: image
}
// Upload an image to a remote repository
#Push: {
// Destination ref
dest: #Ref
// Complete ref after pushing (including digest)
result: #Ref & _push.result
// Registry authentication
// Key must be registry address
auth: [registry=string]: {
username: string
secret: dagger.#Secret
}
// Image to push
image: #Image
_push: engine.#Push & {
dest: dest
input: image.rootfs
config: image.config
}
}

View File

@@ -0,0 +1,15 @@
package docker
cmd: #Command & {
script: "echo hello world"
exec: {
name: "/bin/bash"
flags: {
"-c": script
"--noprofile": true
"--norc": true
"-e": true
"-o": "pipefail"
}
}
}

View File

@@ -0,0 +1,82 @@
package docker
import (
"dagger.io/dagger"
"universe.dagger.io/nginx"
)
build0: #Build & {
steps: [
{
output: #Image & {
config: user: "foo"
}
},
]
}
// Inventory of real-world build use cases
//
// 1. Build netlify image
// - import alpine base
// - execute 'yarn add netlify'
// 2. Build todoapp dev image
// - import nginx base
// - copy app directory into /usr/share/nginx/html
build2: {
source: dagger.#FS
build: #Build & {
steps: [
nginx.#Build & {
flavor: "alpine"
},
{
// Custom step to watermark the image
input: _
output: input & {
config: user: "42"
}
},
#Copy & {
contents: source
dest: "/usr/share/nginx/html"
},
]
}
img: build.output
// Assert:
img: config: user: "42"
}
// 3. Build alpine base image
// - pull from docker hub
// - execute 'apk add' once per package
// 4. Build app from dockerfile
// 5. execute several commands in a row
build3: {
build: #Build & {
steps: [
#Pull & {
source: "alpine"
},
#Run & {
script: "echo A > /A.txt"
},
#Run & {
script: "echo B > /B.txt"
},
#Run & {
script: "echo C > /C.txt"
},
]
}
result: build.output
}