Move prototype 69-dagger-archon to top-level

Signed-off-by: Solomon Hykes <sh.github.6811@hykes.org>
This commit is contained in:
Solomon Hykes 2020-12-29 18:45:16 -08:00
commit 30f75da114
42 changed files with 3955 additions and 0 deletions

12
Dockerfile Normal file
View File

@ -0,0 +1,12 @@
# syntax = docker/dockerfile-upstream:experimental@sha256:398a0a10f19875add7fe359a37f2f971c46746b064faf876776ae632a3472c37
FROM golang:1.14-alpine AS build
WORKDIR /src
RUN apk add --no-cache file
RUN --mount=target=. --mount=target=/root/.cache,type=cache \
CGO_ENABLED=0 go build -o /out/dagger ./cmd/dagger-frontend && file /out/dagger | grep "statically linked"
FROM scratch
COPY --from=build /out/dagger /bin/dagger
ENTRYPOINT ["/bin/dagger"]

3
Makefile Normal file
View File

@ -0,0 +1,3 @@
.PHONY: dagger
dagger:
go generate ./dagger && go build -o ./cmd/dagger/ ./cmd/dagger/

63
README.md Normal file
View File

@ -0,0 +1,63 @@
# Prototype 69-dagger-archon
This is an improved version of 64-blagger with the cuellb pattern, using
everything we learned in 64,64,66.
This prototype marks a strong convergence between SH and AL on the high-level DX and architecture
of the future OSS project. The next step is to get to a working POC implementation together,
that can run a configuration written by SA, end-to-end, and make SA happy with the DX :)
## Points of convergence:
### 1. Cue DX: the cuellb pattern
The "cuellb" pattern is a promising DX for writing delivery workflows in Cue.
On the one hand, the raw arrays are verbose, but they are easy to understand. And they
can be abstracted away by the community as they wish, using the native features of Cue.
TO quote AL: "It's easier to take something verbose and simple, and make it less verbose,
than to take something concise and complex, and make it simple."
### 2. Integration of SAAS features: runtime+engine
The OSS project must combine features from 3 codebases: `bl-runtime`, `bl-api`, and `bl`.
And it must combine them in a way that allows maximum code sharing between SAAS and OSS,
so we don't have to do everything twice.
So in addition to defining a Cue DX and runtime, we also need to define how `dagger` will
implement (or avoid implementing) saas features like settings, secrets, connectors, stacks,
envs, slugs, job history, provider catalog, etc.
A promising approach to do this is to split the OSS project in 2 components:
- 1) the runtime which loads and executes cue confiurations, and
- 2) the engine which orchestrates the runtime, and all its inputs and outputs: config repositories,
settings, dependencies, previous state, execution history, etc.
The engine is also responsible for end-user interaction.
(see 64-blagger/README.md for more details on this architecture).
### 3. Possible performance gains
One consequence of the cuellb model + engine/runtime split, is that it becomes possible (we think)
to compile a dagger job to a single LLB slug, and run it once.
This has several benefits, including making the runtime simpler, and removing the hard dependency
on a registry (which opens the door to new use cases in local development).
One possible benefit is performance. In theory, single-pass llb compilation is automatically faster
because it removes the multiple round-trips between 1) cue eval 2) buildkit run 3) registry push 4) registry pull.
The larger the configuration, the more round-trips, and the bigger the potential performance gain in dagger.
*[SH]* I have done lots of research work on this (prototypes 64-68) and the results are very encouraiging.
I have strong conviction that the performance benefits are huge. But I only have bits and pieces of POC implementations,
each focusing on a small aspect of the problem. The next step is to implement an end-to-end POC, and run a crude benchmark.
### 4. Next step: working end-to-end prototype
We agree on a possible high-level architecture, DX and UX, built on several hypothesis:
is cuellb really a good DX in practice? can dir refs really be eliminated?
is single-pass llb jit really feasible, and if so does it solve our performance issues?
how easy will it be to port Blocklayer to dagger? How much work to migrate beta users?
Now is the time to test these hypothesis with a working end-to-end implementation.
There are many remaining questions but we have enough alignment to find the answers
together.

View File

@ -0,0 +1,14 @@
package main
import (
"dagger.cloud/go/dagger"
"github.com/moby/buildkit/frontend/gateway/grpcclient"
"github.com/moby/buildkit/util/appcontext"
)
func main() {
r := &dagger.Runtime{}
if err := grpcclient.RunFromEnvironment(appcontext.Context(), r.BKFrontend); err != nil {
panic(err)
}
}

2
cmd/dagger/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
dagger
.dagger

50
cmd/dagger/cmd/compute.go Normal file
View File

@ -0,0 +1,50 @@
package cmd
import (
"context"
"fmt"
"os"
"dagger.cloud/go/dagger"
"dagger.cloud/go/dagger/ui"
"github.com/spf13/cobra"
)
var computeCmd = &cobra.Command{
Use: "compute CONFIG",
Short: "Compute a configuration",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
ctx := context.TODO()
c, err := dagger.NewClient(ctx, "")
if err != nil {
ui.Fatal(err)
}
target := args[0]
if target == "-" {
ui.Info("Assembling config from stdin\n")
// FIXME: include cue.mod/pkg from *somewhere* so stdin config can import
if err := c.SetConfig(os.Stdin); err != nil {
ui.Fatal(err)
}
} else {
ui.Info("Assembling config from %q\n", target)
if err := c.SetConfig(target); err != nil {
ui.Fatal(err)
}
}
ui.Info("Running")
output, err := c.Run(ctx, "compute")
if err != nil {
ui.Fatal(err)
}
ui.Info("Processing output")
// output.Print(os.Stdout)
fmt.Println(output.JSON())
// FIXME: write computed values back to env dir
},
}
func init() {
// computeCmd.Flags().StringP("catalog", "c", "", "Cue package catalog")
}

35
cmd/dagger/cmd/create.go Normal file
View File

@ -0,0 +1,35 @@
package cmd
import (
"fmt"
"io/ioutil"
"os"
"dagger.cloud/go/dagger/ui"
"github.com/spf13/cobra"
)
var createCmd = &cobra.Command{
Use: "create ENV BASE",
Short: "Create an env",
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
envname := args[0]
base := args[1]
envdir := ".dagger/env/" + envname
if info, err := os.Stat(envdir); err == nil {
if info.IsDir() {
ui.Fatalf("env already exists: %s", envname)
}
}
if err := os.MkdirAll(envdir, 0755); err != nil {
ui.Fatal(err)
}
baseCue := fmt.Sprintf("package env\nimport base \"%s\"\nbase\n", base)
err := ioutil.WriteFile(envdir+"/base.cue", []byte(baseCue), 0644)
if err != nil {
ui.Fatal(err)
}
ui.Info("created environment %q with base %q", envname, base)
},
}

37
cmd/dagger/cmd/root.go Normal file
View File

@ -0,0 +1,37 @@
package cmd
import (
"dagger.cloud/go/dagger/ui"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "dagger",
Short: "Open-source workflow engine",
}
func init() {
// --debug
rootCmd.PersistentFlags().Bool("debug", false, "Enable debug mode")
// --workspace
rootCmd.PersistentFlags().StringP("workspace", "w", "", "Select workspace")
rootCmd.AddCommand(
// Create an env
createCmd,
computeCmd,
// Change settings on an env
// View or edit env serti
// settingsCmd,
// Query the state of an env
// getCmd,
// unsetCmd,
// computeCmd,
// listCmd,
)
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
ui.Fatal(err)
}
}

9
cmd/dagger/main.go Normal file
View File

@ -0,0 +1,9 @@
package main
import (
"dagger.cloud/go/cmd/dagger/cmd"
)
func main() {
cmd.Execute()
}

434
dagger/client.go Normal file
View File

@ -0,0 +1,434 @@
package dagger
import (
"archive/tar"
"context"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"time"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
// Cue
"cuelang.org/go/cue"
cueerrors "cuelang.org/go/cue/errors"
cueformat "cuelang.org/go/cue/format"
// buildkit
bk "github.com/moby/buildkit/client"
_ "github.com/moby/buildkit/client/connhelper/dockercontainer"
"github.com/moby/buildkit/client/llb"
bkgw "github.com/moby/buildkit/frontend/gateway/client"
// docker output
"github.com/containerd/console"
"github.com/moby/buildkit/util/progress/progressui"
)
const (
defaultBuildkitHost = "docker-container://buildkitd"
bkConfigKey = "context"
bkInputKey = ":dagger:input:"
bkActionKey = ":dagger:action:"
)
type Client struct {
c *bk.Client
inputs map[string]llb.State
localdirs map[string]string
BKFrontend bkgw.BuildFunc
}
func NewClient(ctx context.Context, host string) (*Client, error) {
// buildkit client
if host == "" {
host = os.Getenv("BUILDKIT_HOST")
}
if host == "" {
host = defaultBuildkitHost
}
c, err := bk.New(ctx, host)
if err != nil {
return nil, errors.Wrap(err, "buildkit client")
}
return &Client{
c: c,
inputs: map[string]llb.State{},
localdirs: map[string]string{},
}, nil
}
func (c *Client) ConnectInput(target string, input interface{}) error {
var st llb.State
switch in := input.(type) {
case llb.State:
st = in
case string:
// Generate a random local input label for security
st = c.AddLocalDir(in, target)
default:
return fmt.Errorf("unsupported input type")
}
c.inputs[bkInputKey+target] = st
return nil
}
func (c *Client) AddLocalDir(dir, label string, opts ...llb.LocalOption) llb.State {
c.localdirs[label] = dir
return llb.Local(label, opts...)
}
// Set cue config for future calls.
// input can be:
// - llb.State: valid cue config directory
// - io.Reader: valid cue source
// - string: local path to valid cue file or directory
// - func(llb.State)llb.Stte: modify existing state
func (c *Client) SetConfig(inputs ...interface{}) error {
for _, input := range inputs {
if err := c.setConfig(input); err != nil {
return err
}
}
return nil
}
func (c *Client) setConfig(input interface{}) error {
var st llb.State
switch in := input.(type) {
case llb.State:
st = in
case func(llb.State) llb.State:
// Modify previous state
last, ok := c.inputs[bkConfigKey]
if !ok {
last = llb.Scratch()
}
st = in(last)
case io.Reader:
contents, err := ioutil.ReadAll(in)
if err != nil {
return err
}
st = llb.Scratch().File(llb.Mkfile(
"config.cue",
0660,
contents,
))
// Interpret string as a path (dir or file)
case string:
info, err := os.Stat(in)
if err != nil {
return err
}
if info.IsDir() {
// FIXME: include pattern *.cue ooh yeah
st = c.AddLocalDir(in, "config",
//llb.IncludePatterns([]string{"*.cue", "cue.mod"})),
llb.FollowPaths([]string{"*.cue", "cue.mod"}),
)
} else {
f, err := os.Open(in)
if err != nil {
return err
}
defer f.Close()
return c.SetConfig(f)
}
}
c.inputs[bkConfigKey] = st
return nil
}
func (c *Client) Run(ctx context.Context, action string) (*Output, error) {
// Spawn Build() goroutine
eg, ctx := errgroup.WithContext(ctx)
events := make(chan *bk.SolveStatus)
outr, outw := io.Pipe()
// Spawn build function
eg.Go(c.buildfn(ctx, action, events, outw))
// Spawn print function(s)
dispCtx := context.TODO()
var eventsdup chan *bk.SolveStatus
if os.Getenv("DOCKER_OUTPUT") != "" {
eventsdup = make(chan *bk.SolveStatus)
eg.Go(c.dockerprintfn(dispCtx, eventsdup, os.Stderr))
}
eg.Go(c.printfn(dispCtx, events, eventsdup))
// Retrieve output
out := NewOutput()
eg.Go(c.outputfn(ctx, outr, out))
return out, eg.Wait()
}
func (c *Client) buildfn(ctx context.Context, action string, ch chan *bk.SolveStatus, w io.WriteCloser) func() error {
return func() error {
defer debugf("buildfn complete")
// Setup solve options
opts := bk.SolveOpt{
FrontendAttrs: map[string]string{
bkActionKey: action,
},
LocalDirs: c.localdirs,
FrontendInputs: c.inputs,
// FIXME: catch output & return as cue value
Exports: []bk.ExportEntry{
{
Type: bk.ExporterTar,
Output: func(m map[string]string) (io.WriteCloser, error) {
return w, nil
},
},
},
}
// Setup frontend
bkFrontend := c.BKFrontend
if bkFrontend == nil {
r := &Runtime{}
bkFrontend = r.BKFrontend
}
resp, err := c.c.Build(ctx, opts, "", bkFrontend, ch)
if err != nil {
// Close exporter pipe so that export processor can return
w.Close()
err = errors.New(bkCleanError(err.Error()))
return errors.Wrap(err, "buildkit solve")
}
for k, v := range resp.ExporterResponse {
// FIXME consume exporter response
fmt.Printf("exporter response: %s=%s\n", k, v)
}
return nil
}
}
// Read tar export stream from buildkit Build(), and extract cue output
func (c *Client) outputfn(ctx context.Context, r io.Reader, out *Output) func() error {
return func() error {
defer debugf("outputfn complete")
tr := tar.NewReader(r)
for {
debugf("outputfn: reading next tar entry")
h, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return errors.Wrap(err, "read tar stream")
}
if !strings.HasSuffix(h.Name, ".cue") {
debugf("skipping non-cue file from exporter tar stream: %s", h.Name)
continue
}
debugf("outputfn: compiling & merging %q", h.Name)
// FIXME: only doing this for debug. you can pass tr directly as io.Reader.
contents, err := ioutil.ReadAll(tr)
if err != nil {
return err
}
//if err := out.FillSource(h.Name, tr); err != nil {
if err := out.FillSource(h.Name, contents); err != nil {
debugf("error with %s: contents=\n------\n%s\n-----\n", h.Name, contents)
return errors.Wrap(err, h.Name)
}
debugf("outputfn: DONE: compiling & merging %q", h.Name)
}
return nil
}
}
// Status of a node in the config tree being computed
// Node may be a component, or a value within a component
// (eg. a script or individual operation in a script)
type Node struct {
Path cue.Path
*bk.Vertex
}
func (n Node) ComponentPath() cue.Path {
var parts []cue.Selector
for _, sel := range n.Path.Selectors() {
if strings.HasPrefix(sel.String(), "#") {
break
}
parts = append(parts, sel)
}
return cue.MakePath(parts...)
}
func (n Node) Logf(msg string, args ...interface{}) {
componentPath := n.ComponentPath().String()
args = append([]interface{}{componentPath}, args...)
if msg != "" && !strings.HasSuffix(msg, "\n") {
msg += "\n"
}
fmt.Fprintf(os.Stderr, "[%s] "+msg, args...)
}
func (n Node) LogStream(nStream int, data []byte) {
var stream string
switch nStream {
case 1:
stream = "stdout"
case 2:
stream = "stderr"
default:
stream = fmt.Sprintf("%d", nStream)
}
// FIXME: use bufio reader?
lines := strings.Split(string(data), "\n")
for _, line := range lines {
n.Logf("[%s] %s", stream, line)
}
}
func (n Node) LogError(errmsg string) {
n.Logf("ERROR: %s", bkCleanError(errmsg))
}
func (c *Client) printfn(ctx context.Context, ch, ch2 chan *bk.SolveStatus) func() error {
return func() error {
// Node status mapped to buildkit vertex digest
nodesByDigest := map[string]*Node{}
// Node status mapped to cue path
nodesByPath := map[string]*Node{}
defer debugf("printfn complete")
if ch2 != nil {
defer close(ch2)
}
ticker := time.NewTicker(150 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-ticker.C:
case status, ok := <-ch:
if !ok {
return nil
}
if ch2 != nil {
ch2 <- status
}
debugf("status event: vertexes:%d statuses:%d logs:%d\n",
len(status.Vertexes),
len(status.Statuses),
len(status.Logs),
)
for _, v := range status.Vertexes {
p := cue.ParsePath(v.Name)
if err := p.Err(); err != nil {
debugf("ignoring buildkit vertex %q: not a valid cue path", p.String())
continue
}
n := &Node{
Path: p,
Vertex: v,
}
nodesByPath[n.Path.String()] = n
nodesByDigest[n.Digest.String()] = n
if n.Error != "" {
n.LogError(n.Error)
}
}
for _, log := range status.Logs {
if n, ok := nodesByDigest[log.Vertex.String()]; ok {
n.LogStream(log.Stream, log.Data)
}
}
// debugJSON(status)
// FIXME: callbacks for extracting stream/result
// see proto 67
}
}
return nil
}
}
// A helper to remove noise from buildkit error messages.
// FIXME: Obviously a cleaner solution would be nice.
func bkCleanError(msg string) string {
noise := []string{
"executor failed running ",
"buildkit-runc did not terminate successfully",
"rpc error: code = Unknown desc =",
"failed to solve: ",
}
for _, s := range noise {
msg = strings.Replace(msg, s, "", -1)
}
return msg
}
func (c *Client) dockerprintfn(ctx context.Context, ch chan *bk.SolveStatus, out io.Writer) func() error {
return func() error {
defer debugf("dockerprintfn complete")
var cons console.Console
// FIXME: use smarter writer from blr
return progressui.DisplaySolveStatus(ctx, "", cons, out, ch)
}
}
type Output struct {
r *cue.Runtime
inst *cue.Instance
}
func NewOutput() *Output {
r := &cue.Runtime{}
inst, _ := r.Compile("", "")
return &Output{
r: r,
inst: inst,
}
}
func (o *Output) Print(w io.Writer) error {
v := o.Cue().Value().Eval()
b, err := cueformat.Node(v.Syntax())
if err != nil {
return err
}
_, err = w.Write(b)
return err
}
func (o *Output) JSON() JSON {
return cueToJSON(o.Cue().Value())
}
func (o *Output) Cue() *cue.Instance {
return o.inst
}
func (o *Output) FillSource(filename string, x interface{}) error {
inst, err := o.r.Compile(filename, x)
if err != nil {
return fmt.Errorf("compile %s: %s", filename, cueerrors.Details(err, nil))
}
if err := o.FillValue(inst.Value()); err != nil {
return fmt.Errorf("merge %s: %s", filename, cueerrors.Details(err, nil))
}
return nil
}
func (o *Output) FillValue(x interface{}) error {
inst, err := o.inst.Fill(x)
if err != nil {
return err
}
if err := inst.Value().Validate(); err != nil {
return err
}
o.inst = inst
return nil
}

158
dagger/gen.go Normal file
View File

@ -0,0 +1,158 @@
package dagger
// Generated by gen.sh. DO NOT EDIT.
var DaggerSpec = `
package dagger
// A DAG is the basic unit of programming in dagger.
// It is a special kind of program which runs as a pipeline of computing nodes running in parallel,
// instead of a sequence of operations to be run by a single node.
//
// It is a powerful way to automate various parts of an application delivery workflow:
// build, test, deploy, generate configuration, enforce policies, publish artifacts, etc.
//
// The DAG architecture has many benefits:
// - Because DAGs are made of nodes executing in parallel, they are easy to scale.
// - Because all inputs and outputs are snapshotted and content-addressed, DAGs
// can easily be made repeatable, can be cached aggressively, and can be replayed
// at will.
// - Because nodes are executed by the same container engine as docker-build, DAGs
// can be developed using any language or technology capable of running in a docker.
// Dockerfiles and docker images are natively supported for maximum compatibility.
//
// - Because DAGs are programmed declaratively with a powerful configuration language,
// they are much easier to test, debug and refactor than traditional programming languages.
//
// To execute a DAG, the dagger runtime JIT-compiles it to a low-level format called
// llb, and executes it with buildkit.
// Think of buildkit as a specialized VM for running compute graphs; and dagger as
// a complete programming environment for that VM.
//
// The tradeoff for all those wonderful features is that a DAG architecture cannot be used
// for all software: only software than can be run as a pipeline.
//
// A dagger component is a configuration value augmented
// by scripts defining how to compute it, present it to a user,
// encrypt it, etc.
// FIXME: #Component will not match embedded scalars.
// use Runtime.isComponent() for a reliable check
#Component: {
#dagger: #ComponentConfig
...
}
// The contents of a #dagger annotation
#ComponentConfig: {
input?: bool
// script to compute the value
compute?: #Script
terminal?: {
// Display a message when opening a terminal session
greeting?: string
command: [string]: #Script
}
// Configure how the component is incorporated to user settings.
// Configure how the end-user can configure this component
settings?: {
// If not specified, scrape from comments
title?: string
description?: string
// Disable user input, even if incomplete?
hidden: true | *false
ui: _ // insert here something which can be compiled to react-jsonschema-form
// Show the cue default value to the user, as a default input value?
showDefault: true | *false
// Insert information needed by:
// 1) clients to encrypt
// ie. web wizard, cli
// 2) middleware to implement deicphering in the cuellb pipeline
// eg. integration with clcoud KMS, Vault...
//
// 3) connectors to make sure secrets are preserved
encrypt?: {
pubkey: string
cipher: string
}
}
}
// Any component can be referenced as a directory, since
// every dagger script outputs a filesystem state (aka a directory)
#Dir: #Component
#Script: [...#Op]
// One operation in a script
#Op: #FetchContainer | #FetchGit | #Export | #Exec | #Load | #Copy
// Export a value from fs state to cue
#Export: {
do: "export"
// Source path in the container
source: string
format: "json"|"yaml"|*"string"|"number"|"boolean"
}
#Load: #LoadComponent| #LoadScript
#LoadComponent: {
do: "load"
from: #Component
}
#LoadScript: {
do: "load"
from: #Script
}
#Exec: {
do: "exec"
args: [...string]
env: [string]: string
always: true | *false
dir: string | *"/"
mount?: [string]: #MountTmp | #MountCache | #MountComponent | #MountScript
}
#MountTmp: "tmpfs"
#MountCache: "cache"
#MountComponent: {
input: #Component
path: string | *"/"
}
#MountScript: {
input: #Script
path: string | *"/"
}
#FetchContainer: {
do: "fetch-container"
ref: string
}
#FetchGit: {
do: "fetch-git"
remote: string
ref: string
}
#Copy: {
do: "copy"
from: #Script | #Component
src: string | *"/"
dest: string | *"/"
}
#TestScript: #Script & [
{ do: "fetch-container", ref: "alpine:latest" },
{ do: "exec", args: ["echo", "hello", "world" ] }
]
`

17
dagger/gen.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
set -e
cue eval spec.cue >/dev/null
(
cat <<'EOF'
package dagger
// Generated by gen.sh. DO NOT EDIT.
var DaggerSpec = `
EOF
cat spec.cue
cat <<'EOF'
`
EOF
) > gen.go

326
dagger/job.go Normal file
View File

@ -0,0 +1,326 @@
package dagger
import (
"context"
"fmt"
"path"
"path/filepath"
"strings"
"cuelang.org/go/cue"
cueerrors "cuelang.org/go/cue/errors"
cueload "cuelang.org/go/cue/load"
cueflow "cuelang.org/go/tools/flow"
"github.com/moby/buildkit/client/llb"
bkgw "github.com/moby/buildkit/frontend/gateway/client"
"github.com/pkg/errors"
)
type Solver interface {
Solve(context.Context, llb.State) (bkgw.Reference, error)
}
// 1 buildkit build = 1 job
type Job struct {
c bkgw.Client
// needed for cue operations
r *Runtime
}
// Execute and wrap the result in a buildkit result
func (job Job) BKExecute(ctx context.Context) (_r *bkgw.Result, _e error) {
debugf("Executing bk frontend")
// wrap errors to avoid crashing buildkit with cue error types (why??)
defer func() {
if _e != nil {
_e = fmt.Errorf("%s", cueerrors.Details(_e, nil))
debugf("execute returned an error. Wrapping...")
}
}()
out, err := job.Execute(ctx)
if err != nil {
return nil, err
}
// encode job output to buildkit result
debugf("[runtime] job executed. Encoding output")
// FIXME: we can't serialize result to standalone cue (with no imports).
// So the client cannot safely compile output without access to the same cue.mod
// as the runtime (which we don't want).
// So for now we return the output as json, still parsed as cue on the client
// to keep our options open. Once there is a "tree shake" primitive, we can
// use that to return cue.
//
// Uncomment to return actual cue:
// ----
// outbytes, err := cueformat.Node(out.Value().Eval().Syntax())
// if err != nil {
// return nil, err
// }
// ----
outbytes := cueToJSON(out.Value())
debugf("[runtime] output encoded. Writing output to exporter")
outref, err := job.Solve(ctx,
llb.Scratch().File(llb.Mkfile("computed.cue", 0600, outbytes)),
)
if err != nil {
return nil, err
}
debugf("[runtime] output written to exporter. returning to buildkit solver")
res := bkgw.NewResult()
res.SetRef(outref)
return res, nil
}
func (job Job) Execute(ctx context.Context) (_i *cue.Instance, _e error) {
debugf("[runtime] Execute()")
defer func() { debugf("[runtime] DONE Execute(): err=%v", _e) }()
state, err := job.Config(ctx)
if err != nil {
return nil, err
}
// Merge input information into the cue config
inputs, err := job.Inputs(ctx)
if err != nil {
return nil, err
}
for target := range inputs {
// FIXME: cleaner code generation, missing cue.Value.FillPath
state, err = job.r.fill(state, `#dagger: input: true`, target)
if err != nil {
return nil, errors.Wrapf(err, "connect input %q", target)
}
}
action := job.Action()
switch action {
case "compute":
return job.doCompute(ctx, state)
case "export":
return job.doExport(ctx, state)
default:
return job.doExport(ctx, state)
}
}
func (job Job) doExport(ctx context.Context, state *cue.Instance) (*cue.Instance, error) {
return state, nil
}
func (job Job) doCompute(ctx context.Context, state *cue.Instance) (*cue.Instance, error) {
out, err := job.r.Compile("computed.cue", "")
if err != nil {
return nil, err
}
// Setup cueflow
debugf("Setting up cueflow")
flow := cueflow.New(
&cueflow.Config{
UpdateFunc: func(c *cueflow.Controller, t *cueflow.Task) error {
debugf("cueflow event")
if t == nil {
return nil
}
debugf("cueflow task %q: %s", t.Path().String(), t.State().String())
if t.State() == cueflow.Terminated {
debugf("cueflow task %q: filling result", t.Path().String())
out, err = out.Fill(t.Value(), cuePathToStrings(t.Path())...)
if err != nil {
return err
}
// FIXME: catch merge errors early with state
}
return nil
},
},
state,
// Task match func
func(v cue.Value) (cueflow.Runner, error) {
// Is v a component (has #dagger) with a field 'compute' ?
isComponent, err := job.r.isComponent(v, "compute")
if err != nil {
return nil, err
}
if !isComponent {
return nil, nil
}
debugf("[%s] component detected\n", v.Path().String())
// task runner func
runner := cueflow.RunnerFunc(func(t *cueflow.Task) error {
computeScript := t.Value().LookupPath(cue.ParsePath("#dagger.compute"))
script, err := job.newScript(computeScript)
if err != nil {
return err
}
// Run the script & fill the result into the task
return script.Run(ctx, t)
})
return runner, nil
},
)
debugf("Running cueflow")
if err := flow.Run(ctx); err != nil {
return nil, err
}
debugf("Completed cueflow run. Merging result.")
state, err = state.Fill(out)
if err != nil {
return nil, err
}
debugf("Result merged")
// Return only the computed values
return out, nil
}
func (job Job) bk() bkgw.Client {
return job.c
}
func (job Job) Action() string {
opts := job.bk().BuildOpts().Opts
if action, ok := opts[bkActionKey]; ok {
return action
}
return ""
}
// Load the cue config for this job
// (received as llb input)
func (job Job) Config(ctx context.Context) (*cue.Instance, error) {
src := llb.Local(bkConfigKey,
llb.SessionID(job.bk().BuildOpts().SessionID),
llb.SharedKeyHint(bkConfigKey),
llb.WithCustomName("load config"),
)
bkInputs, err := job.bk().Inputs(ctx)
if err != nil {
return nil, err
}
if st, ok := bkInputs[bkConfigKey]; ok {
src = st
}
// job.runDebug(ctx, src, "ls", "-la", "/mnt")
return job.LoadCue(ctx, src)
}
func (job Job) runDebug(ctx context.Context, mnt llb.State, args ...string) error {
opts := []llb.RunOption{
llb.Args(args),
llb.AddMount("/mnt", mnt),
}
cmd := llb.Image("alpine").Run(opts...).Root()
ref, err := job.Solve(ctx, cmd)
if err != nil {
return errors.Wrap(err, "debug")
}
// force non-lazy solve
if _, err := ref.ReadDir(ctx, bkgw.ReadDirRequest{Path: "/"}); err != nil {
return errors.Wrap(err, "debug")
}
return nil
}
func (job Job) Inputs(ctx context.Context) (map[string]llb.State, error) {
bkInputs, err := job.bk().Inputs(ctx)
if err != nil {
return nil, err
}
inputs := map[string]llb.State{}
for key, input := range bkInputs {
if !strings.HasPrefix(key, bkInputKey) {
continue
}
target := strings.Replace(key, bkInputKey, "", 1)
targetPath := cue.ParsePath(target)
if err := targetPath.Err(); err != nil {
return nil, errors.Wrapf(err, "input target %q", target)
}
// FIXME: check that the path can be passed to Fill
// (eg. only regular fields, no array indexes, no defs)
// see cuePathToStrings
inputs[target] = input
}
return inputs, nil
}
// loadFiles recursively loads all .cue files from a buildkit gateway
// FIXME: this is highly inefficient.
func loadFiles(ctx context.Context, ref bkgw.Reference, p, overlayPrefix string, overlay map[string]cueload.Source) error {
// FIXME: we cannot use `IncludePattern` here, otherwise sub directories
// (e.g. "cue.mod") will be skipped.
files, err := ref.ReadDir(ctx, bkgw.ReadDirRequest{
Path: p,
})
if err != nil {
return err
}
for _, f := range files {
fPath := path.Join(p, f.GetPath())
if f.IsDir() {
if err := loadFiles(ctx, ref, fPath, overlayPrefix, overlay); err != nil {
return err
}
continue
}
if filepath.Ext(fPath) != ".cue" {
continue
}
contents, err := ref.ReadFile(ctx, bkgw.ReadRequest{
Filename: fPath,
})
if err != nil {
return errors.Wrap(err, f.GetPath())
}
overlay[path.Join(overlayPrefix, fPath)] = cueload.FromBytes(contents)
}
return nil
}
func (job Job) LoadCue(ctx context.Context, st llb.State, args ...string) (*cue.Instance, error) {
// The CUE overlay needs to be prefixed by a non-conflicting path with the
// local filesystem, otherwise Cue will merge the Overlay with whatever Cue
// files it finds locally.
const overlayPrefix = "/config"
buildConfig := &cueload.Config{
Dir: overlayPrefix,
Overlay: map[string]cueload.Source{},
}
buildArgs := args
// Inject cue files from llb state into overlay
ref, err := job.Solve(ctx, st)
if err != nil {
return nil, err
}
if err := loadFiles(ctx, ref, ".", overlayPrefix, buildConfig.Overlay); err != nil {
return nil, err
}
instances := cueload.Instances(buildArgs, buildConfig)
if len(instances) != 1 {
return nil, errors.New("only one package is supported at a time")
}
inst, err := job.r.Build(instances[0])
if err != nil {
return nil, cueErr(err)
}
return inst, nil
}
func (job Job) Solve(ctx context.Context, st llb.State) (bkgw.Reference, error) {
// marshal llb
def, err := st.Marshal(ctx, llb.LinuxAmd64)
if err != nil {
return nil, err
}
// call solve
res, err := job.bk().Solve(ctx, bkgw.SolveRequest{Definition: def.ToPB()})
if err != nil {
return nil, err
}
// always use single reference (ignore multiple outputs & metadata)
return res.SingleRef()
}

138
dagger/json.go Normal file
View File

@ -0,0 +1,138 @@
package dagger
import (
"fmt"
"cuelang.org/go/cue"
cuejson "cuelang.org/go/encoding/json"
"github.com/KromDaniel/jonson"
"github.com/pkg/errors"
)
type JSON []byte
func (s JSON) Get(path ...string) ([]byte, error) {
if s == nil {
s = []byte("{}")
}
var (
root *jonson.JSON
)
root, err := jonson.Parse(s)
if err != nil {
return nil, errors.Wrap(err, "parse root json")
}
pointer := root
for _, key := range path {
// FIXME: we can traverse maps but not arrays (need to handle int keys)
pointer = pointer.At(key)
}
// FIXME: use indent function from stdlib
return pointer.ToJSON()
}
func (s JSON) Unset(path ...string) (JSON, error) {
if s == nil {
s = []byte("{}")
}
var (
root *jonson.JSON
)
root, err := jonson.Parse(s)
if err != nil {
return nil, errors.Wrap(err, "unset: parse root json")
}
var (
pointer = root
pathDir []string
)
if len(path) > 0 {
pathDir = path[:len(path)-1]
}
for _, key := range pathDir {
pointer = pointer.At(key)
}
if len(path) == 0 {
pointer.Set(nil)
} else {
key := path[len(path)-1]
pointer.DeleteMapKey(key)
}
return root.ToJSON()
}
func (s JSON) Set(valueJSON []byte, path ...string) (JSON, error) {
if s == nil {
s = []byte("{}")
}
var (
root *jonson.JSON
value *jonson.JSON
)
root, err := jonson.Parse(s)
if err != nil {
return nil, errors.Wrap(err, "parse root json")
}
value, err = jonson.Parse(valueJSON)
if err != nil {
return nil, errors.Wrapf(err, "SetJSON: parse value json: |%s|", valueJSON)
}
var (
pointer = root
pathDir []string
)
if len(path) > 0 {
pathDir = path[:len(path)-1]
}
for _, key := range pathDir {
if !pointer.ObjectKeyExists(key) {
pointer.MapSet(key, jonson.NewEmptyJSONMap())
}
pointer = pointer.At(key)
}
if len(path) == 0 {
pointer.Set(value)
} else {
key := path[len(path)-1]
pointer.MapSet(key, value)
}
return root.ToJSON()
}
func (s JSON) Merge(layers ...JSON) (JSON, error) {
r := new(cue.Runtime)
var resultInst *cue.Instance
for i, l := range append([]JSON{s}, layers...) {
if l == nil {
continue
}
filename := fmt.Sprintf("%d", i)
inst, err := cuejson.Decode(r, filename, []byte(l))
if err != nil {
return nil, err
}
if resultInst == nil {
resultInst = inst
} else {
resultInst, err = resultInst.Fill(inst.Value())
if err != nil {
return nil, err
}
if resultInst.Err != nil {
return nil, resultInst.Err
}
}
}
b, err := resultInst.Value().MarshalJSON()
if err != nil {
return nil, err
}
return JSON(b), nil
}
func (s JSON) String() string {
if s == nil {
return "{}"
}
return string(s)
}

109
dagger/runtime.go Normal file
View File

@ -0,0 +1,109 @@
//go:generate sh gen.sh
package dagger
import (
"context"
"fmt"
"sync"
"cuelang.org/go/cue"
cueerrors "cuelang.org/go/cue/errors"
bkgw "github.com/moby/buildkit/frontend/gateway/client"
"github.com/pkg/errors"
)
type Runtime struct {
l sync.Mutex
cue.Runtime
}
func (r *Runtime) Cue() *cue.Runtime {
return &(r.Runtime)
}
func (r *Runtime) fill(inst *cue.Instance, v interface{}, target string) (*cue.Instance, error) {
targetPath := cue.ParsePath(target)
if err := targetPath.Err(); err != nil {
return nil, err
}
p := cuePathToStrings(targetPath)
if src, ok := v.(string); ok {
vinst, err := r.Compile(target, src)
if err != nil {
return nil, err
}
return inst.Fill(vinst.Value(), p...)
}
return inst.Fill(v, p...)
}
// func (r Runtime) Run(...)
// Buildkit run entrypoint
func (r *Runtime) BKFrontend(ctx context.Context, c bkgw.Client) (*bkgw.Result, error) {
return r.newJob(c).BKExecute(ctx)
}
func (r *Runtime) newJob(c bkgw.Client) Job {
return Job{
r: r,
c: c,
}
}
// Check whether a value is a valid component
// FIXME: calling matchSpec("#Component") is not enough because
// it does not match embedded scalars.
func (r *Runtime) isComponent(v cue.Value, fields ...string) (bool, error) {
cfg := v.LookupPath(cue.ParsePath("#dagger"))
if cfg.Err() != nil {
// No "#dagger" -> not a component
return false, nil
}
for _, field := range fields {
if cfg.Lookup(field).Err() != nil {
return false, nil
}
}
if err := r.validateSpec(cfg, "#ComponentConfig"); err != nil {
return true, errors.Wrap(err, "invalid #dagger")
}
return true, nil
}
// eg. validateSpec(op, "#Op")
// eg. validateSpec(dag, "#DAG")
func (r *Runtime) validateSpec(v cue.Value, defpath string) (err error) {
// Expand cue errors to get full details
// FIXME: there is probably a cleaner way to do this.
defer func() {
if err != nil {
err = fmt.Errorf("%s", cueerrors.Details(err, nil))
}
}()
r.l.Lock()
defer r.l.Unlock()
// FIXME cache spec instance
spec, err := r.Compile("dagger.cue", DaggerSpec)
if err != nil {
panic("invalid spec")
}
def := spec.Value().LookupPath(cue.ParsePath(defpath))
if err := def.Err(); err != nil {
return err
}
v = v.Eval()
if err := v.Validate(); err != nil {
return err
}
res := def.Unify(v)
if err := res.Validate(cue.Final()); err != nil {
return err
}
return nil
}
func (r *Runtime) matchSpec(v cue.Value, def string) bool {
return r.validateSpec(v, def) == nil
}

326
dagger/script.go Normal file
View File

@ -0,0 +1,326 @@
package dagger
import (
"context"
"encoding/json"
"fmt"
"cuelang.org/go/cue"
"github.com/moby/buildkit/client/llb"
"github.com/pkg/errors"
)
type Script struct {
v cue.Value
job Job
// current state
state *State
}
func (job Job) newScript(v cue.Value) (*Script, error) {
s := &Script{
v: v,
job: job,
state: NewState(job),
}
if err := s.Validate(); err != nil {
return nil, s.err(err, "invalid script")
}
return s, nil
}
type Action func(context.Context, cue.Value, Fillable) error
func (s *Script) Run(ctx context.Context, out Fillable) error {
op, err := s.Cue().List()
if err != nil {
return s.err(err, "run")
}
i := 0
for op.Next() {
// If op is not concrete, interrupt execution without error.
// This allows gradual resolution: compute what you can compute.. leave the rest incomplete.
if !cueIsConcrete(op.Value()) {
debugf("%s: non-concrete op. Leaving script unfinished", op.Value().Path().String())
return nil
}
if err := s.Do(ctx, op.Value(), out); err != nil {
return s.err(err, "run op %d", i+1)
}
i += 1
}
return nil
}
func (s *Script) Do(ctx context.Context, op cue.Value, out Fillable) error {
// Skip no-ops without error (allows more flexible use of if())
// FIXME: maybe not needed once a clear pattern is established for
// how to use if() in a script?
if cueIsEmptyStruct(op) {
return nil
}
actions := map[string]Action{
// "#Copy": s.copy,
"#Exec": s.exec,
"#Export": s.export,
"#FetchContainer": s.fetchContainer,
"#FetchGit": s.fetchGit,
"#Load": s.load,
"#Copy": s.copy,
}
for def, action := range actions {
if s.matchSpec(op, def) {
debugf("OP MATCH: %s: %s: %v", def, op.Path().String(), op)
return action(ctx, op, out)
}
}
return fmt.Errorf("[%s] invalid operation: %s", s.v.Path().String(), cueToJSON(op))
}
func (s *Script) copy(ctx context.Context, v cue.Value, out Fillable) error {
// Decode copy options
var op struct {
Src string
Dest string
}
if err := v.Decode(&op); err != nil {
return err
}
from := v.Lookup("from")
if isComponent, err := s.job.r.isComponent(from); err != nil {
return err
} else if isComponent {
return s.copyComponent(ctx, from, op.Src, op.Dest)
}
if s.matchSpec(from, "#Script") {
return s.copyScript(ctx, from, op.Src, op.Dest)
}
return fmt.Errorf("copy: invalid source")
}
func (s *Script) copyScript(ctx context.Context, from cue.Value, src, dest string) error {
// Load source script
fromScript, err := s.job.newScript(from)
if err != nil {
return err
}
// Execute source script
if err := fromScript.Run(ctx, Discard()); err != nil {
return err
}
return s.State().Change(ctx, func(st llb.State) llb.State {
return st.File(llb.Copy(
fromScript.State().LLB(),
src,
dest,
// FIXME: allow more configurable llb options
// For now we define the following convenience presets:
&llb.CopyInfo{
CopyDirContentsOnly: true,
CreateDestPath: true,
AllowWildcard: true,
},
))
})
}
func (s *Script) copyComponent(ctx context.Context, from cue.Value, src, dest string) error {
return s.copyScript(ctx, from.LookupPath(cue.ParsePath("#dagger.compute")), src, dest)
}
func (s *Script) load(ctx context.Context, op cue.Value, out Fillable) error {
from := op.Lookup("from")
isComponent, err := s.job.r.isComponent(from)
if err != nil {
return err
}
if isComponent {
debugf("LOAD: from is a component")
return s.loadScript(ctx, from.LookupPath(cue.ParsePath("#dagger.compute")))
}
if s.matchSpec(from, "#Script") {
return s.loadScript(ctx, from)
}
return fmt.Errorf("load: invalid source")
}
func (s *Script) loadScript(ctx context.Context, v cue.Value) error {
from, err := s.job.newScript(v)
if err != nil {
return errors.Wrap(err, "load")
}
// NOTE we discard cue outputs from running the loaded script.
// This means we load the LLB state but NOT the cue exports.
// In other words: cue exports are always private to their original location.
if err := from.Run(ctx, Discard()); err != nil {
return errors.Wrap(err, "load/execute")
}
// overwrite buildkit state from loaded from
s.state = from.state
return nil
}
func (s *Script) exec(ctx context.Context, v cue.Value, out Fillable) error {
var opts []llb.RunOption
var cmd struct {
Args []string
Env map[string]string
Dir string
Always bool
}
v.Decode(&cmd)
// marker for status events
opts = append(opts, llb.WithCustomName(v.Path().String()))
// args
opts = append(opts, llb.Args(cmd.Args))
// dir
dir := cmd.Dir
if dir == "" {
dir = "/"
}
// env
for k, v := range cmd.Env {
opts = append(opts, llb.AddEnv(k, v))
}
// always?
if cmd.Always {
cacheBuster, err := randomID(8)
if err != nil {
return err
}
opts = append(opts, llb.AddEnv("DAGGER_CACHEBUSTER", cacheBuster))
}
// mounts
mnt, _ := v.Lookup("mount").Fields()
for mnt.Next() {
opt, err := s.mount(ctx, mnt.Label(), mnt.Value())
if err != nil {
return err
}
opts = append(opts, opt)
}
// --> Execute
return s.State().Change(ctx, func(st llb.State) llb.State {
return st.Run(opts...).Root()
})
}
func (s *Script) mount(ctx context.Context, dest string, source cue.Value) (llb.RunOption, error) {
if s.matchSpec(source, "#MountTmp") {
return llb.AddMount(dest, llb.Scratch(), llb.Tmpfs()), nil
}
if s.matchSpec(source, "#MountCache") {
// FIXME: cache mount
return nil, fmt.Errorf("FIXME: cache mount not yet implemented")
}
if s.matchSpec(source, "#MountScript") {
return s.mountScript(ctx, dest, source)
}
if s.matchSpec(source, "#MountComponent") {
return s.mountComponent(ctx, dest, source)
}
return nil, fmt.Errorf("mount %s to %s: invalid source", source.Path().String(), dest)
}
// mount when the input is a script (see mountComponent, mountTmpfs, mountCache)
func (s *Script) mountScript(ctx context.Context, dest string, source cue.Value) (llb.RunOption, error) {
script, err := s.job.newScript(source)
if err != nil {
return nil, err
}
// FIXME: this is where we re-run everything,
// and rely on solver cache / dedup
if err := script.Run(ctx, Discard()); err != nil {
return nil, err
}
return llb.AddMount(dest, script.State().LLB()), nil
}
func (s *Script) mountComponent(ctx context.Context, dest string, source cue.Value) (llb.RunOption, error) {
return s.mountScript(ctx, dest, source.LookupPath(cue.ParsePath("from.#dagger.compute")))
}
func (s *Script) fetchContainer(ctx context.Context, v cue.Value, out Fillable) error {
var op struct {
Ref string
}
if err := v.Decode(&op); err != nil {
return errors.Wrap(err, "decode fetch-container")
}
return s.State().Change(ctx, llb.Image(op.Ref))
}
func (s *Script) fetchGit(ctx context.Context, v cue.Value, out Fillable) error {
// See #FetchGit in spec.cue
var op struct {
Remote string
Ref string
}
if err := v.Decode(&op); err != nil {
return errors.Wrap(err, "decode fetch-git")
}
return s.State().Change(ctx, llb.Git(op.Remote, op.Ref))
}
func (s *Script) export(ctx context.Context, v cue.Value, out Fillable) error {
// See #Export in spec.cue
var op struct {
Source string
// FIXME: target
// Target string
Format string
}
v.Decode(&op)
b, err := s.State().ReadFile(ctx, op.Source)
if err != nil {
return err
}
switch op.Format {
case "string":
return out.Fill(string(b))
case "json":
var o interface{}
if err := json.Unmarshal(b, &o); err != nil {
return err
}
return out.Fill(o)
default:
return fmt.Errorf("unsupported export format: %q", op.Format)
}
}
func (s *Script) Cue() cue.Value {
return s.v
}
func (s *Script) Location() string {
return s.Cue().Path().String()
}
func (s *Script) err(e error, msg string, args ...interface{}) error {
return errors.Wrapf(e, s.Location()+": "+msg, args...)
}
func (s *Script) Validate() error {
return s.job.r.validateSpec(s.Cue(), "#Script")
}
func (s *Script) State() *State {
return s.state
}
func (s *Script) matchSpec(v cue.Value, def string) bool {
// FIXME: we manually filter out empty structs to avoid false positives
// This is necessary because Runtime.ValidateSpec has a bug
// where an empty struct matches everything.
// see https://github.com/cuelang/cue/issues/566#issuecomment-749735878
// Once the bug is fixed, the manual check can be removed.
if st, err := v.Struct(); err == nil {
if st.Len() == 0 {
debugf("FIXME: manually filtering out empty struct from spec match")
return false
}
}
return s.job.r.matchSpec(v, def)
}

152
dagger/spec.cue Normal file
View File

@ -0,0 +1,152 @@
package dagger
// A DAG is the basic unit of programming in dagger.
// It is a special kind of program which runs as a pipeline of computing nodes running in parallel,
// instead of a sequence of operations to be run by a single node.
//
// It is a powerful way to automate various parts of an application delivery workflow:
// build, test, deploy, generate configuration, enforce policies, publish artifacts, etc.
//
// The DAG architecture has many benefits:
// - Because DAGs are made of nodes executing in parallel, they are easy to scale.
// - Because all inputs and outputs are snapshotted and content-addressed, DAGs
// can easily be made repeatable, can be cached aggressively, and can be replayed
// at will.
// - Because nodes are executed by the same container engine as docker-build, DAGs
// can be developed using any language or technology capable of running in a docker.
// Dockerfiles and docker images are natively supported for maximum compatibility.
//
// - Because DAGs are programmed declaratively with a powerful configuration language,
// they are much easier to test, debug and refactor than traditional programming languages.
//
// To execute a DAG, the dagger runtime JIT-compiles it to a low-level format called
// llb, and executes it with buildkit.
// Think of buildkit as a specialized VM for running compute graphs; and dagger as
// a complete programming environment for that VM.
//
// The tradeoff for all those wonderful features is that a DAG architecture cannot be used
// for all software: only software than can be run as a pipeline.
//
// A dagger component is a configuration value augmented
// by scripts defining how to compute it, present it to a user,
// encrypt it, etc.
// FIXME: #Component will not match embedded scalars.
// use Runtime.isComponent() for a reliable check
#Component: {
#dagger: #ComponentConfig
...
}
// The contents of a #dagger annotation
#ComponentConfig: {
input?: bool
// script to compute the value
compute?: #Script
terminal?: {
// Display a message when opening a terminal session
greeting?: string
command: [string]: #Script
}
// Configure how the component is incorporated to user settings.
// Configure how the end-user can configure this component
settings?: {
// If not specified, scrape from comments
title?: string
description?: string
// Disable user input, even if incomplete?
hidden: true | *false
ui: _ // insert here something which can be compiled to react-jsonschema-form
// Show the cue default value to the user, as a default input value?
showDefault: true | *false
// Insert information needed by:
// 1) clients to encrypt
// ie. web wizard, cli
// 2) middleware to implement deicphering in the cuellb pipeline
// eg. integration with clcoud KMS, Vault...
//
// 3) connectors to make sure secrets are preserved
encrypt?: {
pubkey: string
cipher: string
}
}
}
// Any component can be referenced as a directory, since
// every dagger script outputs a filesystem state (aka a directory)
#Dir: #Component
#Script: [...#Op]
// One operation in a script
#Op: #FetchContainer | #FetchGit | #Export | #Exec | #Load | #Copy
// Export a value from fs state to cue
#Export: {
do: "export"
// Source path in the container
source: string
format: "json"|"yaml"|*"string"|"number"|"boolean"
}
#Load: #LoadComponent| #LoadScript
#LoadComponent: {
do: "load"
from: #Component
}
#LoadScript: {
do: "load"
from: #Script
}
#Exec: {
do: "exec"
args: [...string]
env: [string]: string
always: true | *false
dir: string | *"/"
mount?: [string]: #MountTmp | #MountCache | #MountComponent | #MountScript
}
#MountTmp: "tmpfs"
#MountCache: "cache"
#MountComponent: {
input: #Component
path: string | *"/"
}
#MountScript: {
input: #Script
path: string | *"/"
}
#FetchContainer: {
do: "fetch-container"
ref: string
}
#FetchGit: {
do: "fetch-git"
remote: string
ref: string
}
#Copy: {
do: "copy"
from: #Script | #Component
src: string | *"/"
dest: string | *"/"
}
#TestScript: #Script & [
{ do: "fetch-container", ref: "alpine:latest" },
{ do: "exec", args: ["echo", "hello", "world" ] }
]

94
dagger/spec_test.go Normal file
View File

@ -0,0 +1,94 @@
package dagger
import (
"testing"
"cuelang.org/go/cue"
)
func TestMatch(t *testing.T) {
var data = []struct {
Src string
Def string
}{
{
Src: `do: "exec", args: ["echo", "hello"]`,
Def: "#Exec",
},
{
Src: `do: "fetch-git", remote: "github.com/shykes/tests"`,
Def: "#FetchGit",
},
{
Src: `do: "load", from: [{do: "exec", args: ["echo", "hello"]}]`,
Def: "#Load",
},
{
Src: `do: "load", from: #dagger: compute: [{do: "exec", args: ["echo", "hello"]}]`,
Def: "#Load",
},
// Make sure an empty op does NOT match
{
Src: ``,
Def: "",
},
{
Src: `do: "load"
let package={bash:">3.0"}
from: foo
let foo={#dagger: compute: [
{do: "fetch-container", ref: "alpine"},
for pkg, info in package {
if (info & true) != _|_ {
do: "exec"
args: ["echo", "hello", pkg]
}
if (info & string) != _|_ {
do: "exec"
args: ["echo", "hello", pkg, info]
}
}
]}
`,
Def: "#Load",
},
}
for _, d := range data {
testMatch(t, d.Src, d.Def)
}
}
// Test an example op for false positives and negatives
func testMatch(t *testing.T, src interface{}, def string) {
r := &Runtime{}
op := compile(t, r, src)
if def != "" {
if !r.matchSpec(op, def) {
t.Errorf("false negative: %s: %q", def, src)
}
}
for _, cmpDef := range []string{
"#Exec",
"#FetchGit",
"#FetchContainer",
"#Export",
"#Load",
"#Copy",
} {
if cmpDef == def {
continue
}
if r.matchSpec(op, cmpDef) {
t.Errorf("false positive: %s: %q", cmpDef, src)
}
}
return
}
func compile(t *testing.T, r *Runtime, src interface{}) cue.Value {
inst, err := r.Compile("", src)
if err != nil {
t.Fatal(err)
}
return inst.Value()
}

53
dagger/state.go Normal file
View File

@ -0,0 +1,53 @@
package dagger
import (
"context"
"os"
"github.com/moby/buildkit/client/llb"
bkgw "github.com/moby/buildkit/frontend/gateway/client"
)
type State struct {
// Before last solve
input llb.State
// After last solve
output bkgw.Reference
// How to produce the output
s Solver
}
func NewState(s Solver) *State {
return &State{
input: llb.Scratch(),
s: s,
}
}
func (s *State) ReadFile(ctx context.Context, filename string) ([]byte, error) {
if s.output == nil {
return nil, os.ErrNotExist
}
return s.output.ReadFile(ctx, bkgw.ReadRequest{Filename: filename})
}
func (s *State) Change(ctx context.Context, op interface{}) error {
input := s.input
switch OP := op.(type) {
case llb.State:
input = OP
case func(llb.State) llb.State:
input = OP(input)
}
output, err := s.s.Solve(ctx, input)
if err != nil {
return err
}
s.input = input
s.output = output
return nil
}
func (s *State) LLB() llb.State {
return s.input
}

26
dagger/ui/ui.go Normal file
View File

@ -0,0 +1,26 @@
package ui
import (
"fmt"
"os"
"strings"
)
func Fatalf(msg string, args ...interface{}) {
if !strings.HasSuffix(msg, "\n") {
msg = msg + "\n"
}
fmt.Fprintf(os.Stderr, msg, args...)
os.Exit(1)
}
func Fatal(msg interface{}) {
Fatalf("%s\n", msg)
}
func Info(msg string, args ...interface{}) {
if !strings.HasSuffix(msg, "\n") {
msg = msg + "\n"
}
fmt.Fprintf(os.Stderr, "[info] "+msg, args...)
}

288
dagger/utils.go Normal file
View File

@ -0,0 +1,288 @@
package dagger
import (
"crypto/rand"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"cuelang.org/go/cue"
cueAst "cuelang.org/go/cue/ast"
cueerrors "cuelang.org/go/cue/errors"
cueformat "cuelang.org/go/cue/format"
cueload "cuelang.org/go/cue/load"
cueParser "cuelang.org/go/cue/parser"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/client/llb/imagemetaresolver"
"github.com/pkg/errors"
)
// A nil equivalent for cue.Value (when returning errors)
var qnil cue.Value
type Fillable interface {
Fill(interface{}) error
}
func Discard() Fillable {
return discard{}
}
type discard struct{}
func (d discard) Fill(x interface{}) error {
return nil
}
type fillableValue struct {
root cue.Value
}
func cuePrint(v cue.Value) (string, error) {
b, err := cueformat.Node(v.Syntax())
if err != nil {
return "", err
}
return string(b), nil
}
func (f *fillableValue) Fill(v interface{}) error {
root2 := f.root.Fill(v)
if err := root2.Err(); err != nil {
return err
}
f.root = root2
return nil
}
func cueScratch(r *cue.Runtime) Fillable {
f := &fillableValue{}
if inst, err := r.Compile("", ""); err == nil {
f.root = inst.Value()
}
return f
}
func cueErr(err error) error {
return fmt.Errorf("%s", cueerrors.Details(err, &cueerrors.Config{}))
}
func cueDecodeArray(a cue.Value, idx int, out interface{}) {
a.LookupPath(cue.MakePath(cue.Index(idx))).Decode(out)
}
func cueToJSON(v cue.Value) JSON {
var out JSON
v.Walk(
func(v cue.Value) bool {
b, err := v.MarshalJSON()
if err == nil {
newOut, err := out.Set(b, cuePathToStrings(v.Path())...)
if err == nil {
out = newOut
}
return false
}
return true
},
nil,
)
return out
}
// Build a cue instance from a directory and args
func cueBuild(r *cue.Runtime, cueRoot string, buildArgs ...string) (*cue.Instance, error) {
var err error
cueRoot, err = filepath.Abs(cueRoot)
if err != nil {
return nil, err
}
buildConfig := &cueload.Config{
ModuleRoot: cueRoot,
Dir: cueRoot,
}
instances := cueload.Instances(buildArgs, buildConfig)
if len(instances) != 1 {
return nil, errors.New("only one package is supported at a time")
}
return r.Build(instances[0])
}
func debugJSON(v interface{}) {
if os.Getenv("DEBUG") != "" {
e := json.NewEncoder(os.Stderr)
e.SetIndent("", " ")
e.Encode(v)
}
}
func debugf(msg string, args ...interface{}) {
if !strings.HasSuffix(msg, "\n") {
msg = msg + "\n"
}
if os.Getenv("DEBUG") != "" {
fmt.Fprintf(os.Stderr, msg, args...)
}
}
func debug(msg string) {
if os.Getenv("DEBUG") != "" {
fmt.Fprintln(os.Stderr, msg)
}
}
func randomID(size int) (string, error) {
b := make([]byte, size)
_, err := rand.Read(b)
if err != nil {
return "", err
}
return fmt.Sprintf("%x", b), nil
}
func cueWrapExpr(p string, v cueAst.Expr) (cueAst.Expr, error) {
pExpr, err := cueParser.ParseExpr("path", p)
if err != nil {
return v, err
}
out := v
cursor := pExpr
walk:
for {
switch c := cursor.(type) {
case *cueAst.SelectorExpr:
out = cueAst.NewStruct(
&cueAst.Field{
Value: out,
Label: c.Sel,
},
)
cursor = c.X
case *cueAst.Ident:
out = cueAst.NewStruct(
&cueAst.Field{
Value: out,
Label: c,
},
)
break walk
default:
return out, fmt.Errorf("invalid path expression: %q", p)
}
}
return out, nil
}
func cueWrapFile(p string, v interface{}) (*cueAst.File, error) {
f, err := cueParser.ParseFile("value", v)
if err != nil {
return f, err
}
decls := make([]cueAst.Decl, 0, len(f.Decls))
for _, decl := range f.Decls {
switch d := decl.(type) {
case *cueAst.Field:
wrappedExpr, err := cueWrapExpr(p, cueAst.NewStruct(d))
if err != nil {
return f, err
}
decls = append(decls, &cueAst.EmbedDecl{Expr: wrappedExpr})
case *cueAst.EmbedDecl:
wrappedExpr, err := cueWrapExpr(p, d.Expr)
if err != nil {
return f, err
}
d.Expr = wrappedExpr
decls = append(decls, d)
case *cueAst.ImportDecl:
decls = append(decls, decl)
default:
fmt.Printf("skipping unsupported decl type %#v\n\n", decl)
continue
}
}
f.Decls = decls
return f, nil
}
func cueIsEmptyStruct(v cue.Value) bool {
if st, err := v.Struct(); err == nil {
if st.Len() == 0 {
return true
}
}
return false
}
// Return false if v is not concrete, or contains any
// non-concrete fields or items.
func cueIsConcrete(v cue.Value) bool {
// FIXME: use Value.Walk?
if it, err := v.Fields(); err == nil {
for it.Next() {
if !cueIsConcrete(it.Value()) {
return false
}
}
return true
}
if it, err := v.List(); err == nil {
for it.Next() {
if !cueIsConcrete(it.Value()) {
return false
}
}
return true
}
dv, _ := v.Default()
return v.IsConcrete() || dv.IsConcrete()
}
// LLB Helper to pull a Docker image + all its metadata
func llbDockerImage(ref string) llb.State {
return llb.Image(
ref,
llb.WithMetaResolver(imagemetaresolver.Default()),
)
}
func cueStringsToCuePath(parts ...string) cue.Path {
selectors := make([]cue.Selector, 0, len(parts))
for _, part := range parts {
selectors = append(selectors, cue.Str(part))
}
return cue.MakePath(selectors...)
}
func cuePathToStrings(p cue.Path) []string {
selectors := p.Selectors()
out := make([]string, len(selectors))
for i, sel := range selectors {
out[i] = sel.String()
}
return out
}
// Validate a cue path, and return a canonical version
func cueCleanPath(p string) (string, error) {
cp := cue.ParsePath(p)
return cp.String(), cp.Err()
}
func autoMarshal(value interface{}) ([]byte, error) {
switch v := value.(type) {
case []byte:
return v, nil
case string:
return []byte(v), nil
case io.Reader:
return ioutil.ReadAll(v)
default:
return nil, fmt.Errorf("unsupported marshal inoput type")
}
return []byte(fmt.Sprintf("%v", value)), nil
}

View File

@ -0,0 +1,23 @@
# ACME platform
Welcome to the acme-platform repository. It contains everything you need to start developing and shipping improvements
to the ACME Clothing Store.
For information or support, contact the ACME Platform team: platform@acme.infralabs.io
# Things you can do with ACME platform
## Pre-merge or post-merge QA
## Create a personal dev environment
## Cross-team integration testing
## Sales demos
## End-to-end product reviews
## Testing infrastructure changes
## Deploying to production (REQUIRES SPECIAL PRIVILEGES, talk to your SRE)

View File

@ -0,0 +1,38 @@
// ACME platform: everything you need to develop and ship improvements to
// the ACME clothing store.
package acme
import (
"dagger.cloud/dagger"
"dagger.cloud/netlify"
"dagger.cloud/aws/ecs"
"dagger.cloud/microstaging"
)
// Website on netlify
www: netlify & {
domain: string | *defaultDomain
// By default, use a generated microstaging.io domain
// for easy environments on demand.
let defaultDomain=microstaging.#Domain & {
token: _
prefix: "www.acme"
}
}
// API deployed on ECS
api: ecs & {
domain: _ | *defaultDomain
let defaultDomain = microstaging.#Domain & {
token: _
prefix: "api.acme"
}
}
// Database on RDS
db: rds & {
engine: "postgresql"
}

View File

@ -0,0 +1,24 @@
package alpine
#Image: {
version: string | *"latest"
packages: [...string]
#dag: {
do: [
{
//
//
// fetch alpine
},
{
for _, pkg in packages {
}
}
]
}
}

View File

@ -0,0 +1 @@
module: "acme.infralabs.io/acme"

View File

@ -0,0 +1,104 @@
package dagger
// A DAG is the basic unit of programming in dagger.
// It is a special kind of program which runs as a pipeline of computing nodes running in parallel,
// instead of a sequence of operations to be run by a single node.
//
// It is a powerful way to automate various parts of an application delivery workflow:
// build, test, deploy, generate configuration, enforce policies, publish artifacts, etc.
//
// The DAG architecture has many benefits:
// - Because DAGs are made of nodes executing in parallel, they are easy to scale.
// - Because all inputs and outputs are snapshotted and content-addressed, DAGs
// can easily be made repeatable, can be cached aggressively, and can be replayed
// at will.
// - Because nodes are executed by the same container engine as docker-build, DAGs
// can be developed using any language or technology capable of running in a docker.
// Dockerfiles and docker images are natively supported for maximum compatibility.
//
// - Because DAGs are programmed declaratively with a powerful configuration language,
// they are much easier to test, debug and refactor than traditional programming languages.
//
// To execute a DAG, the dagger runtime JIT-compiles it to a low-level format called
// llb, and executes it with buildkit.
// Think of buildkit as a specialized VM for running compute graphs; and dagger as
// a complete programming environment for that VM.
//
// The tradeoff for all those wonderful features is that a DAG architecture cannot be used
// for all software: only software than can be run as a pipeline.
//
// A dagger component is a configuration value augmented
// by scripts defining how to compute it, present it to a user,
// encrypt it, etc.
#Component: #dagger: {
// script to compute the value
compute?: #Script
terminal?: {
// Display a message when opening a terminal session
greeting?: string
command: [string]: #Script
}
// Configure how the component is incorporated to user settings.
// Configure how the end-user can configure this component
settings?: {
// If not specified, scrape from comments
title?: string
description?: string
// Disable user input, even if incomplete?
hidden: true | *false
ui: _ // insert here something which can be compiled to react-jsonschema-form
// Show the cue default value to the user, as a default input value?
showDefault: true | *false
// Insert information needed by:
// 1) clients to encrypt
// ie. web wizard, cli
// 2) middleware to implement deicphering in the cuellb pipeline
// eg. integration with clcoud KMS, Vault...
//
// 3) connectors to make sure secrets are preserved
encrypt?: {
pubkey: string
cipher: string
}
}
}
// Any component can be referenced as a directory, since
// every dagger script outputs a filesystem state (aka a directory)
#Dir: #Component
#Script: [...#Op]
// One operation in a script
#Op: #Fetch | #Export | #Run | #Local
// Export a value from fs state to cue
#Export: ["export", string, "json"|"yaml"|"string"|"number"|"boolean"]
#Run: #runNoOpts | #runWithOpts
#runNoOpts: ["run", ...string]
#runWithOpts: ["run", #RunOpts, ...string]
#RunOpts: {
mount?: string: "tmpfs" | "cache" | { from: #Component|#Script }
env: [string]: string
dir: string | *"/"
always: true | *false
}
#Local: ["local", string]
#Fetch: #FetchGit | #FetchContainer
#FetchContainer: ["fetch", "container", string]
#FetchGit: ["fetch", "git", string, string]
#TestScript: #Script & [
["fetch", "container", "alpine:latest"],
["run", "echo", "hello", "world"]
]

View File

@ -0,0 +1,63 @@
package netlify
import (
".../alpine"
)
auth: {
#dag: {
encrypted: true
do: [
{
action: "fetch"
type: "docker"
source: "alpine"
},
{
action: "push"
}
]
}
{
username: string
password: string
} | {
// FIXME: enrypted!
token: string
}
}
name: string
domain?: string
// FIXME: directory!
source: bl.#Dir
let base = alpine.#Image & {
version: "foo"
packages: ["rsync", "npm", "openssh"]
}
// Netlify site ID
id: {
info1: string
info2: string
#dag: {
// run code to fetch id from netlify API
from: base
do: [
{
action: "run"
command: ["netlify-get-id", name, "-o", "/netlify-id.json"]
}
]
export: json: "/netlify-id.json"
}
}
url: string

View File

@ -0,0 +1,216 @@
// Custom netlify package
// ACME platform team <platform@acme.infralabs.io>
//
// TODO: upstream to dagger standard library.
package netlify
import (
"dagger.cloud/dagger"
)
// Netlify API token
token: {
#dag: {
encrypt: cipher: "..."
}
string
}
// Netlify site name
name?: string
// Source directory to deploy
source: dagger.#Dir
let apply={
#dag: {
from: alpine.#Base
do: [
["run", "npm", "install", "netlify-cli", "-g"],
[
"copy",
[
"fetch", "git", "https://github.com/shykes/tests", "netlify-scripts",
], "/", "/src",
]
// 2. fetch custom netlify scripts & iunstall
// 3. get ID from name; create if doesn't exist
// 4. deploy (via builder)
]
command: {
debug: {
from: base
do: ["run", "sh", "-c", """
env && find /netlify
"""]
}
}
}
}
apply
deployedDir: {
#dag: {
from: apply
export: dir: "/netlify/content"
}
}
// Netlify site ID
ID: {
string
#dag: {
from: apply
export: string: "/netlify/site-id"
}
}
url: {
string
#dag: {
from: apply
export: string: "/netlify/url"
}
}
// Example of short-form cuellb pipeline
// 1. single-op pipeline can omit the array
// 2. action encoded in first key, instead of `action: ` field
// 3. op may implement short-form,
// in this case: `run: [...string]` instead of `run: { command: [...string] }`
do: run: ["ntlfy-get-site-id", name, "-o", "/netlify/site-id"]
// Declarative export from container, instead of awkward `readFile` pseudo-op
export: string: "/netlify/site-id"
}
}
// Configuration presets
preset: {
*"html" | "react" | "custom"
#dag: {
settings: {
markup: select: {
"Static HTML site (no build)": "html"
"ReactJS app built with npm": "react"
"Custom builder": "custom"
}
}
}
}
// Custom builder
// Default: no build, deploy as-is.
builder: {
in: dagger.#Dir & source
out: dagger.#Dir
if preset == "html" {
// Pass-through builder that does nothing
out: in
}
if preset == "react" {
let app = reactjs.#App & {
source: in
}
out: app.build
}
...
}
scripts: {
dagger.#Directory | *latestScripts
let latestScripts = {
#dag: {
do: {
action: "fetch"
type: "git"
source: "https://github.com/shykes/tests"
ref: "netlify-scripts"
}
}
export: dir: "/"
}
}
// This is configurable for dev mode, but hide it from end users.
#dag: settings: hidden: true
}
// Version of the netlify CLI to use
cliVersion: string | *latestCLIVersion
let latestCLIVersion = {
string
#dag: {
from: base
do: run: ["sh", "-c", "npm show netlify-cli dist-tags.latest > /latest-cli-version"]
export: string: "/latest-cli-version"
}
}
// Custom container to run netlify commands + wrappers
let base=alpine.#Base & {
package: {
npm: true
curl: true
}
}
let runner = {
#dag: {
from: base
do: [
{
run: "npm", "install
action: "run"
command: ["npm", "install", "-g", "netlify-cli@" + cliVersion]
},
{
// YOU ARE HERE
// incorporate "netify scripts from personal github" pattern from other POC
}
}
}
url: {
string
#dag: {
from: runner
do: run: {
command: #"""
netlify deploy
--dir="$(pwd)" \
--auth="$(cat /netlify/token)" \
--site="${NETLIFY_SITE_ID}" \
--message="Blocklayer 'netlify deploy'" \
--prod \
| tee /tmp/stdout
curl \
-i -X POST \
-H "Authorization: Bearer $(cat /netlify/token)" \
"https://api.netlify.com/api/v1/sites/${NETLIFY_SITE_ID}/ssl"
"""#
mount: {
"/netlify/token": token
"/netlify/source": builder.out
}
dir: "/netlify/source"
env: {
NETLIFY_SITE_ID: ID
}
}
}
}

View File

@ -0,0 +1,89 @@
package netlify
#dag: {
do: [
{
action: "fetch"
type: "container"
repository: "alpine"
tag: "latest"
},
{
action: "run"
command: "apk add ..."
},
{
action: "copy"
from: [
{
action: "fetch"
type: "git"
repo: "https://github.com/shykes/stuff"
}
]
source: "/"
dest: "/src"
},
]
}
// Name of the netlify site
name: {
string
#dag: {
}
}
// ID of the netlify site
// FIXME: compute
id: {
string
#dag: {
from: ...
do: [
action: "run"
command: ["netlify-get-id", name, "-o", "/netlify-id.txt"]
]
export: string: "/netlify-id.txt"
}
}
// API token
// FIXME: encrypt secret!
token: {
#encrypt: {
pubkey: _
cipher: _
}
string
}
// FIXME: how to receive a directory?
source: bl.#Dir
// Domain of the Netlify site
domain?: string
// FIXME: compute
url: {
#dag: {
do: [
// ...
{
action: "run"
command: "netlify deploy"
dir: "/src"
mount: "/src": source
}
]
}
string
}

View File

@ -0,0 +1,3 @@

View File

@ -0,0 +1,46 @@
package alpine
// Default version pinned to digest. Manually updated.
let defaultDigest="sha256:3c7497bf0c7af93428242d6176e8f7905f2201d8fc5861f45be7a346b5f23436"
ref: string
// Match a combination of inputs 'version' and 'digest':
*{
// no version, no digest:
ref: "index.docker.io/alpine@\(defaultDigest)"
} | {
// version, no digest
version: string
ref: "alpine:\(version)"
} | {
// digest, no version
digest: string
ref: "alpine@\(digest)"
} | {
// version and digest
version: string
digest: string
ref: "alpine:\(version)@\(digest)"
}
// Packages to install
package: [string]: true | false | string
#dagger: compute: [
{
do: "fetch-container"
"ref": ref
},
for pkg, info in package {
if (info & true) != _|_ {
do: "exec"
args: ["apk", "add", "-U", "--no-cache", pkg]
}
if (info & string) != _|_ {
do: "exec"
args: ["apk", "add", "-U", "--no-cache", "\(pkg)\(info)"]
}
},
]

View File

@ -0,0 +1,145 @@
package dagger
// A DAG is the basic unit of programming in dagger.
// It is a special kind of program which runs as a pipeline of computing nodes running in parallel,
// instead of a sequence of operations to be run by a single node.
//
// It is a powerful way to automate various parts of an application delivery workflow:
// build, test, deploy, generate configuration, enforce policies, publish artifacts, etc.
//
// The DAG architecture has many benefits:
// - Because DAGs are made of nodes executing in parallel, they are easy to scale.
// - Because all inputs and outputs are snapshotted and content-addressed, DAGs
// can easily be made repeatable, can be cached aggressively, and can be replayed
// at will.
// - Because nodes are executed by the same container engine as docker-build, DAGs
// can be developed using any language or technology capable of running in a docker.
// Dockerfiles and docker images are natively supported for maximum compatibility.
//
// - Because DAGs are programmed declaratively with a powerful configuration language,
// they are much easier to test, debug and refactor than traditional programming languages.
//
// To execute a DAG, the dagger runtime JIT-compiles it to a low-level format called
// llb, and executes it with buildkit.
// Think of buildkit as a specialized VM for running compute graphs; and dagger as
// a complete programming environment for that VM.
//
// The tradeoff for all those wonderful features is that a DAG architecture cannot be used
// for all software: only software than can be run as a pipeline.
//
// A dagger component is a configuration value augmented
// by scripts defining how to compute it, present it to a user,
// encrypt it, etc.
#Component: {
#dagger: {
// script to compute the value
compute?: #Script
terminal?: {
// Display a message when opening a terminal session
greeting?: string
command: [string]: #Script
}
// Configure how the component is incorporated to user settings.
// Configure how the end-user can configure this component
settings?: {
// If not specified, scrape from comments
title?: string
description?: string
// Disable user input, even if incomplete?
hidden: true | *false
ui: _ // insert here something which can be compiled to react-jsonschema-form
// Show the cue default value to the user, as a default input value?
showDefault: true | *false
// Insert information needed by:
// 1) clients to encrypt
// ie. web wizard, cli
// 2) middleware to implement deicphering in the cuellb pipeline
// eg. integration with clcoud KMS, Vault...
//
// 3) connectors to make sure secrets are preserved
encrypt?: {
pubkey: string
cipher: string
}
}
}
...
}
// Any component can be referenced as a directory, since
// every dagger script outputs a filesystem state (aka a directory)
#Dir: #Component
#Script: [...#Op]
// One operation in a script
// #Op: #FetchContainer | #FetchGit | #Export | #Exec | #Copy | #Load
#Op: #FetchContainer | #Export | #Exec
// Export a value from fs state to cue
#Export: {
do: "export"
// Source path in the container
source: string
format: "json"|"yaml"|*"string"|"number"|"boolean"
}
#Load: #LoadComponent| #LoadScript
#LoadComponent: {
do: "load"
from: #Component
}
#LoadScript: {
do: "load"
from: #Script
}
#Exec: {
do: "exec"
args: [...string]
mount?: [string]: #MountTmp | #MountCache | #MountComponent | #MountScript
env: [string]: string
dir: string | *"/"
always: true | *false
}
#MountTmp: "tmpfs"
#MountCache: "cache"
#MountComponent: {
input: #Component
path: string | *"/"
}
#MountScript: {
input: #Script
path: string | *"/"
}
#FetchContainer: {
do: "fetch-container"
ref: string
}
#FetchGit: {
do: "fetch-git"
remote: string
ref: string
}
#Copy: {
do: "copy"
input: #Script | #Component
src: string | *"/"
dest: string | *"/"
}
#TestScript: #Script & [
{ do: "fetch-container", ref: "alpine:latest" },
{ do: "exec", args: ["echo", "hello", "world" ], env: DEBUG: "1" }
]

View File

@ -0,0 +1,63 @@
package example
import (
"dagger.cloud/alpine"
)
test: {
string
#dagger: compute: [
{ do: "load", from: alpine },
{
do: "copy"
from: [
{ do: "fetch-container", ref: alpine.ref },
]
dest: "/src"
},
{
do: "exec"
dir: "/src"
args: ["sh", "-c", """
ls -l > /tmp/out
"""
]
},
{
do: "export"
source: "/tmp/out"
format: "string"
}
]
}
www: {
// Domain where the site will be deployed (user input)
domain: string
// URL after deployment (computed)
url: {
string & =~ "https://.*"
#dagger: {
compute: [
{ do: "load", from: alpine },
{
do: "exec"
args: ["sh", "-c",
"""
echo 'deploying to netlify (not really)'
echo 'https://\(domain)/foo' > /tmp/out
"""
]
},
{
do: "export"
source: "/tmp/out"
format: "string"
}
]
}
}
}

View File

@ -0,0 +1,3 @@
package example
www: domain: "www.foobar.com"

View File

@ -0,0 +1 @@
module: "acme.infralabs.io/acme"

View File

@ -0,0 +1,46 @@
package alpine
// Default version pinned to digest. Manually updated.
let defaultDigest="sha256:3c7497bf0c7af93428242d6176e8f7905f2201d8fc5861f45be7a346b5f23436"
ref: string
// Match a combination of inputs 'version' and 'digest':
*{
// no version, no digest:
ref: "index.docker.io/alpine@\(defaultDigest)"
} | {
// version, no digest
version: string
ref: "alpine:\(version)"
} | {
// digest, no version
digest: string
ref: "alpine@\(digest)"
} | {
// version and digest
version: string
digest: string
ref: "alpine:\(version)@\(digest)"
}
// Packages to install
package: [string]: true | false | string
#dagger: compute: [
{
do: "fetch-container"
"ref": ref
},
for pkg, info in package {
if (info & true) != _|_ {
do: "exec"
args: ["apk", "add", "-U", "--no-cache", pkg]
}
if (info & string) != _|_ {
do: "exec"
args: ["apk", "add", "-U", "--no-cache", "\(pkg)\(info)"]
}
},
]

View File

@ -0,0 +1,145 @@
package dagger
// A DAG is the basic unit of programming in dagger.
// It is a special kind of program which runs as a pipeline of computing nodes running in parallel,
// instead of a sequence of operations to be run by a single node.
//
// It is a powerful way to automate various parts of an application delivery workflow:
// build, test, deploy, generate configuration, enforce policies, publish artifacts, etc.
//
// The DAG architecture has many benefits:
// - Because DAGs are made of nodes executing in parallel, they are easy to scale.
// - Because all inputs and outputs are snapshotted and content-addressed, DAGs
// can easily be made repeatable, can be cached aggressively, and can be replayed
// at will.
// - Because nodes are executed by the same container engine as docker-build, DAGs
// can be developed using any language or technology capable of running in a docker.
// Dockerfiles and docker images are natively supported for maximum compatibility.
//
// - Because DAGs are programmed declaratively with a powerful configuration language,
// they are much easier to test, debug and refactor than traditional programming languages.
//
// To execute a DAG, the dagger runtime JIT-compiles it to a low-level format called
// llb, and executes it with buildkit.
// Think of buildkit as a specialized VM for running compute graphs; and dagger as
// a complete programming environment for that VM.
//
// The tradeoff for all those wonderful features is that a DAG architecture cannot be used
// for all software: only software than can be run as a pipeline.
//
// A dagger component is a configuration value augmented
// by scripts defining how to compute it, present it to a user,
// encrypt it, etc.
#Component: {
#dagger: {
// script to compute the value
compute?: #Script
terminal?: {
// Display a message when opening a terminal session
greeting?: string
command: [string]: #Script
}
// Configure how the component is incorporated to user settings.
// Configure how the end-user can configure this component
settings?: {
// If not specified, scrape from comments
title?: string
description?: string
// Disable user input, even if incomplete?
hidden: true | *false
ui: _ // insert here something which can be compiled to react-jsonschema-form
// Show the cue default value to the user, as a default input value?
showDefault: true | *false
// Insert information needed by:
// 1) clients to encrypt
// ie. web wizard, cli
// 2) middleware to implement deicphering in the cuellb pipeline
// eg. integration with clcoud KMS, Vault...
//
// 3) connectors to make sure secrets are preserved
encrypt?: {
pubkey: string
cipher: string
}
}
}
...
}
// Any component can be referenced as a directory, since
// every dagger script outputs a filesystem state (aka a directory)
#Dir: #Component
#Script: [...#Op]
// One operation in a script
// #Op: #FetchContainer | #FetchGit | #Export | #Exec | #Copy | #Load
#Op: #FetchContainer | #Export | #Exec
// Export a value from fs state to cue
#Export: {
do: "export"
// Source path in the container
source: string
format: "json"|"yaml"|*"string"|"number"|"boolean"
}
#Load: #LoadComponent| #LoadScript
#LoadComponent: {
do: "load"
from: #Component
}
#LoadScript: {
do: "load"
from: #Script
}
#Exec: {
do: "exec"
args: [...string]
mount?: [string]: #MountTmp | #MountCache | #MountComponent | #MountScript
env: [string]: string
dir: string | *"/"
always: true | *false
}
#MountTmp: "tmpfs"
#MountCache: "cache"
#MountComponent: {
input: #Component
path: string | *"/"
}
#MountScript: {
input: #Script
path: string | *"/"
}
#FetchContainer: {
do: "fetch-container"
ref: string
}
#FetchGit: {
do: "fetch-git"
remote: string
ref: string
}
#Copy: {
do: "copy"
input: #Script | #Component
src: string | *"/"
dest: string | *"/"
}
#TestScript: #Script & [
{ do: "fetch-container", ref: "alpine:latest" },
{ do: "exec", args: ["echo", "hello", "world" ], env: DEBUG: "1" }
]

View File

@ -0,0 +1,33 @@
// ACME platform: everything you need to develop and ship improvements to
// the ACME clothing store.
package acme
import (
"dagger.cloud/alpine"
)
let base=alpine & {
package: {
bash: ">3.0"
rsync: true
}
}
www: {
source: {
#dagger: input: true
}
host: string
url: {
string
#dagger: compute: [
{ do: "load", from: base },
{ do: "exec", args: ["sh", "-c", "echo -n 'https://\(host)/foo' > /tmp/out"] },
{ do: "export", format: "string", source: "/tmp/out" },
]
}
}

View File

@ -0,0 +1,3 @@
package acme
www: host: "acme.infralabs.io"

76
go.mod Normal file
View File

@ -0,0 +1,76 @@
module dagger.cloud/go
go 1.13
require (
cuelang.org/go v0.3.0-alpha6
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible // indirect
github.com/Azure/go-autorest v10.8.1+incompatible // indirect
github.com/KromDaniel/jonson v0.0.0-20180630143114-d2f9c3c389db
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect
github.com/aws/aws-sdk-go v1.15.11 // indirect
github.com/bitly/go-simplejson v0.5.0 // indirect
github.com/blang/semver v3.1.0+incompatible // indirect
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/bshuster-repo/logrus-logstash-hook v0.4.1 // indirect
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd // indirect
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b // indirect
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 // indirect
github.com/containerd/console v1.0.0
github.com/containerd/containerd v1.4.0-0.20191014053712-acdcf13d5eaf
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba // indirect
github.com/dnaeon/go-vcr v1.0.1 // indirect
github.com/docker/cli v0.0.0-20200227165822-2298e6a3fe24
github.com/docker/distribution v2.7.1+incompatible
github.com/docker/docker v1.14.0-0.20190319215453-e7b5f7dbe98c // indirect
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916 // indirect
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 // indirect
github.com/emicklei/proto v1.9.0 // indirect
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 // indirect
github.com/gofrs/uuid v3.3.0+incompatible
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 // indirect
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce // indirect
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874 // indirect
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7 // indirect
github.com/machinebox/graphql v0.2.2
github.com/marstr/guid v1.1.0 // indirect
github.com/matryer/is v1.3.0 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f // indirect
github.com/moby/buildkit v0.7.1-0.20200529013301-643f0ffb5643
github.com/ncw/swift v1.0.47 // indirect
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/runc v1.0.0-rc9.0.20200221051241-688cf6d43cc4 // indirect
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39 // indirect
github.com/pkg/errors v0.9.1
github.com/prometheus/procfs v0.0.5 // indirect
github.com/rs/zerolog v1.17.2
github.com/satori/go.uuid v1.2.0 // indirect
github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.6.2
github.com/tonistiigi/fsutil v0.0.0-20200512175118-ae3a8d753069
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f // indirect
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 // indirect
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 // indirect
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f // indirect
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 // indirect
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 // indirect
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff // indirect
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8 // indirect
google.golang.org/grpc v1.27.1
gopkg.in/yaml.v3 v3.0.0-20200506231410-2ff61e1afc86 // indirect
k8s.io/kubernetes v1.13.0 // indirect
)
replace github.com/hashicorp/go-immutable-radix => github.com/tonistiigi/go-immutable-radix v0.0.0-20170803185627-826af9ccf0fe
replace github.com/jaguilar/vt100 => github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305
replace github.com/containerd/containerd => github.com/containerd/containerd v1.3.1-0.20200227195959-4d242818bf55
replace github.com/docker/docker => github.com/docker/docker v1.4.2-0.20200227233006-38f52c9fec82

444
go.sum Normal file
View File

@ -0,0 +1,444 @@
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cuelang.org/go v0.2.2 h1:i/wFo48WDibGHKQTRZ08nB8PqmGpVpQ2sRflZPj73nQ=
cuelang.org/go v0.3.0-alpha4.0.20201118122203-cd621ffa2727 h1:9oH36KOs7IQwrSXlLhGRy6PPkiwBCC7pcbkTE1TmPwY=
cuelang.org/go v0.3.0-alpha4.0.20201118122203-cd621ffa2727/go.mod h1:un6c4wnW3jEIrp97ARM3eJOlIJojP61ASQTRHyR0uiE=
cuelang.org/go v0.3.0-alpha6 h1:UA+GMa6JdTMG2ywjBCbEzgPbZ4y4pudaFGvFSeZ9h8s=
cuelang.org/go v0.3.0-alpha6/go.mod h1:NwRWsRzQvqkhCHdkIjFBKo9ujPd1OQLZzugDjElHh8Q=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AkihiroSuda/containerd-fuse-overlayfs v0.0.0-20200512015515-32086ef23a5a/go.mod h1:RkqizX9+ro7Pp7RxEZAJWIr1/FrkZKCuUDi944JHt0U=
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/KromDaniel/jonson v0.0.0-20180630143114-d2f9c3c389db h1:Zkf5kwhxdW0xV7WM/crqIcOP5LCFGnAmumWSFAewJ74=
github.com/KromDaniel/jonson v0.0.0-20180630143114-d2f9c3c389db/go.mod h1:RU+6d0CNIRSp6yo1mXLIIrnFa/3LHhvcDVLVJyovptM=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/apd/v2 v2.0.1 h1:y1Rh3tEU89D+7Tgbw+lp52T6p/GJLpDmNvr10UWqLTE=
github.com/cockroachdb/apd/v2 v2.0.1/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw=
github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
github.com/containerd/cgroups v0.0.0-20200327175542-b44481373989/go.mod h1:CStdkl05lBnJej94BPFoJ7vB8cELKXwViS+dgfW0/M8=
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
github.com/containerd/console v1.0.0 h1:fU3UuQapBs+zLJu82NhR11Rif1ny2zfMMAyPJzSN5tQ=
github.com/containerd/console v1.0.0/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
github.com/containerd/containerd v1.3.1-0.20200227195959-4d242818bf55 h1:FGO0nwSBESgoGCakj+w3OQXyrMLsz2omdo9b2UfG/BQ=
github.com/containerd/containerd v1.3.1-0.20200227195959-4d242818bf55/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20200228182428-0f16d7a0959c/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY=
github.com/containerd/continuity v0.0.0-20200413184840-d3ef23f19fbb h1:nXPkFq8X1a9ycY3GYQpFNxHh3j2JgY7zDZfq2EXMIzk=
github.com/containerd/continuity v0.0.0-20200413184840-d3ef23f19fbb/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY=
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
github.com/containerd/go-cni v0.0.0-20200107172653-c154a49e2c75/go.mod h1:0mg8r6FCdbxvLDqCXwAx2rO+KA37QICjKL8+wHOG5OE=
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/ttrpc v1.0.1 h1:IfVOxKbjyBn9maoye2JN95pgGYOmPkQVqxtOu7rtNIc=
github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/cli v0.0.0-20200227165822-2298e6a3fe24 h1:bjsfAvm8BVtvQFxV7TYznmKa35J8+fmgrRJWvcS3yJo=
github.com/docker/cli v0.0.0-20200227165822-2298e6a3fe24/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v1.4.2-0.20200227233006-38f52c9fec82 h1:kZwwJwYnVWtU/byBNjD9rEGWVMvwnfiKu9lFJXjrk04=
github.com/docker/docker v1.4.2-0.20200227233006-38f52c9fec82/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.0/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libnetwork v0.8.0-dev.2.0.20200226230617-d8334ccdb9be/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8=
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/emicklei/proto v1.6.15/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/emicklei/proto v1.9.0 h1:l0QiNT6Qs7Yj0Mb4X6dnWBQer4ebei2BFcgQLbGqUDc=
github.com/emicklei/proto v1.9.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.7.0 h1:pGFUjl501gafK9HBt1VGL1KCOd/YhIooID+xgyJCf3g=
github.com/gofrs/flock v0.7.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/googleapis v1.3.2 h1:kX1es4djPJrsDhY7aZKJy7aZasdcB5oSOEphMjSB53c=
github.com/gogo/googleapis v1.3.2/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9 h1:JM174NTeGNJ2m/oLH3UOWOvWQQKd+BoL3hcSCUWFLt0=
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg=
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c/go.mod h1:fHzc09UnyJyqyW+bFuq864eh+wC7dj65aXmXLRe5to0=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/ishidawataru/sctp v0.0.0-20191218070446-00ab2ac2db07/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/machinebox/graphql v0.2.2/go.mod h1:F+kbVMHuwrQ5tYgU9JXlnskM8nOaFxCAEolaQybkjWA=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
github.com/matryer/is v1.3.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/moby/buildkit v0.7.1-0.20200529013301-643f0ffb5643 h1:UFxj67FF2yIwNjCUV0PL+GbzSovoXMk+mWz9Cn+wa04=
github.com/moby/buildkit v0.7.1-0.20200529013301-643f0ffb5643/go.mod h1:4otpTPV294PXwTbjBlYnS1Er42Pe6baNDIS84W/Q2PM=
github.com/moby/sys/mount v0.1.0/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74=
github.com/moby/sys/mountinfo v0.1.0/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o=
github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto=
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc10/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc9.0.20200221051241-688cf6d43cc4 h1:JhRvjyrjq24YPSDS0MQo9KJHQh95naK5fYl9IT+dzPM=
github.com/opencontainers/runc v1.0.0-rc9.0.20200221051241-688cf6d43cc4/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
github.com/opentracing-contrib/go-stdlib v0.0.0-20171029140428-b1a47cfbdd75/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.6.2-0.20200830194709-1115b6af0369/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.17.2/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tonistiigi/fsutil v0.0.0-20200512175118-ae3a8d753069 h1:F8MYILe5YNjbPLpmF/OgYrfsHBnAFZecylt1AXl8zow=
github.com/tonistiigi/fsutil v0.0.0-20200512175118-ae3a8d753069/go.mod h1:uA7OEv9Ab41e89xMMPlecftcyNsXGVHuI71vi1acNeg=
github.com/tonistiigi/go-immutable-radix v0.0.0-20170803185627-826af9ccf0fe/go.mod h1:/+MCh11CJf2oz0BXmlmqyopK/ad1rKkcOXPoYuPCJYU=
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305 h1:y/1cL5AL2oRcfzz8CAHHhR6kDDfIOT0WEyH5k40sccM=
github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305/go.mod h1:gXOLibKqQTRAVuVZ9gX7G9Ykky8ll8yb4slxsEMoY0c=
github.com/uber/jaeger-client-go v2.11.2+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v1.2.1/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200612220849-54c614fe050c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200227132054-3f1135a288c9 h1:Koy0f8zyrEVfIHetH7wjP5mQLUXiqDpubSg8V1fAxqc=
google.golang.org/genproto v0.0.0-20200227132054-3f1135a288c9/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200506231410-2ff61e1afc86 h1:OfFoIUYv/me30yv7XlMy4F9RJw8DEm8WQ6QG1Ph4bH0=
gopkg.in/yaml.v3 v3.0.0-20200506231410-2ff61e1afc86/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=

43
main.go Normal file
View File

@ -0,0 +1,43 @@
// A simple main.go for testing the dagger Go API
package main
import (
"context"
"fmt"
"os"
"dagger.cloud/go/dagger"
)
func main() {
ctx := context.TODO()
c, err := dagger.NewClient(ctx, "")
if err != nil {
fatal(err)
}
configPath := "."
if len(os.Args) > 1 {
configPath = os.Args[1]
}
if err := c.SetConfig(configPath); err != nil {
fatal(err)
}
// if err := c.ConnectInput("source", os.Getenv("HOME")+"/Documents/github/samalba/hello-go"); err != nil {
// fatal(err)
// }
if err := c.Run(ctx, "compute"); err != nil {
fatal(err)
}
}
func fatalf(msg string, args ...interface{}) {
fmt.Fprintf(os.Stderr, msg, args...)
os.Exit(1)
}
func fatal(msg interface{}) {
fatalf("%s\n", msg)
}