commit 30f75da114246f4ba40f08929d06564364040e0e Author: Solomon Hykes Date: Tue Dec 29 18:45:16 2020 -0800 Move prototype 69-dagger-archon to top-level Signed-off-by: Solomon Hykes diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..c9d07302 --- /dev/null +++ b/Dockerfile @@ -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"] + diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..2f859fed --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +.PHONY: dagger +dagger: + go generate ./dagger && go build -o ./cmd/dagger/ ./cmd/dagger/ diff --git a/README.md b/README.md new file mode 100644 index 00000000..4d3c96ae --- /dev/null +++ b/README.md @@ -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. diff --git a/cmd/dagger-frontend/main.go b/cmd/dagger-frontend/main.go new file mode 100644 index 00000000..9894b00d --- /dev/null +++ b/cmd/dagger-frontend/main.go @@ -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) + } +} diff --git a/cmd/dagger/.gitignore b/cmd/dagger/.gitignore new file mode 100644 index 00000000..195836cc --- /dev/null +++ b/cmd/dagger/.gitignore @@ -0,0 +1,2 @@ +dagger +.dagger diff --git a/cmd/dagger/cmd/compute.go b/cmd/dagger/cmd/compute.go new file mode 100644 index 00000000..cd98b3e4 --- /dev/null +++ b/cmd/dagger/cmd/compute.go @@ -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") +} diff --git a/cmd/dagger/cmd/create.go b/cmd/dagger/cmd/create.go new file mode 100644 index 00000000..72f6c6aa --- /dev/null +++ b/cmd/dagger/cmd/create.go @@ -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) + }, +} diff --git a/cmd/dagger/cmd/root.go b/cmd/dagger/cmd/root.go new file mode 100644 index 00000000..81a4638f --- /dev/null +++ b/cmd/dagger/cmd/root.go @@ -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) + } +} diff --git a/cmd/dagger/main.go b/cmd/dagger/main.go new file mode 100644 index 00000000..d76dc523 --- /dev/null +++ b/cmd/dagger/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "dagger.cloud/go/cmd/dagger/cmd" +) + +func main() { + cmd.Execute() +} diff --git a/dagger/client.go b/dagger/client.go new file mode 100644 index 00000000..5a48195c --- /dev/null +++ b/dagger/client.go @@ -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 +} diff --git a/dagger/gen.go b/dagger/gen.go new file mode 100644 index 00000000..40f876c3 --- /dev/null +++ b/dagger/gen.go @@ -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" ] } +] +` diff --git a/dagger/gen.sh b/dagger/gen.sh new file mode 100755 index 00000000..f851175e --- /dev/null +++ b/dagger/gen.sh @@ -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 diff --git a/dagger/job.go b/dagger/job.go new file mode 100644 index 00000000..ee97eb03 --- /dev/null +++ b/dagger/job.go @@ -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() +} diff --git a/dagger/json.go b/dagger/json.go new file mode 100644 index 00000000..c0eaf767 --- /dev/null +++ b/dagger/json.go @@ -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) +} diff --git a/dagger/runtime.go b/dagger/runtime.go new file mode 100644 index 00000000..bad7eb92 --- /dev/null +++ b/dagger/runtime.go @@ -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 +} diff --git a/dagger/script.go b/dagger/script.go new file mode 100644 index 00000000..e5636a19 --- /dev/null +++ b/dagger/script.go @@ -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) +} diff --git a/dagger/spec.cue b/dagger/spec.cue new file mode 100644 index 00000000..f0f8c8c0 --- /dev/null +++ b/dagger/spec.cue @@ -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" ] } +] diff --git a/dagger/spec_test.go b/dagger/spec_test.go new file mode 100644 index 00000000..b1f3b750 --- /dev/null +++ b/dagger/spec_test.go @@ -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() +} diff --git a/dagger/state.go b/dagger/state.go new file mode 100644 index 00000000..27be087d --- /dev/null +++ b/dagger/state.go @@ -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 +} diff --git a/dagger/ui/ui.go b/dagger/ui/ui.go new file mode 100644 index 00000000..bff3e8f5 --- /dev/null +++ b/dagger/ui/ui.go @@ -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...) +} diff --git a/dagger/utils.go b/dagger/utils.go new file mode 100644 index 00000000..5588372d --- /dev/null +++ b/dagger/utils.go @@ -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 +} diff --git a/examples/acme-platform/README.md b/examples/acme-platform/README.md new file mode 100644 index 00000000..11e9af89 --- /dev/null +++ b/examples/acme-platform/README.md @@ -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) diff --git a/examples/acme-platform/acme.cue b/examples/acme-platform/acme.cue new file mode 100644 index 00000000..951518e0 --- /dev/null +++ b/examples/acme-platform/acme.cue @@ -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" + +} diff --git a/examples/acme-platform/alpine.cue b/examples/acme-platform/alpine.cue new file mode 100644 index 00000000..83ec7dd4 --- /dev/null +++ b/examples/acme-platform/alpine.cue @@ -0,0 +1,24 @@ +package alpine + + +#Image: { + + version: string | *"latest" + packages: [...string] + + #dag: { + do: [ + { + // + // + // fetch alpine + }, + { + for _, pkg in packages { + + } + } + + ] + } +} diff --git a/examples/acme-platform/cue.mod/module.cue b/examples/acme-platform/cue.mod/module.cue new file mode 100644 index 00000000..b66f698b --- /dev/null +++ b/examples/acme-platform/cue.mod/module.cue @@ -0,0 +1 @@ +module: "acme.infralabs.io/acme" diff --git a/examples/acme-platform/cue.mod/pkg/dagger.cloud/dagger/dagger.cue b/examples/acme-platform/cue.mod/pkg/dagger.cloud/dagger/dagger.cue new file mode 100644 index 00000000..90c3af20 --- /dev/null +++ b/examples/acme-platform/cue.mod/pkg/dagger.cloud/dagger/dagger.cue @@ -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"] +] diff --git a/examples/acme-platform/mynetlify.cue b/examples/acme-platform/mynetlify.cue new file mode 100644 index 00000000..6e48f039 --- /dev/null +++ b/examples/acme-platform/mynetlify.cue @@ -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 diff --git a/examples/acme-platform/netlify/netlify.cue b/examples/acme-platform/netlify/netlify.cue new file mode 100644 index 00000000..93fa86e5 --- /dev/null +++ b/examples/acme-platform/netlify/netlify.cue @@ -0,0 +1,216 @@ +// Custom netlify package +// ACME platform team +// +// 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 + } + } + } +} diff --git a/examples/acme-platform/scratch.cue b/examples/acme-platform/scratch.cue new file mode 100644 index 00000000..3a1166d2 --- /dev/null +++ b/examples/acme-platform/scratch.cue @@ -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 +} diff --git a/examples/acme-platform/throaway b/examples/acme-platform/throaway new file mode 100644 index 00000000..b28b04f6 --- /dev/null +++ b/examples/acme-platform/throaway @@ -0,0 +1,3 @@ + + + diff --git a/examples/olivier/cue.mod/pkg/dagger.cloud/alpine/alpine.cue b/examples/olivier/cue.mod/pkg/dagger.cloud/alpine/alpine.cue new file mode 100644 index 00000000..58e0d7af --- /dev/null +++ b/examples/olivier/cue.mod/pkg/dagger.cloud/alpine/alpine.cue @@ -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)"] + } + }, +] + diff --git a/examples/olivier/cue.mod/pkg/dagger.cloud/dagger/dagger.cue b/examples/olivier/cue.mod/pkg/dagger.cloud/dagger/dagger.cue new file mode 100644 index 00000000..6ee939da --- /dev/null +++ b/examples/olivier/cue.mod/pkg/dagger.cloud/dagger/dagger.cue @@ -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" } +] diff --git a/examples/olivier/example.cue b/examples/olivier/example.cue new file mode 100644 index 00000000..b1331cc9 --- /dev/null +++ b/examples/olivier/example.cue @@ -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" + } + ] + } + } +} diff --git a/examples/olivier/values.cue b/examples/olivier/values.cue new file mode 100644 index 00000000..69c6ebdc --- /dev/null +++ b/examples/olivier/values.cue @@ -0,0 +1,3 @@ +package example + +www: domain: "www.foobar.com" diff --git a/examples/simple/cue.mod/module.cue b/examples/simple/cue.mod/module.cue new file mode 100644 index 00000000..b66f698b --- /dev/null +++ b/examples/simple/cue.mod/module.cue @@ -0,0 +1 @@ +module: "acme.infralabs.io/acme" diff --git a/examples/simple/cue.mod/pkg/dagger.cloud/alpine/alpine.cue b/examples/simple/cue.mod/pkg/dagger.cloud/alpine/alpine.cue new file mode 100644 index 00000000..58e0d7af --- /dev/null +++ b/examples/simple/cue.mod/pkg/dagger.cloud/alpine/alpine.cue @@ -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)"] + } + }, +] + diff --git a/examples/simple/cue.mod/pkg/dagger.cloud/dagger/dagger.cue b/examples/simple/cue.mod/pkg/dagger.cloud/dagger/dagger.cue new file mode 100644 index 00000000..6ee939da --- /dev/null +++ b/examples/simple/cue.mod/pkg/dagger.cloud/dagger/dagger.cue @@ -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" } +] diff --git a/examples/simple/simple.cue b/examples/simple/simple.cue new file mode 100644 index 00000000..f2125984 --- /dev/null +++ b/examples/simple/simple.cue @@ -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" }, + ] + } +} diff --git a/examples/simple/values.cue b/examples/simple/values.cue new file mode 100644 index 00000000..5884ac8b --- /dev/null +++ b/examples/simple/values.cue @@ -0,0 +1,3 @@ +package acme + +www: host: "acme.infralabs.io" diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..fc227d4e --- /dev/null +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..f00e17f1 --- /dev/null +++ b/go.sum @@ -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= diff --git a/main.go b/main.go new file mode 100644 index 00000000..fcb1040b --- /dev/null +++ b/main.go @@ -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) +}