diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2e57d44 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +nomad-runtime-driver-skeleton diff --git a/Makefile b/Makefile deleted file mode 100644 index 45ba444..0000000 --- a/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -NAME := nomad-driver-skeleton -PKG := github.com/hashicorp/$(NAME) - -CGO_ENABLED := 0 - -# Set any default go build tags. -BUILDTAGS := -# Set an output prefix, which is the local directory if not specified -PREFIX?=$(shell pwd) - -GOOSARCHES=linux/amd64 - -GO=go - -.PHONY: build -build: $(NAME) ## Builds a dynamic executable or package. - -$(NAME): $(wildcard *.go) $(wildcard */*.go) - @echo "+ $@" - $(V)GO111MODULE=on GOOS=linux $(GO) build -tags "$(BUILDTAGS)" ${GO_LDFLAGS} -o $(NAME) ./cmd/driver/main.go - -.PHONY: static -static: ## Builds a static executable. - @echo "+ $@" - CGO_ENABLED=$(CGO_ENABLED) $(GO) build \ - -tags "$(BUILDTAGS) static_build" \ - ${GO_LDFLAGS_STATIC} -o $(NAME) . - -all: clean build fmt lint test vet - -.PHONY: fmt -fmt: ## Verifies all files have been `gofmt`ed. - @echo "+ $@" - @gofmt -s -l . | grep -v '.pb.go:' | grep -v vendor | tee /dev/stderr - -.PHONY: test -test: ## Runs the go tests. - @echo "+ $@" - @$(GO) test -v -tags "$(BUILDTAGS) cgo" $(shell $(GO) list ./... | grep -v vendor) - -.PHONY: vet -vet: ## Verifies `go vet` passes. - @echo "+ $@" - @$(GO) vet -printfuncs Error,ErrorDepth,Errorf,Errorln,Exit,ExitDepth,Exitf,Exitln,Fatal,FatalDepth,Fatalf,Fatalln,Info,InfoDepth,Infof,Infoln,Warning,WarningDepth,Warningf,Warningln -all ./... - -.PHONY: clean -clean: ## Cleanup any build binaries or packages. - @echo "+ $@" - $(RM) $(NAME) - $(RM) -r $(BUILDDIR) - -dep: - $(V)GO111MODULE=on go mod download - $(V)GO111MODULE=on go mod vendor - $(V)GO111MODULE=on go mod tidy diff --git a/README.md b/README.md index 0b236e2..aabf13c 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,49 @@ -# nomad-driver-skeleton -This project is a [Hashicorp Nomad](https://www.nomadproject.io/) runtime driver plugin skeleton implementation. -You can clone and modify this to get started writing your -own driver plugin. +nomad-runtime-driver-skeleton +========== -Note that this is only a skeleton to help you get started. The Makefile only builds -for linux/amd64 +This project is a [Hashicorp Nomad](https://www.nomadproject.io/) [task driver +plugin](https://www.nomadproject.io/docs/drivers/index.html) skeleton +implementation. You can clone and modify this to get started writing your own +task driver plugin. Requirements ------------- +------------------- - [Nomad](https://www.nomadproject.io/downloads.html) v0.9.5+ - [Go](https://golang.org/doc/install) v1.11+ (to build the plugin) Get Started ------------ +------------------- -Clone repository to: `$GOPATH/src/github.com/hashicorp/nomad-driver-skeleton - -Copy the files into your new driver's package and change package names as needed. +Download the project module: ```sh -$ cd $GOPATH/src/github.com/hashicorp/nomad-driver-skeleton -$ make dep -$ make build +go get github.com/hashicorp/nomad-skeleton-task-driver-plugin ``` -Requirements ------------- +Follow the comments marked with a `TODO` tag to implement your driver's logic. +For more information consult the Nomad documentation on +[plugins](https://www.nomadproject.io/docs/internals/plugins/index.html). -[Go](http://www.golang.org) should be installed on your machine. -You will also need to correctly setup a [GOPATH](http://golang.org/doc/code.html#GOPATH) -and add `$GOPATH/bin` to your `$PATH`. - -To compile the driver plugin, run `make build`. This will build the plugin and put the task driver binary under the -NOMAD plugin dir, which by default is located under `/plugins/`. -Check Nomad `-data-dir` and `-plugin-dir` flags for more information. +The initial state of the skeleton is a simple task that outputs a greeting. You +can try it out by starting a Nomad server and client and running the job +provided in the `config` folder: ```sh -$ make build +go build +nomad agent -dev -config=./config/client.hcl -plugin-dir=$(PWD) + +# in another shell +nomad run ./config/example.nomad ``` -Makefile and project module structure were inspired by [Sylab's singularity]() driver. +You should rename the project's root folder and the `hello` module to fit your +own driver. Code Organization ------------------ -The skeleton driver comes with an in memory task state store (see state.go). -This in-memory map is for storing runtime details (like a process id). The details -of what to store is left to the implementation of the runtime driver. -Fields stored in the base implementation in this skeleton can be found in the `taskHandle` struct. +------------------- +The skeleton driver comes with an in memory task state store (see +`hello/state.go`). This in-memory map is for storing runtime details (like a +process id). The details of what to store is left to the implementation of the +task driver. Fields stored in the base implementation in this skeleton can be +found in the `taskHandle` struct. diff --git a/config/client.hcl b/config/client.hcl new file mode 100644 index 0000000..654b5e1 --- /dev/null +++ b/config/client.hcl @@ -0,0 +1,7 @@ +log_level = "TRACE" + +plugin "nomad-runtime-driver-skeleton" { + config { + shell = "bash" + } +} diff --git a/config/example.nomad b/config/example.nomad new file mode 100644 index 0000000..cf7e73c --- /dev/null +++ b/config/example.nomad @@ -0,0 +1,14 @@ +job "example" { + datacenters = ["dc1"] + type = "batch" + + group "example" { + task "hello-world" { + driver = "hello-world-example" + + config { + greeting = "hello" + } + } + } +} diff --git a/go.mod b/go.mod index 30338d6..1941124 100644 --- a/go.mod +++ b/go.mod @@ -1,49 +1,85 @@ -module github.com/hashicorp/nomad-driver-skeleton +module github.com/hashicorp/nomad-runtime-driver-skeleton + +go 1.12 require ( github.com/LK4D4/joincontext v0.0.0-20171026170139-1724345da6d5 // indirect - github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect + github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/fsnotify/fsnotify v1.4.7 // indirect - github.com/go-ole/go-ole v1.2.2 // indirect - github.com/golang/snappy v0.0.1 // indirect - github.com/gorhill/cronexpr v0.0.0-20140423231348-a557574d6c02 // indirect - github.com/hashicorp/consul v1.0.7 // indirect - github.com/hashicorp/go-hclog v0.8.0 - github.com/hashicorp/go-plugin v0.0.0-20190212232519-b838ffee39ce // indirect - github.com/hashicorp/go-retryablehttp v0.5.3 // indirect - github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 // indirect - github.com/hashicorp/go-uuid v1.0.1 // indirect - github.com/hashicorp/go-version v1.0.0 // indirect - github.com/hashicorp/hcl v0.0.0-20161101180025-6e968a3fcdcb // indirect - github.com/hashicorp/memberlist v0.1.3 // indirect - github.com/hashicorp/nomad v0.9.0-rc2 - github.com/hashicorp/raft v0.0.0-20190104133720-9c733b2b7f53 // indirect - github.com/hashicorp/serf v0.0.0-20180119224300-b6017ae61f44 // indirect - github.com/hashicorp/vault v1.1.0 // indirect - github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect - github.com/hpcloud/tail v0.0.0-20180514194441-a1dbeea552b7 // indirect - github.com/kr/pretty v0.1.0 // indirect - github.com/mitchellh/copystructure v0.0.0-20170525013902-d23ffcb85de3 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/go-testing-interface v1.0.0 // indirect - github.com/mitchellh/hashstructure v0.0.0-20160118175604-1ef5c71b025a // indirect - github.com/mitchellh/mapstructure v1.1.2 // indirect - github.com/mitchellh/reflectwalk v1.0.0 // indirect - github.com/pascaldekloe/goe v0.1.0 // indirect - github.com/pierrec/lz4 v2.0.5+incompatible // indirect - github.com/pkg/errors v0.8.1 // indirect - github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 // indirect - github.com/shirou/gopsutil v0.0.0-20190131151121-071446942108 // indirect + github.com/go-ole/go-ole v1.2.4 // indirect + github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 // indirect + github.com/hashicorp/consul-template v0.23.0 + github.com/hashicorp/go-hclog v0.9.1 + github.com/hashicorp/go-plugin v1.0.1 + github.com/hashicorp/go-version v1.2.0 // indirect + github.com/hashicorp/nomad v0.10.0 + github.com/hashicorp/raft v1.1.1 // indirect + github.com/hpcloud/tail v1.0.0 // indirect + github.com/kr/pretty v0.1.0 + github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b // indirect + github.com/shirou/gopsutil v0.0.0-00010101000000-000000000000 // indirect github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect - github.com/ugorji/go v0.0.0-20170620060102-0053ebfd9d0e // indirect - github.com/vmihailenco/msgpack v4.0.2+incompatible // indirect - github.com/zclconf/go-cty v0.0.0-20180718220526-02bd58e97b57 // indirect - golang.org/x/sys v0.0.0-20190220154126-629670e5acc5 // indirect - golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect - google.golang.org/appengine v1.4.0 // indirect - google.golang.org/genproto v0.0.0-20190219182410-082222b4a5c5 // indirect - google.golang.org/grpc v1.18.0 // indirect - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect - gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect + github.com/ugorji/go v0.0.0-00010101000000-000000000000 // indirect + github.com/zclconf/go-cty v1.1.0 // indirect + golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf // indirect + google.golang.org/grpc v1.22.0 + gopkg.in/fsnotify.v1 v1.4.7 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect ) + +// don't use shirou/gopsutil, use the hashicorp fork +replace github.com/shirou/gopsutil => github.com/hashicorp/gopsutil v0.0.0-20180427102116-62d5761ddb7d + +// don't use ugorji/go, use the hashicorp fork +replace github.com/ugorji/go => github.com/hashicorp/go-msgpack v0.0.0-20190927083313-23165f7bc3c2 + +// fix the version of hashicorp/go-msgpack to 96ddbed8d05b +replace github.com/hashicorp/go-msgpack => github.com/hashicorp/go-msgpack v0.0.0-20191101193846-96ddbed8d05b + +//require ( +// github.com/LK4D4/joincontext v0.0.0-20171026170139-1724345da6d5 // indirect +// github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect +// github.com/fsnotify/fsnotify v1.4.7 // indirect +// github.com/go-ole/go-ole v1.2.2 // indirect +// github.com/golang/snappy v0.0.1 // indirect +// github.com/gorhill/cronexpr v0.0.0-20140423231348-a557574d6c02 // indirect +// github.com/hashicorp/consul v1.0.7 // indirect +// github.com/hashicorp/go-hclog v0.8.0 +// github.com/hashicorp/go-plugin v0.0.0-20190212232519-b838ffee39ce // indirect +// github.com/hashicorp/go-retryablehttp v0.5.3 // indirect +// github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 // indirect +// github.com/hashicorp/go-uuid v1.0.1 // indirect +// github.com/hashicorp/go-version v1.0.0 // indirect +// github.com/hashicorp/hcl v0.0.0-20161101180025-6e968a3fcdcb // indirect +// github.com/hashicorp/memberlist v0.1.3 // indirect +// github.com/hashicorp/nomad v0.9.0-rc2 +// github.com/hashicorp/raft v0.0.0-20190104133720-9c733b2b7f53 // indirect +// github.com/hashicorp/serf v0.0.0-20180119224300-b6017ae61f44 // indirect +// github.com/hashicorp/vault v1.1.0 // indirect +// github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect +// github.com/hpcloud/tail v0.0.0-20180514194441-a1dbeea552b7 // indirect +// github.com/kr/pretty v0.1.0 // indirect +// github.com/mitchellh/copystructure v0.0.0-20170525013902-d23ffcb85de3 // indirect +// github.com/mitchellh/go-homedir v1.1.0 // indirect +// github.com/mitchellh/go-testing-interface v1.0.0 // indirect +// github.com/mitchellh/hashstructure v0.0.0-20160118175604-1ef5c71b025a // indirect +// github.com/mitchellh/mapstructure v1.1.2 // indirect +// github.com/mitchellh/reflectwalk v1.0.0 // indirect +// github.com/pascaldekloe/goe v0.1.0 // indirect +// github.com/pierrec/lz4 v2.0.5+incompatible // indirect +// github.com/pkg/errors v0.8.1 // indirect +// github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 // indirect +// github.com/shirou/gopsutil v0.0.0-20190131151121-071446942108 // indirect +// github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect +// github.com/ugorji/go v0.0.0-20170620060102-0053ebfd9d0e // indirect +// github.com/vmihailenco/msgpack v4.0.2+incompatible // indirect +// github.com/zclconf/go-cty v0.0.0-20180718220526-02bd58e97b57 // indirect +// golang.org/x/sys v0.0.0-20190220154126-629670e5acc5 // indirect +// golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect +// google.golang.org/appengine v1.4.0 // indirect +// google.golang.org/genproto v0.0.0-20190219182410-082222b4a5c5 // indirect +// google.golang.org/grpc v1.18.0 // indirect +// gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect +// gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect +// gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect +//) diff --git a/go.sum b/go.sum index 42c6daa..c54e2d0 100644 --- a/go.sum +++ b/go.sum @@ -1,94 +1,199 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/LK4D4/joincontext v0.0.0-20171026170139-1724345da6d5 h1:U7q69tqXiCf6m097GRlNQB0/6SI1qWIOHYHhCEvDxF4= github.com/LK4D4/joincontext v0.0.0-20171026170139-1724345da6d5/go.mod h1:nxQPcNPR/34g+HcK2hEsF99O+GJgIkW/OmPl8wtzhmk= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM= +github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/frankban/quicktest v1.4.0/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-ole/go-ole v1.2.2 h1:QNWhweRd9D5Py2rRVboZ2L4SEoW/dyraWJCc8bgS8kE= github.com/go-ole/go-ole v1.2.2/go.mod h1:pnvuG7BrDMZ8ifMurTQmxwhQM/odqm9sSqNe5BUI7v4= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +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/gorhill/cronexpr v0.0.0-20140423231348-a557574d6c02 h1:Spo+4PFAGDqULAsZ7J69MOxq4/fwgZ0zvmDTBqpq7yU= github.com/gorhill/cronexpr v0.0.0-20140423231348-a557574d6c02/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= +github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 h1:f0n1xnMSmBLzVfsMMvriDyA75NB/oBgILX2GcHXIQzY= +github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= github.com/hashicorp/consul v1.0.7 h1:GuHjalgSkzFdlBVxfKzlLJJ6bT4VCvAXV678UWpuJbw= github.com/hashicorp/consul v1.0.7/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI= +github.com/hashicorp/consul v1.6.2 h1:OjnFfc1vHPLtwrYAMC9HlJ4WTSmgBTq2erWuBERm3hY= +github.com/hashicorp/consul-template v0.23.0 h1:cEQ3eEK8XRj0DdvLAcqNd/eSV2IAnSj+zPJeo/5C330= +github.com/hashicorp/consul-template v0.23.0/go.mod h1:KcTEopo2kCp7kww0d4oG7d3oX2Uou4hzb1Rs/wY9TVI= +github.com/hashicorp/consul/api v1.2.0 h1:oPsuzLp2uk7I7rojPKuncWbZ+m5TMoD4Ivs+2Rkeh4Y= +github.com/hashicorp/consul/api v1.2.0/go.mod h1:1SIkFYi2ZTXUE5Kgt179+4hH33djo11+0Eo2XgTAtkw= +github.com/hashicorp/consul/sdk v0.2.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-gatedio v0.5.0/go.mod h1:Lr3t8L6IyxD3DAeaUxGcgl2JnRUpWMCsmBl4Omu/2t4= github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-hclog v0.8.0 h1:z3ollgGRg8RjfJH6UVBaG54R70GFd++QOkvnJH3VSBY= github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.9.1 h1:9PZfAcVEvez4yhLH2TBU64/h/z4xlFI80cWXRrxuKuM= +github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc= +github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.0.0-20190927083313-23165f7bc3c2 h1:kZFZ8oAS1Rfk3QG3dgd0iiBNnOf0cQliaWZgfRz1eQw= +github.com/hashicorp/go-msgpack v0.0.0-20190927083313-23165f7bc3c2/go.mod h1:CNnb6ZvPKQMR+Hz6QI76TRCBNgyJIxEmTBn+1u8HELw= +github.com/hashicorp/go-msgpack v0.0.0-20191101193846-96ddbed8d05b h1:lB+3FXrgs94Mz066O5Yz59m3l/O0uEsf2jPiZyUpKTU= +github.com/hashicorp/go-msgpack v0.0.0-20191101193846-96ddbed8d05b/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4= github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-plugin v0.0.0-20190212232519-b838ffee39ce h1:I3KJUf8jyMubLFeHit2ibr9YeVxJX2CXMXVM6xlB+Qk= github.com/hashicorp/go-plugin v0.0.0-20190212232519-b838ffee39ce/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= +github.com/hashicorp/go-plugin v1.0.1 h1:4OtAfUGbnKC6yS48p0CtMX2oFYtzFZVv6rok3cRWgnE= +github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= github.com/hashicorp/go-retryablehttp v0.5.3 h1:QlWt0KvWT0lq8MFppF9tsJGF+ynG7ztc2KIPhzRGk7s= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE= +github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 h1:VBj0QYQ0u2MCJzBfeYXGexnAl17GsH1yidnoxCqqD9E= github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90/go.mod h1:o4zcYY1e0GEZI6eSEr+43QDYmuGglw1qSO6qdHUHCgg= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.1 h1:DMo4fmknnz0E0evoNYnV48RjWndOsmd6OW+09R3cEP8= +github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8= github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/gopsutil v0.0.0-20180427102116-62d5761ddb7d h1:SFth5WDiMWzND4B4iAGynG5MUhlLeKPaojk8u2qMVQ0= +github.com/hashicorp/gopsutil v0.0.0-20180427102116-62d5761ddb7d/go.mod h1:Mz8xr0ujmCW1qsmWMIUDOc+RgaN9QMvrgnBzQF12nYM= github.com/hashicorp/hcl v0.0.0-20161101180025-6e968a3fcdcb h1:S76lj//ZzHoMTiOOi3P9NcBQhoUxi4ZnYTcGqQnkCcE= github.com/hashicorp/hcl v0.0.0-20161101180025-6e968a3fcdcb/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/nomad v0.9.0-rc2 h1:sN6Dy8ve/fwrWFSNzYSikYaXqqoUm1o4pqtJIOTaRQo= github.com/hashicorp/nomad v0.9.0-rc2/go.mod h1:WRaKjdO1G2iqi86TvTjIYtKTyxg4pl7NLr9InxtWaI0= +github.com/hashicorp/nomad v0.10.0 h1:GDoqEklnNV9McCFp7PmcD96xGgSzKLfpJtJM96cOTH0= +github.com/hashicorp/nomad v0.10.0/go.mod h1:WRaKjdO1G2iqi86TvTjIYtKTyxg4pl7NLr9InxtWaI0= github.com/hashicorp/raft v0.0.0-20190104133720-9c733b2b7f53 h1:Kcs5PApYrQyn+RLWUvk3eQhKNGDj8M/r1mt0X8p9HAA= github.com/hashicorp/raft v0.0.0-20190104133720-9c733b2b7f53/go.mod h1:DVSAWItjLjTOkVbSpWQ0j0kUADIvDaCtBxIcbNAQLkI= +github.com/hashicorp/raft v1.1.1 h1:HJr7UE1x/JrJSc9Oy6aDBHtNHUUBHjcQjTgvUVihoZs= +github.com/hashicorp/raft v1.1.1/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= +github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= github.com/hashicorp/serf v0.0.0-20180119224300-b6017ae61f44 h1:uY64+3NuMxFyc9+nchedxCBabBhU67QF+ymrIedtexE= github.com/hashicorp/serf v0.0.0-20180119224300-b6017ae61f44/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE= +github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.8.3 h1:MWYcmct5EtKz0efYooPcL0yNkem+7kWxqXDi/UIh+8k= +github.com/hashicorp/serf v0.8.3/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k= github.com/hashicorp/vault v1.1.0 h1:v79NUgO5xCZnXVzUkIqFOXtP8YhpnHAi1fk3eo9cuOE= github.com/hashicorp/vault v1.1.0/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0= +github.com/hashicorp/vault/api v1.0.4 h1:j08Or/wryXT4AcHj1oCbMd7IijXcKzYUGw59LGu9onU= +github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= +github.com/hashicorp/vault/api v1.0.5-0.20190730042357-746c0b111519/go.mod h1:i9PKqwFko/s/aihU1uuHGh/FaQS+Xcgvd9dvnfAvQb0= +github.com/hashicorp/vault/sdk v0.1.13 h1:mOEPeOhT7jl0J4AMl1E705+BcmeRs1VmKNb9F0sMLy8= +github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= +github.com/hashicorp/vault/sdk v0.1.14-0.20190730042320-0dc007d98cc8/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v0.0.0-20180514194441-a1dbeea552b7 h1:lus8hJKTrh146vNoUWNHv2F1jdtsud5ajNL0/YndJUw= github.com/hpcloud/tail v0.0.0-20180514194441-a1dbeea552b7/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v0.0.0-20170525013902-d23ffcb85de3 h1:dECZqiJYhKdj9QlLpiQaRDXHDXRTdiyZI3owdDGhlYY= github.com/mitchellh/copystructure v0.0.0-20170525013902-d23ffcb85de3/go.mod h1:eOsF2yLPlBBJPvD+nhl5QMTBSOBbOph6N7j/IDUw7PY= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b h1:9+ke9YJ9KGWw5ANXK6ozjoK47uI3uNbXv4YVINBnGm8= +github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/hashstructure v0.0.0-20160118175604-1ef5c71b025a h1:2p2+qxgbcNvMPt/Q/Nfd0mEHJokUf7rHAYDVQH1dKpM= github.com/mitchellh/hashstructure v0.0.0-20160118175604-1ef5c71b025a/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= +github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= +github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= @@ -100,12 +205,22 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.2.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 h1:7YvPJVmEeFHR1Tj9sZEYsmarJEQfMVYpd/Vyy/A8dqE= github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shirou/gopsutil v0.0.0-20190131151121-071446942108 h1:XXgDK65TPH+Qbo2sdYHldM5avclwThBXVYZHxroFkTQ= @@ -116,50 +231,105 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go v0.0.0-20170620060102-0053ebfd9d0e h1:Eurc/1QbldPTy6eU1WSHOH1vuFc7BAUdKDWSsZd4ieo= github.com/ugorji/go v0.0.0-20170620060102-0053ebfd9d0e/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= +github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI= +github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.2+incompatible h1:6ujmmycMfB62Mwv2N4atpnf8CKLSzhgodqMenpELKIQ= github.com/vmihailenco/msgpack v4.0.2+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/zclconf/go-cty v0.0.0-20180718220526-02bd58e97b57 h1:FWjfWQtNK83IJMe58FfMCSNkySe0PO9TvrQyLOKcPxw= github.com/zclconf/go-cty v0.0.0-20180718220526-02bd58e97b57/go.mod h1:LnDKxj8gN4aatfXUqmUNooaDjvmDcLPbAN3hYBIVoJE= +github.com/zclconf/go-cty v1.1.0 h1:uJwc9HiBOCpoKIObTQaLR+tsEXx1HBHnOsOOpcdhZgw= +github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf h1:fnPsqIDRbCSgumaMCRpoIoF2s4qxv0xSSS0BVZUE/ss= +golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 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/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180811021610-c39426892332/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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953 h1:LuZIitY8waaxUfNIdtajyE/YzA/zyf0YxXG27VpLrkg= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/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-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 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 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= 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/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/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-20190220154126-629670e5acc5 h1:3Nsfe5Xa1wTt01QxlAFIY5j9ycDtS+d7mhvI8ZY5bn0= golang.org/x/sys v0.0.0-20190220154126-629670e5acc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5 h1:sM3evRHxE/1RuMe1FYAL3j7C7fUfIjkbE+NiDAYUF8U= +golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190730183949-1393eb018365 h1:SaXEMXhWzMJThc05vu6uh61Q245r4KaWMrsTedk0FDc= +golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +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 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/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-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-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190219182410-082222b4a5c5 h1:SdCO7As+ChE1iV3IjBleIIWlj8VjZWuYEUF5pjELOOQ= google.golang.org/genproto v0.0.0-20190219182410-082222b4a5c5/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107 h1:xtNn7qFlagY2mQNFHMSRPjT2RkOV4OXM7P5TVy9xATo= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.18.0 h1:IZl7mfBGfbhYx2p2rKRtYgDFw6SBz+kclmxYrCksPPA= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify/fsnotify.v1 v1.4.7 h1:XNNYLJHt73EyYiCZi6+xjupS9CpvmiDgjPTAjrBlQbo= gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE= +gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +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= diff --git a/hello/driver.go b/hello/driver.go new file mode 100644 index 0000000..67f557d --- /dev/null +++ b/hello/driver.go @@ -0,0 +1,640 @@ +package hello + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "time" + + "github.com/hashicorp/consul-template/signals" + "github.com/hashicorp/go-hclog" + log "github.com/hashicorp/go-hclog" + "github.com/hashicorp/nomad/drivers/shared/eventer" + "github.com/hashicorp/nomad/drivers/shared/executor" + "github.com/hashicorp/nomad/plugins/base" + "github.com/hashicorp/nomad/plugins/drivers" + "github.com/hashicorp/nomad/plugins/shared/hclspec" + "github.com/hashicorp/nomad/plugins/shared/structs" +) + +const ( + // pluginName is the name of the plugin + // this is used for logging and (along with the version) for uniquely + // identifying plugin binaries fingerprinted by the client + pluginName = "hello-world-example" + + // pluginVersion allows the client to identify and use newer versions of + // an installed plugin + pluginVersion = "v0.1.0" + + // fingerprintPeriod is the interval at which the plugin will send + // fingerprint responses + fingerprintPeriod = 30 * time.Second + + // taskHandleVersion is the version of task handle which this plugin sets + // and understands how to decode + // this is used to allow modification and migration of the task schema + // used by the plugin + taskHandleVersion = 1 +) + +var ( + // pluginInfo describes the plugin + pluginInfo = &base.PluginInfoResponse{ + Type: base.PluginTypeDriver, + PluginApiVersions: []string{drivers.ApiVersion010}, + PluginVersion: pluginVersion, + Name: pluginName, + } + + // configSpec is the specification of the plugin's configuration + // this is used to validate the configuration specified for the plugin + // on the client. + // this is not global, but can be specified on a per-client basis. + configSpec = hclspec.NewObject(map[string]*hclspec.Spec{ + // TODO: define plugin's agent configuration schema. + // + // The schema should be defined using HCL specs and it will be used to + // validate the agent configuration provided by the user in the + // `plugin` stanza (https://www.nomadproject.io/docs/configuration/plugin.html). + // + // For example, for the schema below a valid configuration would be: + // + // plugin "hello-driver-plugin" { + // config { + // shell = "fish" + // } + // } + "shell": hclspec.NewDefault( + hclspec.NewAttr("shell", "string", false), + hclspec.NewLiteral(`"bash"`), + ), + }) + + // taskConfigSpec is the specification of the plugin's configuration for + // a task + // this is used to validated the configuration specified for the plugin + // when a job is submitted. + taskConfigSpec = hclspec.NewObject(map[string]*hclspec.Spec{ + // TODO: define plugin's task configuration schema + // + // The schema should be defined using HCL specs and it will be used to + // validate the task configuration provided by the user when they + // submit a job. + // + // For example, for the schema below a valid task would be: + // job "example" { + // group "example" { + // task "say-hi" { + // driver = "hello-driver-plugin" + // config { + // greeting = "Hi" + // } + // } + // } + // } + "greeting": hclspec.NewDefault( + hclspec.NewAttr("greeting", "string", false), + hclspec.NewLiteral(`"Hello, World!"`), + ), + }) + + // capabilities indicates what optional features this driver supports + // this should be set according to the target run time. + capabilities = &drivers.Capabilities{ + // TODO: set plugin's capabilities + // + // The plugin's capabilities signal Nomad which extra functionalities + // are supported. For a list of available options check the docs page: + // https://godoc.org/github.com/hashicorp/nomad/plugins/drivers#Capabilities + SendSignals: true, + Exec: false, + } +) + +// Config contains configuration information for the plugin +type Config struct { + // TODO: create decoded plugin configuration struct + // + // This struct is the decoded version of the schema defined in the + // configSpec variable above. It's used to convert the HCL configuration + // passed by the Nomad agent into Go contructs. + Shell string `codec:"shell"` +} + +// TaskConfig contains configuration information for a task that runs with +// this plugin +type TaskConfig struct { + // TODO: create decoded plugin task configuration struct + // + // This struct is the decoded version of the schema defined in the + // taskConfigSpec variable above. It's used to convert the string + // configuration for the task into Go contructs. + Greeting string `codec:"greeting"` +} + +// TaskState is the runtime state which is encoded in the handle returned to +// Nomad client. +// This information is needed to rebuild the task state and handler during +// recovery. +type TaskState struct { + ReattachConfig *structs.ReattachConfig + TaskConfig *drivers.TaskConfig + StartedAt time.Time + + // TODO: add any extra important values that must be persisted in order + // to restore a task. + // + // The plugin keeps track of its running tasks in a in-memory data + // structure. If the plugin crashes, this data will be lost, so Nomad + // will respawn a new instance of the plugin and try to restore its + // in-memory representation of the running tasks using the RecoverTask() + // method below. + Pid int +} + +// HelloDriverPlugin is an example driver plugin. When provisioned in a job, +// the taks will output a greet specified by the user. +type HelloDriverPlugin struct { + // eventer is used to handle multiplexing of TaskEvents calls such that an + // event can be broadcast to all callers + eventer *eventer.Eventer + + // config is the plugin configuration set by the SetConfig RPC + config *Config + + // nomadConfig is the client config from Nomad + nomadConfig *base.ClientDriverConfig + + // tasks is the in memory datastore mapping taskIDs to driver handles + tasks *taskStore + + // ctx is the context for the driver. It is passed to other subsystems to + // coordinate shutdown + ctx context.Context + + // signalShutdown is called when the driver is shutting down and cancels + // the ctx passed to any subsystems + signalShutdown context.CancelFunc + + // logger will log to the Nomad agent + logger log.Logger +} + +// NewPlugin returns a new example driver plugin +func NewPlugin(logger log.Logger) drivers.DriverPlugin { + ctx, cancel := context.WithCancel(context.Background()) + logger = logger.Named(pluginName) + + return &HelloDriverPlugin{ + eventer: eventer.NewEventer(ctx, logger), + config: &Config{}, + tasks: newTaskStore(), + ctx: ctx, + signalShutdown: cancel, + logger: logger, + } +} + +// PluginInfo returns information describing the plugin. +func (d *HelloDriverPlugin) PluginInfo() (*base.PluginInfoResponse, error) { + return pluginInfo, nil +} + +// ConfigSchema returns the plugin configuration schema. +func (d *HelloDriverPlugin) ConfigSchema() (*hclspec.Spec, error) { + return configSpec, nil +} + +// SetConfig is called by the client to pass the configuration for the plugin. +func (d *HelloDriverPlugin) SetConfig(cfg *base.Config) error { + var config Config + if len(cfg.PluginConfig) != 0 { + if err := base.MsgPackDecode(cfg.PluginConfig, &config); err != nil { + return err + } + } + + // Save the configuration to the plugin + d.config = &config + + // TODO: parse and validated any configuration value if necessary. + // + // If your driver agent configuration requires any complex validation + // (some dependency between attributes) or special data parsing (the + // string "10s" into a time.Interval) you can do it here and update the + // value in d.config. + // + // In the example below we check if the shell specified by the user is + // supported by the plugin. + shell := d.config.Shell + if shell != "bash" && shell != "fish" { + return fmt.Errorf("invalid shell %s", d.config.Shell) + } + + // Save the Nomad agent configuration + if cfg.AgentConfig != nil { + d.nomadConfig = cfg.AgentConfig.Driver + } + + // TODO: initialize any extra requirements if necessary. + // + // Here you can use the config values to initialize any resources that are + // shared by all tasks that use this driver, such as a daemon process. + + return nil +} + +// TaskConfigSchema returns the HCL schema for the configuration of a task. +func (d *HelloDriverPlugin) TaskConfigSchema() (*hclspec.Spec, error) { + return taskConfigSpec, nil +} + +// Capabilities returns the features supported by the driver. +func (d *HelloDriverPlugin) Capabilities() (*drivers.Capabilities, error) { + return capabilities, nil +} + +// Fingerprint returns a channel that will be used to send health information +// and other driver specific node attributes. +func (d *HelloDriverPlugin) Fingerprint(ctx context.Context) (<-chan *drivers.Fingerprint, error) { + ch := make(chan *drivers.Fingerprint) + go d.handleFingerprint(ctx, ch) + return ch, nil +} + +// handleFingerprint manages the channel and the flow of fingerprint data. +func (d *HelloDriverPlugin) handleFingerprint(ctx context.Context, ch chan<- *drivers.Fingerprint) { + defer close(ch) + + // Nomad expects the initial fingerprint to be sent immediately + ticker := time.NewTimer(0) + for { + select { + case <-ctx.Done(): + return + case <-d.ctx.Done(): + return + case <-ticker.C: + // after the initial fingerprint we can set the proper fingerprint + // period + ticker.Reset(fingerprintPeriod) + ch <- d.buildFingerprint() + } + } +} + +// buildFingerprint returns the driver's fingerprint data +func (d *HelloDriverPlugin) buildFingerprint() *drivers.Fingerprint { + fp := &drivers.Fingerprint{ + Attributes: map[string]*structs.Attribute{}, + Health: drivers.HealthStateHealthy, + HealthDescription: drivers.DriverHealthy, + } + + // TODO: implement fingerprinting logic to populate health and driver + // attributes. + // + // Fingerprinting is used by the plugin to relay two important information + // to Nomad: health state and node attributes. + // + // If the plugin reports to be unhealthy, or doesn't send any fingerprint + // data in the expected interval of time, Nomad will restart it. + // + // Node attributes can be used to report any relevant information about + // the node in which the plugin is running (specific library availability, + // installed versions of a software etc.). These attributes can then be + // used by an operator to set job constrains. + // + // In the example below we check if the shell specified by the user exists + // in the node. + shell := d.config.Shell + + cmd := exec.Command("which", shell) + if err := cmd.Run(); err != nil { + return &drivers.Fingerprint{ + Health: drivers.HealthStateUndetected, + HealthDescription: fmt.Sprintf("shell %s not found", shell), + } + } + + // We also set the shell and its version as attributes + cmd = exec.Command(shell, "--version") + if out, err := cmd.Output(); err != nil { + d.logger.Warn("failed to find shell version: %v", err) + } else { + re := regexp.MustCompile("[0-9]\\.[0-9]\\.[0-9]") + version := re.FindString(string(out)) + + fp.Attributes["driver.hello.shell_version"] = structs.NewStringAttribute(version) + fp.Attributes["driver.hello.shell"] = structs.NewStringAttribute(shell) + } + + return fp +} + +// StartTask returns a task handle and a driver network if necessary. +func (d *HelloDriverPlugin) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drivers.DriverNetwork, error) { + if _, ok := d.tasks.Get(cfg.ID); ok { + return nil, nil, fmt.Errorf("task with ID %q already started", cfg.ID) + } + + var driverConfig TaskConfig + if err := cfg.DecodeDriverConfig(&driverConfig); err != nil { + return nil, nil, fmt.Errorf("failed to decode driver config: %v", err) + } + + d.logger.Info("starting task", "driver_cfg", hclog.Fmt("%+v", driverConfig)) + handle := drivers.NewTaskHandle(taskHandleVersion) + handle.Config = cfg + + // TODO: implement driver specific mechanism to start the task. + // + // Once the task is started you will need to store any relevant runtime + // information in a taskHandle and TaskState. The taskHandle will be + // stored in-memory in the plugin and will be used to interact with the + // task. + // + // The TaskState will be returned to the Nomad client inside a + // drivers.TaskHandle instance. This TaskHandle will be sent back to plugin + // if the task ever needs to be recovered, so the TaskState should contain + // enough information to handle that. + // + // In the example below we use an executor to fork a process to run our + // greeter. The executor is then stored in the handle so we can access it + // later and the the plugin.Client is used to generate a reattach + // configuration that can be used to recover communication with the task. + executorConfig := &executor.ExecutorConfig{ + LogFile: filepath.Join(cfg.TaskDir().Dir, "executor.out"), + LogLevel: "debug", + } + + exec, pluginClient, err := executor.CreateExecutor(d.logger, d.nomadConfig, executorConfig) + if err != nil { + return nil, nil, fmt.Errorf("failed to create executor: %v", err) + } + + echoCmd := fmt.Sprintf(`echo "%s"`, driverConfig.Greeting) + execCmd := &executor.ExecCommand{ + Cmd: d.config.Shell, + Args: []string{"-c", echoCmd}, + StdoutPath: cfg.StdoutPath, + StderrPath: cfg.StderrPath, + } + + ps, err := exec.Launch(execCmd) + if err != nil { + pluginClient.Kill() + return nil, nil, fmt.Errorf("failed to launch command with executor: %v", err) + } + + h := &taskHandle{ + exec: exec, + pid: ps.Pid, + pluginClient: pluginClient, + taskConfig: cfg, + procState: drivers.TaskStateRunning, + startedAt: time.Now().Round(time.Millisecond), + logger: d.logger, + } + + driverState := TaskState{ + ReattachConfig: structs.ReattachConfigFromGoPlugin(pluginClient.ReattachConfig()), + Pid: ps.Pid, + TaskConfig: cfg, + StartedAt: h.startedAt, + } + + if err := handle.SetDriverState(&driverState); err != nil { + return nil, nil, fmt.Errorf("failed to set driver state: %v", err) + } + + d.tasks.Set(cfg.ID, h) + go h.run() + return handle, nil, nil +} + +// RecoverTask recreates the in-memory state of a task from a TaskHandle. +func (d *HelloDriverPlugin) RecoverTask(handle *drivers.TaskHandle) error { + if handle == nil { + return fmt.Errorf("error: handle cannot be nil") + } + + if _, ok := d.tasks.Get(handle.Config.ID); ok { + return nil + } + + var taskState TaskState + if err := handle.GetDriverState(&taskState); err != nil { + return fmt.Errorf("failed to decode task state from handle: %v", err) + } + + var driverConfig TaskConfig + if err := taskState.TaskConfig.DecodeDriverConfig(&driverConfig); err != nil { + return fmt.Errorf("failed to decode driver config: %v", err) + } + + // TODO: implement driver specific logic to recover a task. + // + // Recovering a task involves recreating and storing a taskHandle as if the + // task was just started. + // + // In the example below we use the executor to re-attach to the process + // that was created when the task first started. + plugRC, err := structs.ReattachConfigToGoPlugin(taskState.ReattachConfig) + if err != nil { + return fmt.Errorf("failed to build ReattachConfig from taskConfig state: %v", err) + } + + execImpl, pluginClient, err := executor.ReattachToExecutor(plugRC, d.logger) + if err != nil { + return fmt.Errorf("failed to reattach to executor: %v", err) + } + + h := &taskHandle{ + exec: execImpl, + pid: taskState.Pid, + pluginClient: pluginClient, + taskConfig: taskState.TaskConfig, + procState: drivers.TaskStateRunning, + startedAt: taskState.StartedAt, + exitResult: &drivers.ExitResult{}, + } + + d.tasks.Set(taskState.TaskConfig.ID, h) + + go h.run() + return nil +} + +// WaitTask returns a channel used to notify Nomad when a task exits. +func (d *HelloDriverPlugin) WaitTask(ctx context.Context, taskID string) (<-chan *drivers.ExitResult, error) { + handle, ok := d.tasks.Get(taskID) + if !ok { + return nil, drivers.ErrTaskNotFound + } + + ch := make(chan *drivers.ExitResult) + go d.handleWait(ctx, handle, ch) + return ch, nil +} + +func (d *HelloDriverPlugin) handleWait(ctx context.Context, handle *taskHandle, ch chan *drivers.ExitResult) { + defer close(ch) + var result *drivers.ExitResult + + // TODO: implement driver specific logic to notify Nomad the task has been + // completed and what was the exit result. + // + // When a result is sent in the result channel Nomad will stop the task and + // emit an event that an operator can use to get an insight on why the task + // stopped. + // + // In the example below we block and wait until the executor finishes + // running, at which point we send the exit code and signal in the result + // channel. + ps, err := handle.exec.Wait(ctx) + if err != nil { + result = &drivers.ExitResult{ + Err: fmt.Errorf("executor: error waiting on process: %v", err), + } + } else { + result = &drivers.ExitResult{ + ExitCode: ps.ExitCode, + Signal: ps.Signal, + } + } + + for { + select { + case <-ctx.Done(): + return + case <-d.ctx.Done(): + return + case ch <- result: + } + } +} + +// StopTask stops a running task with the given signal and within the timeout window. +func (d *HelloDriverPlugin) StopTask(taskID string, timeout time.Duration, signal string) error { + handle, ok := d.tasks.Get(taskID) + if !ok { + return drivers.ErrTaskNotFound + } + + // TODO: implement driver specific logic to stop a task. + // + // The StopTask function is expected to stop a running task by sending the + // given signal to it. If the task does not stop during the given timeout, + // the driver must forcefully kill the task. + // + // In the example below we let the executor handle the task shutdown + // process for us, but you might need to customize this for your own + // implementation. + if err := handle.exec.Shutdown(signal, timeout); err != nil { + if handle.pluginClient.Exited() { + return nil + } + return fmt.Errorf("executor Shutdown failed: %v", err) + } + + return nil +} + +// DestroyTask cleans up and removes a task that has terminated. +func (d *HelloDriverPlugin) DestroyTask(taskID string, force bool) error { + handle, ok := d.tasks.Get(taskID) + if !ok { + return drivers.ErrTaskNotFound + } + + if handle.IsRunning() && !force { + return fmt.Errorf("cannot destroy running task") + } + + // TODO: implement driver specific logic to destroy a complete task. + // + // Destroying a task includes removing any resources used by task and any + // local references in the plugin. If force is set to true the task should + // be destroyed even if it's currently running. + // + // In the example below we use the executor to force shutdown the task + // (timeout equals 0). + if !handle.pluginClient.Exited() { + if err := handle.exec.Shutdown("", 0); err != nil { + handle.logger.Error("destroying executor failed", "err", err) + } + + handle.pluginClient.Kill() + } + + d.tasks.Delete(taskID) + return nil +} + +// InspectTask returns detailed status information for the referenced taskID. +func (d *HelloDriverPlugin) InspectTask(taskID string) (*drivers.TaskStatus, error) { + handle, ok := d.tasks.Get(taskID) + if !ok { + return nil, drivers.ErrTaskNotFound + } + + return handle.TaskStatus(), nil +} + +// TaskStats returns a channel which the driver should send stats to at the given interval. +func (d *HelloDriverPlugin) TaskStats(ctx context.Context, taskID string, interval time.Duration) (<-chan *drivers.TaskResourceUsage, error) { + handle, ok := d.tasks.Get(taskID) + if !ok { + return nil, drivers.ErrTaskNotFound + } + + // TODO: implement driver specific logic to send task stats. + // + // This function returns a channel that Nomad will use to listen for task + // stats (e.g., CPU and memory usage) in a given interval. It should send + // stats until the context is canceled or the task stops running. + // + // In the example below we use the Stats function provided by the executor, + // but you can build a set of functions similar to the fingerprint process. + return handle.exec.Stats(ctx, interval) +} + +// TaskEvents returns a channel that the plugin can use to emit task related events. +func (d *HelloDriverPlugin) TaskEvents(ctx context.Context) (<-chan *drivers.TaskEvent, error) { + return d.eventer.TaskEvents(ctx) +} + +// SignalTask forwards a signal to a task. +// This is an optional capability. +func (d *HelloDriverPlugin) SignalTask(taskID string, signal string) error { + handle, ok := d.tasks.Get(taskID) + if !ok { + return drivers.ErrTaskNotFound + } + + // TODO: implement driver specific signal handling logic. + // + // The given signal must be forwarded to the target taskID. If this plugin + // doesn't support receiving signals (capability SendSignals is set to + // false) you can just return nil. + sig := os.Interrupt + if s, ok := signals.SignalLookup[signal]; ok { + sig = s + } else { + d.logger.Warn("unknown signal to send to task, using SIGINT instead", "signal", signal, "task_id", handle.taskConfig.ID) + + } + return handle.exec.Signal(sig) +} + +// ExecTask returns the result of executing the given command inside a task. +// This is an optional capability. +func (d *HelloDriverPlugin) ExecTask(taskID string, cmd []string, timeout time.Duration) (*drivers.ExecTaskResult, error) { + // TODO: implement driver specific logic to execute commands in a task. + return nil, fmt.Errorf("This driver does not support exec") +} diff --git a/pkg/plugin/handle.go b/hello/handle.go similarity index 58% rename from pkg/plugin/handle.go rename to hello/handle.go index ddeab4a..6dfb976 100644 --- a/pkg/plugin/handle.go +++ b/hello/handle.go @@ -1,4 +1,4 @@ -package skeleton +package hello import ( "context" @@ -7,6 +7,8 @@ import ( "time" "github.com/hashicorp/go-hclog" + "github.com/hashicorp/go-plugin" + "github.com/hashicorp/nomad/drivers/shared/executor" "github.com/hashicorp/nomad/plugins/drivers" ) @@ -14,17 +16,20 @@ import ( // such as process ID if this is a local task or other meta // data if this driver deals with external APIs type taskHandle struct { - pid int - logger hclog.Logger - // stateLock syncs access to all fields below stateLock sync.RWMutex - taskConfig *drivers.TaskConfig - procState drivers.TaskState - startedAt time.Time - completedAt time.Time - exitResult *drivers.ExitResult + logger hclog.Logger + exec executor.Executor + pluginClient *plugin.Client + taskConfig *drivers.TaskConfig + procState drivers.TaskState + startedAt time.Time + completedAt time.Time + exitResult *drivers.ExitResult + + // TODO: add any extra relevant information about the task. + pid int } func (h *taskHandle) TaskStatus() *drivers.TaskStatus { @@ -57,23 +62,19 @@ func (h *taskHandle) run() { } h.stateLock.Unlock() + // TODO: wait for your task to complete and upate its state. + ps, err := h.exec.Wait(context.Background()) h.stateLock.Lock() defer h.stateLock.Unlock() - // Implement running the task here, this is driver specific -} - -func (h *taskHandle) stats(ctx context.Context, interval time.Duration) (<-chan *drivers.TaskResourceUsage, error) { - ch := make(chan *drivers.TaskResourceUsage) - - return ch, nil -} - -// shutdown shuts down the container, with `timeout` grace period -// before killing the container with SIGKILL. -func (h *taskHandle) shutdown(timeout time.Duration) error { - // Implement shutdown of the task. Shutdown should be tried - // gracefully first. If its past the timeout, then this method - // should do a hard shutdown and return - return nil + if err != nil { + h.exitResult.Err = err + h.procState = drivers.TaskStateUnknown + h.completedAt = time.Now() + return + } + h.procState = drivers.TaskStateExited + h.exitResult.ExitCode = ps.ExitCode + h.exitResult.Signal = ps.Signal + h.completedAt = ps.Time } diff --git a/pkg/plugin/state.go b/hello/state.go similarity index 97% rename from pkg/plugin/state.go rename to hello/state.go index 266780a..30f10d7 100644 --- a/pkg/plugin/state.go +++ b/hello/state.go @@ -1,4 +1,4 @@ -package skeleton +package hello import ( "sync" diff --git a/cmd/driver/main.go b/main.go similarity index 69% rename from cmd/driver/main.go rename to main.go index 433f9bc..4c8241b 100644 --- a/cmd/driver/main.go +++ b/main.go @@ -1,12 +1,10 @@ -// +build linux - package main import ( log "github.com/hashicorp/go-hclog" + "github.com/hashicorp/nomad-runtime-driver-skeleton/hello" "github.com/hashicorp/nomad/plugins" - "github.com/hashicorp/nomad-driver-skeleton/pkg/plugin" ) func main() { @@ -16,6 +14,5 @@ func main() { // factory returns a new instance of a nomad driver plugin func factory(log log.Logger) interface{} { - return skeleton.NewSkeletonDriver(log) + return hello.NewPlugin(log) } - diff --git a/pkg/plugin/driver.go b/pkg/plugin/driver.go deleted file mode 100644 index 5a8e7db..0000000 --- a/pkg/plugin/driver.go +++ /dev/null @@ -1,330 +0,0 @@ -package skeleton - -import ( - "context" - "fmt" - "time" - - hclog "github.com/hashicorp/go-hclog" - "github.com/hashicorp/nomad/drivers/shared/eventer" - "github.com/hashicorp/nomad/plugins/base" - "github.com/hashicorp/nomad/plugins/drivers" - "github.com/hashicorp/nomad/plugins/shared/hclspec" - structs "github.com/hashicorp/nomad/plugins/shared/structs" -) - -const ( - // pluginName is the name of the plugin - pluginName = "test-plugin" - - // fingerprintPeriod is the interval at which the driver will send fingerprint responses - fingerprintPeriod = 30 * time.Second - - // taskHandleVersion is the version of task handle which this driver sets - // and understands how to decode driver state - taskHandleVersion = 1 -) - -var ( - // pluginInfo is the response returned for the PluginInfo RPC - pluginInfo = &base.PluginInfoResponse{ - Type: base.PluginTypeDriver, - PluginApiVersions: []string{"0.1.0"}, - PluginVersion: "0.0.1", - Name: pluginName, - } - - // configSpec is the hcl specification returned by the ConfigSchema RPC - configSpec = hclspec.NewObject(map[string]*hclspec.Spec{ - "enabled": hclspec.NewDefault( - hclspec.NewAttr("enabled", "bool", false), - hclspec.NewLiteral("true"), - ), - }) - - // taskConfigSpec is the hcl specification for the driver config section of - // a taskConfig within a job. It is returned in the TaskConfigSchema RPC - taskConfigSpec = hclspec.NewObject(map[string]*hclspec.Spec{ - "strconfig": hclspec.NewAttr("strconfig", "string", true), - "boolcfg": hclspec.NewAttr("boolcfg", "bool", false), - }) - - // capabilities is returned by the Capabilities RPC and indicates what - // optional features this driver supports. This should be set according - // to the target run time - capabilities = &drivers.Capabilities{ - SendSignals: true, - Exec: false, - } -) - -// Driver is a skeleton driver implementation. The fields in this struct -// are to help plugin authors get started writing their runtime plugins. -type Driver struct { - // eventer is used to handle multiplexing of TaskEvents calls such that an - // event can be broadcast to all callers - eventer *eventer.Eventer - - // config is the driver configuration set by the SetConfig RPC - config *Config - - // nomadConfig is the client config from nomad - nomadConfig *base.ClientDriverConfig - - // tasks is the in memory datastore mapping taskIDs to rawExecDriverHandles - tasks *taskStore - - // ctx is the context for the driver. It is passed to other subsystems to - // coordinate shutdown - ctx context.Context - - // signalShutdown is called when the driver is shutting down and cancels the - // ctx passed to any subsystems - signalShutdown context.CancelFunc - - // logger will log to the Nomad agent - logger hclog.Logger -} - -// Config is the driver configuration set by the SetConfig RPC call -type Config struct { - // Enabled is set to true to enable this driver - Enabled bool `codec:"enabled"` -} - -// TaskConfig is the driver configuration of a task within a job -type TaskConfig struct { - StrConfig string `codec:"strconfig"` - BoolConfig bool `codec:"boolconfig"` -} - -// TaskState is the state which is encoded in the handle returned in -// StartTask. This information is needed to rebuild the task state and handler -// during recovery. -type TaskState struct { - TaskConfig *drivers.TaskConfig - ContainerName string - StartedAt time.Time - PID int -} - -// NewSkeletonDriver returns a new DriverPlugin implementation -func NewSkeletonDriver(logger hclog.Logger) drivers.DriverPlugin { - ctx, cancel := context.WithCancel(context.Background()) - logger = logger.Named(pluginName) - - return &Driver{ - eventer: eventer.NewEventer(ctx, logger), - config: &Config{}, - tasks: newTaskStore(), - ctx: ctx, - signalShutdown: cancel, - logger: logger, - } -} - -func (d *Driver) PluginInfo() (*base.PluginInfoResponse, error) { - return pluginInfo, nil -} - -func (d *Driver) ConfigSchema() (*hclspec.Spec, error) { - return configSpec, nil -} - -func (d *Driver) SetConfig(cfg *base.Config) error { - var config Config - if len(cfg.PluginConfig) != 0 { - if err := base.MsgPackDecode(cfg.PluginConfig, &config); err != nil { - return err - } - } - - d.config = &config - if cfg.AgentConfig != nil { - d.nomadConfig = cfg.AgentConfig.Driver - } - - return nil -} - -func (d *Driver) Shutdown(ctx context.Context) error { - d.signalShutdown() - return nil -} - -func (d *Driver) TaskConfigSchema() (*hclspec.Spec, error) { - return taskConfigSpec, nil -} - -func (d *Driver) Capabilities() (*drivers.Capabilities, error) { - return capabilities, nil -} - -func (d *Driver) Fingerprint(ctx context.Context) (<-chan *drivers.Fingerprint, error) { - ch := make(chan *drivers.Fingerprint) - go d.handleFingerprint(ctx, ch) - return ch, nil -} - -func (d *Driver) handleFingerprint(ctx context.Context, ch chan<- *drivers.Fingerprint) { - defer close(ch) - ticker := time.NewTimer(0) - for { - select { - case <-ctx.Done(): - return - case <-d.ctx.Done(): - return - case <-ticker.C: - ticker.Reset(fingerprintPeriod) - ch <- d.buildFingerprint() - } - } -} - -func (d *Driver) buildFingerprint() *drivers.Fingerprint { - var health drivers.HealthState - var desc string - attrs := map[string]*structs.Attribute{} - - // Implement finger printing logic here to populate health and driver attributes - - return &drivers.Fingerprint{ - Attributes: attrs, - Health: health, - HealthDescription: desc, - } -} - -func (d *Driver) RecoverTask(handle *drivers.TaskHandle) error { - if handle == nil { - return fmt.Errorf("error: handle cannot be nil") - } - - if _, ok := d.tasks.Get(handle.Config.ID); ok { - return nil - } - - var taskState TaskState - if err := handle.GetDriverState(&taskState); err != nil { - return fmt.Errorf("failed to decode task state from handle: %v", err) - } - - var driverConfig TaskConfig - if err := taskState.TaskConfig.DecodeDriverConfig(&driverConfig); err != nil { - return fmt.Errorf("failed to decode driver config: %v", err) - } - - // Implement driver specific way to reattach to a running task or restart - return nil -} - -// StartTask setup the task exec and calls the container excecutor -func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drivers.DriverNetwork, error) { - if _, ok := d.tasks.Get(cfg.ID); ok { - return nil, nil, fmt.Errorf("task with ID %q already started", cfg.ID) - } - - var driverConfig TaskConfig - if err := cfg.DecodeDriverConfig(&driverConfig); err != nil { - return nil, nil, fmt.Errorf("failed to decode driver config: %v", err) - } - - handle := drivers.NewTaskHandle(taskHandleVersion) - handle.Config = cfg - - // Implement driver specific mechanism to start the task here - - return nil, nil, nil - -} - -func (d *Driver) WaitTask(ctx context.Context, taskID string) (<-chan *drivers.ExitResult, error) { - handle, ok := d.tasks.Get(taskID) - if !ok { - return nil, drivers.ErrTaskNotFound - } - - ch := make(chan *drivers.ExitResult) - go d.handleWait(ctx, handle, ch) - - return ch, nil -} - -func (d *Driver) handleWait(ctx context.Context, handle *taskHandle, ch chan *drivers.ExitResult) { - defer close(ch) - - ticker := time.NewTicker(1 * time.Second) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return - case <-d.ctx.Done(): - return - case <-ticker.C: - s := handle.TaskStatus() - if s.State == drivers.TaskStateExited { - ch <- handle.exitResult - } - } - } -} - -func (d *Driver) StopTask(taskID string, timeout time.Duration, signal string) error { - handle, ok := d.tasks.Get(taskID) - if !ok { - return drivers.ErrTaskNotFound - } - - if err := handle.shutdown(timeout); err != nil { - return fmt.Errorf("executor Shutdown failed: %v", err) - } - - return nil -} - -func (d *Driver) DestroyTask(taskID string, force bool) error { - handle, ok := d.tasks.Get(taskID) - if !ok { - return drivers.ErrTaskNotFound - } - - if handle.IsRunning() && !force { - return fmt.Errorf("cannot destroy running task") - } - - d.tasks.Delete(taskID) - return nil -} - -func (d *Driver) InspectTask(taskID string) (*drivers.TaskStatus, error) { - handle, ok := d.tasks.Get(taskID) - if !ok { - return nil, drivers.ErrTaskNotFound - } - - return handle.TaskStatus(), nil -} - -func (d *Driver) TaskStats(ctx context.Context, taskID string, interval time.Duration) (<-chan *drivers.TaskResourceUsage, error) { - handle, ok := d.tasks.Get(taskID) - if !ok { - return nil, drivers.ErrTaskNotFound - } - - return handle.stats(ctx, interval) -} - -func (d *Driver) TaskEvents(ctx context.Context) (<-chan *drivers.TaskEvent, error) { - return d.eventer.TaskEvents(ctx) -} - -func (d *Driver) SignalTask(taskID string, signal string) error { - return fmt.Errorf("This driver does not support signals") -} - -func (d *Driver) ExecTask(taskID string, cmd []string, timeout time.Duration) (*drivers.ExecTaskResult, error) { - return nil, fmt.Errorf("This driver does not support exec") //TODO -}