diff --git a/Makefile b/Makefile index e4ccd2c553..36a5a90845 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,8 @@ DOCKER_IMAGE?=skywire-runner # docker image to use for running skywire-node.`gol DOCKER_NETWORK?=SKYNET DOCKER_NODE?=SKY01 DOCKER_OPTS?=GO111MODULE=on GOOS=linux # go options for compiling for docker container +TEST_OPTS?=-race -tags no_ci -cover -timeout=5m +BUILD_OPTS?=-race check: lint test ## Run linters and tests @@ -59,19 +61,19 @@ vendorcheck: ## Run vendorcheck test: ## Run tests -go clean -testcache &>/dev/null - ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./internal/... + ${OPTS} go test ${TEST_OPTS} ./internal/... #${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/... - ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/app/... - ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/cipher/... - ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/manager/... - ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/messaging-discovery/... - ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/node/... - ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/route-finder/... - ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/router/... - ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/routing/... - ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/setup/... - ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/transport/... - ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/transport-discovery/... + ${OPTS} go test ${TEST_OPTS} ./pkg/app/... + ${OPTS} go test ${TEST_OPTS} ./pkg/cipher/... + ${OPTS} go test ${TEST_OPTS} ./pkg/manager/... + ${OPTS} go test ${TEST_OPTS} ./pkg/messaging-discovery/... + ${OPTS} go test ${TEST_OPTS} ./pkg/node/... + ${OPTS} go test ${TEST_OPTS} ./pkg/route-finder/... + ${OPTS} go test ${TEST_OPTS} ./pkg/router/... + ${OPTS} go test ${TEST_OPTS} ./pkg/routing/... + ${OPTS} go test ${TEST_OPTS} ./pkg/setup/... + ${OPTS} go test ${TEST_OPTS} ./pkg/transport/... + ${OPTS} go test ${TEST_OPTS} ./pkg/transport-discovery/... ${OPTS} go test -tags no_ci -cover -timeout=5m ./pkg/messaging/... @@ -93,20 +95,20 @@ dep: ## Sorts dependencies # Apps host-apps: ## Build app - ${OPTS} go build -race -o ./apps/skychat.v1.0 ./cmd/apps/skychat - ${OPTS} go build -race -o ./apps/helloworld.v1.0 ./cmd/apps/helloworld - ${OPTS} go build -race -o ./apps/socksproxy.v1.0 ./cmd/apps/therealproxy - ${OPTS} go build -race -o ./apps/socksproxy-client.v1.0 ./cmd/apps/therealproxy-client - ${OPTS} go build -race -o ./apps/SSH.v1.0 ./cmd/apps/therealssh - ${OPTS} go build -race -o ./apps/SSH-client.v1.0 ./cmd/apps/therealssh-client + ${OPTS} go build ${BUILD_OPTS} -o ./apps/skychat.v1.0 ./cmd/apps/skychat + ${OPTS} go build ${BUILD_OPTS} -o ./apps/helloworld.v1.0 ./cmd/apps/helloworld + ${OPTS} go build ${BUILD_OPTS} -o ./apps/socksproxy.v1.0 ./cmd/apps/therealproxy + ${OPTS} go build ${BUILD_OPTS} -o ./apps/socksproxy-client.v1.0 ./cmd/apps/therealproxy-client + ${OPTS} go build ${BUILD_OPTS} -o ./apps/SSH.v1.0 ./cmd/apps/therealssh + ${OPTS} go build ${BUILD_OPTS} -o ./apps/SSH-client.v1.0 ./cmd/apps/therealssh-client # Bin bin: ## Build `skywire-node`, `skywire-cli`, `manager-node`, `SSH-cli` - ${OPTS} go build -race -o ./skywire-node ./cmd/skywire-node - ${OPTS} go build -race -o ./skywire-cli ./cmd/skywire-cli - ${OPTS} go build -race -o ./setup-node ./cmd/setup-node - ${OPTS} go build -race -o ./manager-node ./cmd/manager-node - ${OPTS} go build -race -o ./SSH-cli ./cmd/therealssh-cli + ${OPTS} go build ${BUILD_OPTS} -o ./skywire-node ./cmd/skywire-node + ${OPTS} go build ${BUILD_OPTS} -o ./skywire-cli ./cmd/skywire-cli + ${OPTS} go build ${BUILD_OPTS} -o ./setup-node ./cmd/setup-node + ${OPTS} go build ${BUILD_OPTS} -o ./manager-node ./cmd/manager-node + ${OPTS} go build ${BUILD_OPTS} -o ./SSH-cli ./cmd/therealssh-cli release: ## Build skywire-node`, skywire-cli, manager-node, SSH-cli and apps without -race flag ${OPTS} go build -o ./skywire-node ./cmd/skywire-node @@ -121,8 +123,6 @@ release: ## Build skywire-node`, skywire-cli, manager-node, SSH-cli and apps wit ${OPTS} go build -o ./apps/SSH.v1.0 ./cmd/apps/therealssh ${OPTS} go build -o ./apps/SSH-client.v1.0 ./cmd/apps/therealssh-client - - # Dockerized skywire-node docker-image: ## Build docker image `skywire-runner` docker image build --tag=skywire-runner --rm - < skywire-runner.Dockerfile diff --git a/cmd/skywire-node/commands/root.go b/cmd/skywire-node/commands/root.go index f6e88d0e1d..ec11faa32b 100644 --- a/cmd/skywire-node/commands/root.go +++ b/cmd/skywire-node/commands/root.go @@ -3,6 +3,7 @@ package commands import ( "bufio" "encoding/json" + "fmt" "io" "log" "log/syslog" @@ -16,6 +17,10 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/spf13/cobra" + "net/http" + _ "net/http/pprof" //no_lint + + "github.com/pkg/profile" "github.com/skycoin/skywire/pkg/node" "github.com/skycoin/skywire/pkg/util/pathutil" ) @@ -27,6 +32,8 @@ var ( syslogAddr string tag string cfgFromStdin bool + profileMode string + pport string ) var rootCmd = &cobra.Command{ @@ -34,6 +41,26 @@ var rootCmd = &cobra.Command{ Short: "App Node for skywire", Run: func(_ *cobra.Command, args []string) { + profilePath := profile.ProfilePath("./logs/" + tag) + switch profileMode { + case "cpu": + defer profile.Start(profilePath, profile.CPUProfile).Stop() + case "mem": + defer profile.Start(profilePath, profile.MemProfile).Stop() + case "mutex": + defer profile.Start(profilePath, profile.MutexProfile).Stop() + case "block": + defer profile.Start(profilePath, profile.BlockProfile).Stop() + case "trace": + defer profile.Start(profilePath, profile.TraceProfile).Stop() + case "http": + go func() { + log.Println(http.ListenAndServe(fmt.Sprintf("localhost:%v", pport), nil)) + }() + default: + // do nothing + } + logger := logging.MustGetLogger(tag) if syslogAddr != "none" { @@ -102,6 +129,8 @@ func init() { rootCmd.Flags().StringVarP(&syslogAddr, "syslog", "", "none", "syslog server address. E.g. localhost:514") rootCmd.Flags().StringVarP(&tag, "tag", "", "skywire", "logging tag") rootCmd.Flags().BoolVarP(&cfgFromStdin, "stdin", "i", false, "read config from STDIN") + rootCmd.Flags().StringVarP(&profileMode, "pprof", "p", "none", "enable profiling with pprof. Mode: none or one of: [cpu, mem, mutex, block, trace, http]") + rootCmd.Flags().StringVarP(&pport, "pport", "", "6060", "port for http-mode of pprof") } // Execute executes root CLI command. diff --git a/cmd/skywire-node/skywire-node.go b/cmd/skywire-node/skywire-node.go index 8e4fd143ee..37060942fd 100644 --- a/cmd/skywire-node/skywire-node.go +++ b/cmd/skywire-node/skywire-node.go @@ -3,7 +3,10 @@ skywire application node */ package main -import "github.com/skycoin/skywire/cmd/skywire-node/commands" +import( + "github.com/skycoin/skywire/cmd/skywire-node/commands" + +) func main() { commands.Execute() diff --git a/go.mod b/go.mod index cc4b075986..3f73b0a813 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/mattn/go-colorable v0.1.1 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/mitchellh/go-homedir v1.1.0 + github.com/pkg/profile v1.3.0 github.com/prometheus/client_golang v0.9.2 github.com/sirupsen/logrus v1.4.1 github.com/skycoin/skycoin v0.25.1 diff --git a/go.sum b/go.sum index deceaea6ae..c101e95af0 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,8 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1f github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 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/pkg/profile v1.3.0 h1:OQIvuDgm00gWVWGTf4m4mCt6W1/0YqU7Ntg0mySWgaI= +github.com/pkg/profile v1.3.0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= 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/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= diff --git a/vendor/github.com/pkg/profile/.travis.yml b/vendor/github.com/pkg/profile/.travis.yml new file mode 100644 index 0000000000..1c9e6bb6bc --- /dev/null +++ b/vendor/github.com/pkg/profile/.travis.yml @@ -0,0 +1,10 @@ +language: go +go_import_path: github.com/pkg/profile +go: + - 1.10.x + - 1.12.x + - tip + +script: + - go test github.com/pkg/profile + - go test -race github.com/pkg/profile diff --git a/vendor/github.com/pkg/profile/AUTHORS b/vendor/github.com/pkg/profile/AUTHORS new file mode 100644 index 0000000000..00441d354b --- /dev/null +++ b/vendor/github.com/pkg/profile/AUTHORS @@ -0,0 +1 @@ +Dave Cheney diff --git a/vendor/github.com/pkg/profile/LICENSE b/vendor/github.com/pkg/profile/LICENSE new file mode 100644 index 0000000000..f747a8411e --- /dev/null +++ b/vendor/github.com/pkg/profile/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2013 Dave Cheney. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/pkg/profile/README.md b/vendor/github.com/pkg/profile/README.md new file mode 100644 index 0000000000..37bfa58c5f --- /dev/null +++ b/vendor/github.com/pkg/profile/README.md @@ -0,0 +1,54 @@ +profile +======= + +Simple profiling support package for Go + +[![Build Status](https://travis-ci.org/pkg/profile.svg?branch=master)](https://travis-ci.org/pkg/profile) [![GoDoc](http://godoc.org/github.com/pkg/profile?status.svg)](http://godoc.org/github.com/pkg/profile) + + +installation +------------ + + go get github.com/pkg/profile + +usage +----- + +Enabling profiling in your application is as simple as one line at the top of your main function + +```go +import "github.com/pkg/profile" + +func main() { + defer profile.Start().Stop() + ... +} +``` + +options +------- + +What to profile is controlled by config value passed to profile.Start. +By default CPU profiling is enabled. + +```go +import "github.com/pkg/profile" + +func main() { + // p.Stop() must be called before the program exits to + // ensure profiling information is written to disk. + p := profile.Start(profile.MemProfile, profile.ProfilePath("."), profile.NoShutdownHook) + ... +} +``` + +Several convenience package level values are provided for cpu, memory, and block (contention) profiling. + +For more complex options, consult the [documentation](http://godoc.org/github.com/pkg/profile). + +contributing +------------ + +We welcome pull requests, bug fixes and issue reports. + +Before proposing a change, please discuss it first by raising an issue. diff --git a/vendor/github.com/pkg/profile/mutex.go b/vendor/github.com/pkg/profile/mutex.go new file mode 100644 index 0000000000..e69c5b44d8 --- /dev/null +++ b/vendor/github.com/pkg/profile/mutex.go @@ -0,0 +1,13 @@ +// +build go1.8 + +package profile + +import "runtime" + +func enableMutexProfile() { + runtime.SetMutexProfileFraction(1) +} + +func disableMutexProfile() { + runtime.SetMutexProfileFraction(0) +} diff --git a/vendor/github.com/pkg/profile/mutex17.go b/vendor/github.com/pkg/profile/mutex17.go new file mode 100644 index 0000000000..b004c21d56 --- /dev/null +++ b/vendor/github.com/pkg/profile/mutex17.go @@ -0,0 +1,9 @@ +// +build !go1.8 + +package profile + +// mock mutex support for Go 1.7 and earlier. + +func enableMutexProfile() {} + +func disableMutexProfile() {} diff --git a/vendor/github.com/pkg/profile/profile.go b/vendor/github.com/pkg/profile/profile.go new file mode 100644 index 0000000000..20e285427e --- /dev/null +++ b/vendor/github.com/pkg/profile/profile.go @@ -0,0 +1,263 @@ +// Package profile provides a simple way to manage runtime/pprof +// profiling of your Go application. +package profile + +import ( + "io/ioutil" + "log" + "os" + "os/signal" + "path/filepath" + "runtime" + "runtime/pprof" + "sync/atomic" +) + +const ( + cpuMode = iota + memMode + mutexMode + blockMode + traceMode + threadCreateMode +) + +// Profile represents an active profiling session. +type Profile struct { + // quiet suppresses informational messages during profiling. + quiet bool + + // noShutdownHook controls whether the profiling package should + // hook SIGINT to write profiles cleanly. + noShutdownHook bool + + // mode holds the type of profiling that will be made + mode int + + // path holds the base path where various profiling files are written. + // If blank, the base path will be generated by ioutil.TempDir. + path string + + // memProfileRate holds the rate for the memory profile. + memProfileRate int + + // closer holds a cleanup function that run after each profile + closer func() + + // stopped records if a call to profile.Stop has been made + stopped uint32 +} + +// NoShutdownHook controls whether the profiling package should +// hook SIGINT to write profiles cleanly. +// Programs with more sophisticated signal handling should set +// this to true and ensure the Stop() function returned from Start() +// is called during shutdown. +func NoShutdownHook(p *Profile) { p.noShutdownHook = true } + +// Quiet suppresses informational messages during profiling. +func Quiet(p *Profile) { p.quiet = true } + +// CPUProfile enables cpu profiling. +// It disables any previous profiling settings. +func CPUProfile(p *Profile) { p.mode = cpuMode } + +// DefaultMemProfileRate is the default memory profiling rate. +// See also http://golang.org/pkg/runtime/#pkg-variables +const DefaultMemProfileRate = 4096 + +// MemProfile enables memory profiling. +// It disables any previous profiling settings. +func MemProfile(p *Profile) { + p.memProfileRate = DefaultMemProfileRate + p.mode = memMode +} + +// MemProfileRate enables memory profiling at the preferred rate. +// It disables any previous profiling settings. +func MemProfileRate(rate int) func(*Profile) { + return func(p *Profile) { + p.memProfileRate = rate + p.mode = memMode + } +} + +// MutexProfile enables mutex profiling. +// It disables any previous profiling settings. +func MutexProfile(p *Profile) { p.mode = mutexMode } + +// BlockProfile enables block (contention) profiling. +// It disables any previous profiling settings. +func BlockProfile(p *Profile) { p.mode = blockMode } + +// Trace profile enables execution tracing. +// It disables any previous profiling settings. +func TraceProfile(p *Profile) { p.mode = traceMode } + +// ThreadcreationProfile enables thread creation profiling.. +// It disables any previous profiling settings. +func ThreadcreationProfile(p *Profile) { p.mode = threadCreateMode } + +// ProfilePath controls the base path where various profiling +// files are written. If blank, the base path will be generated +// by ioutil.TempDir. +func ProfilePath(path string) func(*Profile) { + return func(p *Profile) { + p.path = path + } +} + +// Stop stops the profile and flushes any unwritten data. +func (p *Profile) Stop() { + if !atomic.CompareAndSwapUint32(&p.stopped, 0, 1) { + // someone has already called close + return + } + p.closer() + atomic.StoreUint32(&started, 0) +} + +// started is non zero if a profile is running. +var started uint32 + +// Start starts a new profiling session. +// The caller should call the Stop method on the value returned +// to cleanly stop profiling. +func Start(options ...func(*Profile)) interface { + Stop() +} { + if !atomic.CompareAndSwapUint32(&started, 0, 1) { + log.Fatal("profile: Start() already called") + } + + var prof Profile + for _, option := range options { + option(&prof) + } + + path, err := func() (string, error) { + if p := prof.path; p != "" { + return p, os.MkdirAll(p, 0777) + } + return ioutil.TempDir("", "profile") + }() + + if err != nil { + log.Fatalf("profile: could not create initial output directory: %v", err) + } + + logf := func(format string, args ...interface{}) { + if !prof.quiet { + log.Printf(format, args...) + } + } + + switch prof.mode { + case cpuMode: + fn := filepath.Join(path, "cpu.pprof") + f, err := os.Create(fn) + if err != nil { + log.Fatalf("profile: could not create cpu profile %q: %v", fn, err) + } + logf("profile: cpu profiling enabled, %s", fn) + pprof.StartCPUProfile(f) + prof.closer = func() { + pprof.StopCPUProfile() + f.Close() + logf("profile: cpu profiling disabled, %s", fn) + } + + case memMode: + fn := filepath.Join(path, "mem.pprof") + f, err := os.Create(fn) + if err != nil { + log.Fatalf("profile: could not create memory profile %q: %v", fn, err) + } + old := runtime.MemProfileRate + runtime.MemProfileRate = prof.memProfileRate + logf("profile: memory profiling enabled (rate %d), %s", runtime.MemProfileRate, fn) + prof.closer = func() { + pprof.Lookup("heap").WriteTo(f, 0) + f.Close() + runtime.MemProfileRate = old + logf("profile: memory profiling disabled, %s", fn) + } + + case mutexMode: + fn := filepath.Join(path, "mutex.pprof") + f, err := os.Create(fn) + if err != nil { + log.Fatalf("profile: could not create mutex profile %q: %v", fn, err) + } + enableMutexProfile() + logf("profile: mutex profiling enabled, %s", fn) + prof.closer = func() { + if mp := pprof.Lookup("mutex"); mp != nil { + mp.WriteTo(f, 0) + } + f.Close() + disableMutexProfile() + logf("profile: mutex profiling disabled, %s", fn) + } + + case blockMode: + fn := filepath.Join(path, "block.pprof") + f, err := os.Create(fn) + if err != nil { + log.Fatalf("profile: could not create block profile %q: %v", fn, err) + } + runtime.SetBlockProfileRate(1) + logf("profile: block profiling enabled, %s", fn) + prof.closer = func() { + pprof.Lookup("block").WriteTo(f, 0) + f.Close() + runtime.SetBlockProfileRate(0) + logf("profile: block profiling disabled, %s", fn) + } + + case threadCreateMode: + fn := filepath.Join(path, "threadcreation.pprof") + f, err := os.Create(fn) + if err != nil { + log.Fatalf("profile: could not create thread creation profile %q: %v", fn, err) + } + logf("profile: thread creation profiling enabled, %s", fn) + prof.closer = func() { + if mp := pprof.Lookup("threadcreate"); mp != nil { + mp.WriteTo(f, 0) + } + f.Close() + logf("profile: thread creation profiling disabled, %s", fn) + } + + case traceMode: + fn := filepath.Join(path, "trace.out") + f, err := os.Create(fn) + if err != nil { + log.Fatalf("profile: could not create trace output file %q: %v", fn, err) + } + if err := startTrace(f); err != nil { + log.Fatalf("profile: could not start trace: %v", err) + } + logf("profile: trace enabled, %s", fn) + prof.closer = func() { + stopTrace() + logf("profile: trace disabled, %s", fn) + } + } + + if !prof.noShutdownHook { + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + <-c + + log.Println("profile: caught interrupt, stopping profiles") + prof.Stop() + + os.Exit(0) + }() + } + + return &prof +} diff --git a/vendor/github.com/pkg/profile/trace.go b/vendor/github.com/pkg/profile/trace.go new file mode 100644 index 0000000000..b349ed8b25 --- /dev/null +++ b/vendor/github.com/pkg/profile/trace.go @@ -0,0 +1,8 @@ +// +build go1.7 + +package profile + +import "runtime/trace" + +var startTrace = trace.Start +var stopTrace = trace.Stop diff --git a/vendor/github.com/pkg/profile/trace16.go b/vendor/github.com/pkg/profile/trace16.go new file mode 100644 index 0000000000..6aa6566ef6 --- /dev/null +++ b/vendor/github.com/pkg/profile/trace16.go @@ -0,0 +1,10 @@ +// +build !go1.7 + +package profile + +import "io" + +// mock trace support for Go 1.6 and earlier. + +func startTrace(w io.Writer) error { return nil } +func stopTrace() {} diff --git a/vendor/modules.txt b/vendor/modules.txt index 381b62751e..f32d09e8aa 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -35,6 +35,8 @@ github.com/matttproud/golang_protobuf_extensions/pbutil github.com/mgutz/ansi # github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir +# github.com/pkg/profile v1.3.0 +github.com/pkg/profile # github.com/pmezard/go-difflib v1.0.0 github.com/pmezard/go-difflib/difflib # github.com/prometheus/client_golang v0.9.2