From c5a71ec1ba53e3bd38f086be37284e623f335cdb Mon Sep 17 00:00:00 2001 From: Yevgeny Pats Date: Fri, 2 Aug 2019 21:27:54 +0300 Subject: [PATCH] Add Local Sanity option via docker --- client/auth.go | 38 +---- client/client.go | 41 +++++- client/commands.go | 195 +++++++++++++++++++++++-- client/storage.go | 41 +++++- cmd/auth.go | 3 +- cmd/download.go | 83 +++++++++++ cmd/get.go | 2 +- cmd/job.go | 27 +++- cmd/root.go | 2 +- docker/debian/stretch/llvm8/Dockerfile | 8 +- docker/debian/stretch/llvm9/Dockerfile | 10 +- docker/run.sh | 41 ++++++ go.mod | 8 + go.sum | 22 +++ 14 files changed, 461 insertions(+), 60 deletions(-) create mode 100644 cmd/download.go create mode 100755 docker/run.sh diff --git a/client/auth.go b/client/auth.go index 7fcfc0a..799ed81 100644 --- a/client/auth.go +++ b/client/auth.go @@ -9,7 +9,6 @@ import ( "io/ioutil" "log" "net/url" - "os" "os/user" "path" "time" @@ -19,33 +18,7 @@ import ( "google.golang.org/api/option" ) -func (c *fuzzitClient) ReAuthenticate(force bool) error { - usr, err := user.Current() - if err != nil { - log.Fatal(err) - } - cacheFile := path.Join(usr.HomeDir, ".fuzzit.cache") - - if !force { - file, err := os.Open(cacheFile) - if err != nil { - return err - } - - err = json.NewDecoder(file).Decode(c) - file.Close() - if err != nil { - // try to prevent being stuck forever if cache file gets corrupted - os.Remove(cacheFile) // if a file - os.RemoveAll(cacheFile) // if a directory - return err - } - } - - if c.ApiKey == "" { - return errors.New("API Key is no configured, please run fuzzit auth [api_key]") - } - +func (c *fuzzitClient) refreshToken() error { if c.IdToken == "" || (time.Now().Unix()-c.LastRefresh) > 60*45 { createCustomTokenEndpoint := fmt.Sprintf("%s/createCustomToken?api_key=%s", FuzzitEndpoint, url.QueryEscape(c.ApiKey)) r, err := c.httpClient.Get(createCustomTokenEndpoint) @@ -54,7 +27,7 @@ func (c *fuzzitClient) ReAuthenticate(force bool) error { } defer r.Body.Close() if r.StatusCode != 200 { - return errors.New("API Key is not valid") + return errors.New("API Key is not valid. Try running fuzzit auth again") } err = json.NewDecoder(r.Body).Decode(c) @@ -81,6 +54,12 @@ func (c *fuzzitClient) ReAuthenticate(force bool) error { if err != nil { return err } + + usr, err := user.Current() + if err != nil { + log.Fatal(err) + } + cacheFile := path.Join(usr.HomeDir, ".fuzzit.cache") err = ioutil.WriteFile(cacheFile, cBytes, 0644) if err != nil { return err @@ -97,7 +76,6 @@ func (c *fuzzitClient) ReAuthenticate(force bool) error { tokenSource := oauth2.StaticTokenSource(&token) ctx := context.Background() - // some known issue with go afaik firestoreClient, err := firestore.NewClient(ctx, "fuzzit-b5fbf", option.WithTokenSource(tokenSource)) c.firestoreClient = firestoreClient diff --git a/client/client.go b/client/client.go index 8b5512a..70a87f4 100644 --- a/client/client.go +++ b/client/client.go @@ -2,7 +2,12 @@ package client import ( "cloud.google.com/go/firestore" + "encoding/json" + "log" "net/http" + "os" + "os/user" + "path" "time" ) @@ -15,6 +20,7 @@ type Target struct { type Job struct { TargetId string `firestore:"target_id"` Args string `firestore:"args"` + Local bool Type string `firestore:"type"` Host string `firestore:"host"` Revision string `firestore:"revision"` @@ -49,21 +55,48 @@ type fuzzitClient struct { httpClient *http.Client } -func NewFuzzitClient(apiKey string) *fuzzitClient { +func NewFuzzitClient(apiKey string) (*fuzzitClient, error) { c := &fuzzitClient{} c.httpClient = &http.Client{Timeout: 60 * time.Second} c.ApiKey = apiKey - - return c + err := c.refreshToken() + if err != nil { + return nil, err + } + return c, nil } func LoadFuzzitFromCache() (*fuzzitClient, error) { c := &fuzzitClient{} c.httpClient = &http.Client{Timeout: 60 * time.Second} - err := c.ReAuthenticate(false) + + usr, err := user.Current() + if err != nil { + log.Fatal(err) + } + cacheFile := path.Join(usr.HomeDir, ".fuzzit.cache") + + if _, err := os.Stat(cacheFile); os.IsNotExist(err) { + return c, nil + } + + file, err := os.Open(cacheFile) + if err != nil { + return nil, err + } + + err = json.NewDecoder(file).Decode(c) + file.Close() if err != nil { + // try to prevent being stuck forever if cache file gets corrupted + os.Remove(cacheFile) // if a file + os.RemoveAll(cacheFile) // if a directory return nil, err } + //if c.ApiKey == "" { + // return errors.New("API Key is not configured (will have access only to public repositories)") + //} + return c, nil } diff --git a/client/commands.go b/client/commands.go index 5a23317..9e666f8 100644 --- a/client/commands.go +++ b/client/commands.go @@ -1,23 +1,88 @@ package client import ( + "cloud.google.com/go/firestore" "context" "encoding/json" "fmt" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/pkg/stdcopy" + "github.com/google/uuid" + "github.com/mholt/archiver" + "google.golang.org/api/iterator" + "io" "io/ioutil" "log" "os" "path/filepath" "strings" - - "cloud.google.com/go/firestore" - "github.com/google/uuid" - "github.com/mholt/archiver" - "google.golang.org/api/iterator" + //"github.com/docker/docker/api/types/container" + "github.com/docker/docker/client" + //"github.com/docker/docker/pkg/stdcopy" ) +func (c *fuzzitClient) archiveFiles(files []string) (string, error) { + fuzzerPath := files[0] + filename := filepath.Base(fuzzerPath) + if !strings.HasSuffix(filename, ".tar.gz") { + tmpDir, err := ioutil.TempDir("", "fuzzit") + if err != nil { + return "", err + } + dstPath := filepath.Join(tmpDir, "fuzzer") + _, err = copyFile(dstPath, fuzzerPath) + if err != nil { + return "", err + } + + prefix, err := uuid.NewRandom() + if err != nil { + return "", err + } + filesToArchive := append([]string{dstPath}, files[1:]...) + + tmpfile := filepath.Join(os.TempDir(), prefix.String()+".tar.gz") + z := archiver.NewTarGz() + err = z.Archive(filesToArchive, tmpfile) + if err != nil { + return "", err + } + fuzzerPath = tmpfile + } + + return fuzzerPath, nil +} + +func (c *fuzzitClient) DownloadSeed(dst string, target string) error { + storagePath := fmt.Sprintf("orgs/%s/targets/%s/seed", c.Org, target) + err := c.downloadFile(dst, storagePath) + if err != nil { + return err + } + return nil +} + +func (c *fuzzitClient) DownloadCorpus(dst string, target string) error { + storagePath := fmt.Sprintf("orgs/%s/targets/%s/corpus", c.Org, target) + err := c.downloadFile(dst, storagePath) + if err != nil { + return err + } + return nil +} + +func (c *fuzzitClient) DownloadFuzzer(dst string, target string, job string) error { + storagePath := fmt.Sprintf("orgs/%s/targets/%s/jobs/%s/fuzzer", c.Org, target, job) + err := c.downloadFile(dst, storagePath) + if err != nil { + return err + } + return nil +} + func (c *fuzzitClient) GetResource(resource string) error { - err := c.ReAuthenticate(false) + err := c.refreshToken() if err != nil { return err } @@ -74,9 +139,14 @@ func (c *fuzzitClient) GetResource(resource string) error { } func (c *fuzzitClient) CreateTarget(targetName string, seedPath string) (*firestore.DocumentRef, error) { + err := c.refreshToken() + if err != nil { + return nil, err + } + ctx := context.Background() docRef := c.firestoreClient.Doc("orgs/" + c.Org + "/targets/" + targetName) - _, err := docRef.Get(ctx) + _, err = docRef.Get(ctx) if err == nil { return nil, fmt.Errorf("target %s already exist", targetName) } @@ -97,9 +167,116 @@ func (c *fuzzitClient) CreateTarget(targetName string, seedPath string) (*firest return docRef, nil } -func (c *fuzzitClient) CreateJob(jobConfig Job, files []string) (*firestore.DocumentRef, error) { +func (c *fuzzitClient) CreateLocalJob(jobConfig Job, files []string) error { ctx := context.Background() + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + return err + } + cli.NegotiateAPIVersion(ctx) + fuzzerPath, err := c.archiveFiles(files) + if err != nil { + return err + } + + fuzzer, err := os.Open(fuzzerPath) + if err != nil { + return err + } + + corpusPath := fmt.Sprintf("orgs/%s/targets/%s/corpus.tar.gz", c.Org, jobConfig.TargetId) + log.Print(corpusPath) + corpusLink, err := c.getStorageLink(corpusPath, "read") + if err != nil { + return err + } + + seedPath := fmt.Sprintf("orgs/%s/targets/%s/seed", c.Org, jobConfig.TargetId) + seedLink, err := c.getStorageLink(seedPath, "read") + if err != nil { + return err + } + + log.Println("Pulling container") + reader, err := cli.ImagePull(ctx, "docker.io/fuzzitdev/fuzzit:stretch-llvm9", types.ImagePullOptions{}) + if err != nil { + return err + } + _, err = io.Copy(os.Stdout, reader) + if err != nil { + return err + } + + log.Println("Creating container") + createdContainer, err := cli.ContainerCreate(ctx, &container.Config{ + Env: []string{ + "CORPUS_LINK=" + corpusLink, + "SEED_LINK=" + seedLink, + "ASAN_OPTIONS=" + jobConfig.AsanOptions, + "UBSAN_OPTIONS=" + jobConfig.UbsanOptions, + "ARGS=" + jobConfig.Args}, + Image: "docker.io/fuzzitdev/fuzzit:stretch-llvm9", + AttachStdin: true, + }, nil, nil, "") + if err != nil { + return err + } + + log.Println("Uploading fuzzer to container") + err = cli.CopyToContainer(ctx, createdContainer.ID, "/app/", fuzzer, types.CopyToContainerOptions{ + AllowOverwriteDirWithFile: true, + }) + if err != nil { + return err + } + + log.Println("Starting the container") + err = cli.ContainerStart(ctx, createdContainer.ID, types.ContainerStartOptions{}) + if err != nil { + return err + } + + out, err := cli.ContainerLogs(ctx, createdContainer.ID, types.ContainerLogsOptions{ + ShowStdout: true, + ShowStderr: true, + Follow: true, + }) + if err != nil { + return err + } + + stdcopy.StdCopy(os.Stdout, os.Stderr, out) + + log.Println("Waiting for container") + statusCh, errCh := cli.ContainerWait(ctx, createdContainer.ID, container.WaitConditionNotRunning) + select { + case err := <-errCh: + if err != nil { + cli.ContainerRemove(ctx, createdContainer.ID, types.ContainerRemoveOptions{}) + return err + } + case status := <-statusCh: + if status.StatusCode != 0 { + cli.ContainerRemove(ctx, createdContainer.ID, types.ContainerRemoveOptions{}) + return fmt.Errorf("fuzzer exited with %d", status.StatusCode) + } + } + + err = cli.ContainerRemove(ctx, createdContainer.ID, types.ContainerRemoveOptions{}) + if err != nil { + return err + } + + return nil +} + +func (c *fuzzitClient) CreateJob(jobConfig Job, files []string) (*firestore.DocumentRef, error) { + err := c.refreshToken() + if err != nil { + return nil, err + } + ctx := context.Background() collectionRef := c.firestoreClient.Collection("orgs/" + c.Org + "/targets/" + jobConfig.TargetId + "/jobs") fullJob := job{} fullJob.Job = jobConfig @@ -141,7 +318,7 @@ func (c *fuzzitClient) CreateJob(jobConfig Job, files []string) (*firestore.Docu storagePath := fmt.Sprintf("orgs/%s/targets/%s/jobs/%s/fuzzer", c.Org, jobConfig.TargetId, jobRef.ID) log.Println("Uploading fuzzer...") - err := c.uploadFile(fuzzerPath, storagePath, "application/gzip", "fuzzer.tar.gz") + err = c.uploadFile(fuzzerPath, storagePath, "application/gzip", "fuzzer.tar.gz") if err != nil { return nil, err } diff --git a/client/storage.go b/client/storage.go index fb54223..7f20917 100755 --- a/client/storage.go +++ b/client/storage.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "io/ioutil" "log" "net/http" @@ -18,15 +19,18 @@ type storageLinkResponse struct { StorageLink string `json:"storage_link"` } -func (c *fuzzitClient) getStorageLink(storagePath string) (string, error) { - uri := fmt.Sprintf("https://app.fuzzit.dev/getStorageLink?path=%s&api_key=%s", url.QueryEscape(storagePath), url.QueryEscape(c.ApiKey)) +func (c *fuzzitClient) getStorageLink(storagePath string, action string) (string, error) { + uri := fmt.Sprintf("https://app.fuzzit.dev/getStorageLinkV3?path=%s&api_key=%s&action=%s", + url.QueryEscape(storagePath), + url.QueryEscape(c.ApiKey), + action) r, err := httpClient.Get(uri) if err != nil { return "", err } defer r.Body.Close() if r.StatusCode != 200 { - return "", fmt.Errorf("API Key is not valid") + return "", errors.New(r.Status) } var res storageLinkResponse @@ -45,7 +49,7 @@ func (c *fuzzitClient) uploadFile(filePath string, storagePath string, contentTy } defer data.Close() - storageLink, err := c.getStorageLink(storagePath) + storageLink, err := c.getStorageLink(storagePath, "write") if err != nil { return err } @@ -69,3 +73,32 @@ func (c *fuzzitClient) uploadFile(filePath string, storagePath string, contentTy } return nil } + +func (c *fuzzitClient) downloadFile(filePath string, storagePath string) error { + storageLink, err := c.getStorageLink(storagePath, "read") + if err != nil { + return err + } + + out, err := os.Create(filePath) + if err != nil { + return err + } + defer out.Close() + + resp, err := http.Get(storageLink) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != 200 { + return errors.New(resp.Status) + } + + _, err = io.Copy(out, resp.Body) + if err != nil { + return err + } + + return nil +} diff --git a/cmd/auth.go b/cmd/auth.go index 78eea5c..2c41e92 100644 --- a/cmd/auth.go +++ b/cmd/auth.go @@ -29,8 +29,7 @@ var authCmd = &cobra.Command{ Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { log.Println("Authenticating with fuzzit...") - c := client.NewFuzzitClient(args[0]) - err := c.ReAuthenticate(true) + _, err := client.NewFuzzitClient(args[0]) if err != nil { log.Fatal(err) } diff --git a/cmd/download.go b/cmd/download.go new file mode 100644 index 0000000..8446f37 --- /dev/null +++ b/cmd/download.go @@ -0,0 +1,83 @@ +/* +Copyright © 2019 fuzzit.dev, inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cmd + +import ( + "github.com/fuzzitdev/fuzzit/client" + "github.com/spf13/cobra" + "log" + "strings" +) + +// downloadCmd represents the download command +var downloadCmd = &cobra.Command{ + Use: "download [TARGET] [corpus|seed] [LOCAL_PATH]", + Short: "download seed/corpus", + Example: `./fuzzit download fuzzit/example-go corpus +./fuzzit download example-go corpus # This can work if you are already authenticated`, + Args: cobra.MinimumNArgs(2), + Run: func(cmd *cobra.Command, args []string) { + target := args[0] + resource := args[1] + + c, err := client.LoadFuzzitFromCache() + if err != nil { + log.Fatal(err) + } + + targetSplice := strings.Split(target, "/") + if len(targetSplice) > 2 { + log.Fatalf("[TARGET] can only be of type 'target' or 'project/target-name'.") + } else if len(targetSplice) == 2 { + target = targetSplice[1] + } + + if c.Org == "" { + if len(targetSplice) != 2 { + log.Fatalf("For unauthenticated requests [TARGET] should be 'project/target-name'") + } + c.Org = targetSplice[0] + } + + if resource == "corpus" { + localPath := "corpus" + if len(args) > 2 { + localPath = args[2] + } + log.Print("downloading corpus") + err := c.DownloadCorpus(localPath, target) + if err != nil { + log.Fatal(err) + } + } else if resource == "seed" { + localPath := "seed" + if len(args) > 2 { + localPath = args[2] + } + err := c.DownloadSeed(localPath, target) + if err != nil { + log.Fatal(err) + } + } else { + log.Fatalf("resource should be either corpus or seed") + } + + }, +} + +func init() { + rootCmd.AddCommand(downloadCmd) +} diff --git a/cmd/get.go b/cmd/get.go index 3d4c4eb..e205885 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -25,7 +25,7 @@ import ( var getCmd = &cobra.Command{ Use: "get ", Short: "get information about targets or jobs", - Long: `Example: + Example: ` ./fuzzit get targets # retrieve all targets ./fuzzit get targets/ # retrieve specific target ./fuzzit get targets//jobs # retrieve all jobs for target diff --git a/cmd/job.go b/cmd/job.go index 9fb8565..bf4b9f0 100644 --- a/cmd/job.go +++ b/cmd/job.go @@ -17,9 +17,9 @@ package cmd import ( "github.com/fuzzitdev/fuzzit/client" - "log" - "github.com/spf13/cobra" + "log" + "strings" ) // jobCmd represents the job command @@ -35,13 +35,29 @@ var jobCmd = &cobra.Command{ log.Fatalf("--type should be either fuzzing or sanity. Recieved: %s", newJob.Type) } - log.Println("Creating job") + log.Println("Creating job...") c, err := client.LoadFuzzitFromCache() if err != nil { log.Fatal(err) } - newJob.TargetId = args[0] - _, err = c.CreateJob(newJob, args[1:]) + + target := args[0] + targetSplice := strings.Split(args[0], "/") + if len(targetSplice) > 2 { + log.Fatalf("[TARGET] can only be of type 'target' or 'project/target-name'.") + } else if len(targetSplice) == 2 { + target = targetSplice[1] + c.Org = targetSplice[0] + } + + newJob.TargetId = target + + if newJob.Local { + err = c.CreateLocalJob(newJob, args[1:]) + } else { + _, err = c.CreateJob(newJob, args[1:]) + } + if err != nil { log.Fatal(err) } @@ -53,6 +69,7 @@ func init() { createCmd.AddCommand(jobCmd) jobCmd.Flags().StringVar(&newJob.Type, "type", "fuzzing", "fuzzing/sanity") + jobCmd.Flags().BoolVar(&newJob.Local, "local", false, "run fuzzing/sanity locally in a docker") jobCmd.Flags().Uint16Var(&newJob.Parallelism, "cpus", 1, "number of cpus to use (only relevant for fuzzing job)") jobCmd.Flags().StringVar(&newJob.Revision, "revision", "", "Revision tag of fuzzer") jobCmd.Flags().StringVar(&newJob.Branch, "branch", "", "Branch of the fuzzer") diff --git a/cmd/root.go b/cmd/root.go index 473dfac..3c768c3 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -24,7 +24,7 @@ import ( var cfgFile string var rootCmd = &cobra.Command{ - Use: "Fuzzit", + Use: "fuzzit", Short: "Continuous fuzzing made simple CLI", Version: "2.3.0", } diff --git a/docker/debian/stretch/llvm8/Dockerfile b/docker/debian/stretch/llvm8/Dockerfile index 63bfb7b..7d39122 100644 --- a/docker/debian/stretch/llvm8/Dockerfile +++ b/docker/debian/stretch/llvm8/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:stretch +FROM debian:stretch-slim LABEL maintainer="Fuzzit.dev, inc." @@ -8,4 +8,8 @@ RUN echo "deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch-8 main" >> /et RUN echo "deb-src http://apt.llvm.org/stretch/ llvm-toolchain-stretch-8 main" >> /etc/apt/sources.list RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - RUN apt-get update && apt-get install -y libllvm-8-ocaml-dev libllvm8 llvm-8 llvm-8-dev llvm-8-doc llvm-8-examples llvm-8-runtime -RUN ln -s /usr/lib/llvm-8/bin/llvm-symbolizer /bin/llvm-symbolizer \ No newline at end of file +RUN ln -s /usr/lib/llvm-8/bin/llvm-symbolizer /bin/llvm-symbolizer + +WORKDIR /app + +ADD run.sh /app/run.sh diff --git a/docker/debian/stretch/llvm9/Dockerfile b/docker/debian/stretch/llvm9/Dockerfile index 4a0abd9..f48b788 100644 --- a/docker/debian/stretch/llvm9/Dockerfile +++ b/docker/debian/stretch/llvm9/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:stretch +FROM debian:stretch-slim LABEL maintainer="Fuzzit.dev, inc." @@ -8,4 +8,10 @@ RUN echo "deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch main" >> /etc/ RUN echo "deb-src http://apt.llvm.org/stretch/ llvm-toolchain-stretch main" >> /etc/apt/sources.list RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - RUN apt-get update && apt-get install -y libllvm-9-ocaml-dev libllvm9 llvm-9 llvm-9-dev llvm-9-doc llvm-9-examples llvm-9-runtime -RUN ln -s /usr/lib/llvm-9/bin/llvm-symbolizer /bin/llvm-symbolizer \ No newline at end of file +RUN ln -s /usr/lib/llvm-9/bin/llvm-symbolizer /bin/llvm-symbolizer + +WORKDIR /app + +ADD run.sh /app/run.sh + +CMD /app/run.sh diff --git a/docker/run.sh b/docker/run.sh new file mode 100755 index 0000000..2602b5b --- /dev/null +++ b/docker/run.sh @@ -0,0 +1,41 @@ +#!/bin/sh +set -x + +mkdir corpus_dir +mkdir seed_dir +touch corpus_dir/empty # This to avoid fuzzer stuck +touch seed_dir/empty # This is to avoid fuzzer stuck + +wget -O corpus.tar.gz $CORPUS_LINK || rm -f corpus.tar.gz # remove empty file if corpus doesn't exist +if test -f "corpus.tar.gz"; then + tar -xzvf corpus.tar.gz -C corpus_dir +else + echo "corpus is still empty. continuing without..." +fi + +wget -O seed $CORPUS_LINK || rm -f seed +if test -f "seed"; then + case $(file --mime-type -b seed) in + application/x-gzip) + tar -xzvf seed -C seed_dir + ;; + application/zip) + unzip seed -d seed_dir + ;; + *) + echo "seed in unknown format. Please upload seed in tar.gz or zip format. If you did and you believe it's + a bug, please contact support@fuzzit.dev" + exit 1 + ;; + esac +else + echo "seed corpus is empty. continuing without..." +fi +ls -la +if test -f "fuzzer"; then + echo "running fuzzer" + chmod a+x fuzzer + ./fuzzer -exact_artifact_path=./artifact -print_final_stats=1 ./seed_dir/* ./corpus_dir/* $ARGS +else + echo "failed to locate fuzzer. does 'fuzzer' executable exist in the archive?" +fi \ No newline at end of file diff --git a/go.mod b/go.mod index 2581dfe..cbce012 100644 --- a/go.mod +++ b/go.mod @@ -4,17 +4,25 @@ go 1.12 require ( cloud.google.com/go v0.43.0 + github.com/containerd/containerd v1.2.7 // indirect + github.com/docker/distribution v2.7.1+incompatible // indirect + github.com/docker/docker v0.7.3-0.20190802030316-4e83c90ae836 + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.4.0 // indirect github.com/dsnet/compress v0.0.1 // indirect github.com/golang/snappy v0.0.1 // indirect github.com/google/uuid v1.1.1 github.com/mholt/archiver v3.1.1+incompatible github.com/mitchellh/go-homedir v1.1.0 github.com/nwaples/rardecode v1.0.0 // indirect + github.com/opencontainers/go-digest v1.0.0-rc1 // indirect + github.com/opencontainers/image-spec v1.0.1 // indirect github.com/pierrec/lz4 v2.2.5+incompatible // indirect github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.4.0 github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 google.golang.org/api v0.7.0 + google.golang.org/grpc v1.21.1 gopkg.in/urfave/cli.v1 v1.20.0 ) diff --git a/go.sum b/go.sum index 529f19b..974bee1 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/containerd/containerd v1.2.7 h1:8lqLbl7u1j3MmiL9cJ/O275crSq7bfwUayvvatEupQk= +github.com/containerd/containerd v1.2.7/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= 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-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= @@ -23,6 +25,18 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/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 v0.0.0-20170601211448-f5ec1e2936dc h1:S8H7eaOGNNOZ83UGSgpgv4FlCtoBTJxG6GzFNkwJr5Q= +github.com/docker/docker v0.0.0-20170601211448-f5ec1e2936dc/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v0.7.3-0.20190802030316-4e83c90ae836 h1:+CrvVfB8NpGBPgGHd1wti3jk6lxPOBl64py2db9bHFE= +github.com/docker/docker v0.7.3-0.20190802030316-4e83c90ae836/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo= +github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= @@ -34,6 +48,7 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 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/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 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= @@ -94,10 +109,15 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs= github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +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/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pierrec/lz4 v2.2.5+incompatible h1:xOYu2+sKj87pJz7V+I7260354UlcRyAZUGhMCToTzVw= github.com/pierrec/lz4 v2.2.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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= @@ -111,6 +131,7 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z 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/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 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= @@ -148,6 +169,7 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=