From dab3190fef82d21043eb4b6b7f377792fd93f0be Mon Sep 17 00:00:00 2001 From: Aaron Prindle Date: Mon, 29 Aug 2016 10:49:29 -0700 Subject: [PATCH] Changed minikube code to use pkg/errors, this is to improve error messages to allow for stacktraces for future error-reporting. Added error messages to .Wrap errors. --- Godeps/Godeps.json | 5 + cmd/minikube/cmd/env.go | 5 +- cmd/minikube/cmd/service.go | 9 +- pkg/minikube/cluster/cluster.go | 116 ++++----- pkg/minikube/cluster/cluster_test.go | 4 +- pkg/minikube/cluster/credentials.go | 5 +- pkg/minikube/cluster/localkube_caching.go | 40 ++- pkg/minikube/kubeconfig/config.go | 14 +- .../kubernetes_versions.go | 7 +- pkg/minikube/notify/notify.go | 9 +- pkg/minikube/sshutil/sshutil.go | 21 +- pkg/minikube/tests/api_mock.go | 3 +- pkg/minikube/tests/ssh_mock.go | 11 +- pkg/util/crypto.go | 35 +-- pkg/util/utils.go | 19 +- pkg/util/utils_test.go | 14 +- vendor/github.com/pkg/errors/.gitignore | 24 ++ vendor/github.com/pkg/errors/.travis.yml | 10 + vendor/github.com/pkg/errors/LICENSE | 23 ++ vendor/github.com/pkg/errors/README.md | 52 ++++ vendor/github.com/pkg/errors/appveyor.yml | 32 +++ vendor/github.com/pkg/errors/errors.go | 238 ++++++++++++++++++ vendor/github.com/pkg/errors/stack.go | 178 +++++++++++++ 23 files changed, 728 insertions(+), 146 deletions(-) create mode 100644 vendor/github.com/pkg/errors/.gitignore create mode 100644 vendor/github.com/pkg/errors/.travis.yml create mode 100644 vendor/github.com/pkg/errors/LICENSE create mode 100644 vendor/github.com/pkg/errors/README.md create mode 100644 vendor/github.com/pkg/errors/appveyor.yml create mode 100644 vendor/github.com/pkg/errors/errors.go create mode 100644 vendor/github.com/pkg/errors/stack.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index f44bab3616..a313eadeba 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1382,6 +1382,11 @@ "ImportPath": "github.com/pkg/browser", "Rev": "9302be274faad99162b9d48ec97b24306872ebb0" }, + { + "ImportPath": "github.com/pkg/errors", + "Comment": "v0.7.1", + "Rev": "17b591df37844cde689f4d5813e5cea0927d8dd2" + }, { "ImportPath": "github.com/pmezard/go-difflib/difflib", "Rev": "d8ed2627bdf02c080bf22230dbb337003b7aba2d" diff --git a/cmd/minikube/cmd/env.go b/cmd/minikube/cmd/env.go index 8696b9ae15..0a9176e548 100644 --- a/cmd/minikube/cmd/env.go +++ b/cmd/minikube/cmd/env.go @@ -28,6 +28,7 @@ import ( "github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine/shell" "github.com/golang/glog" + "github.com/pkg/errors" "github.com/spf13/cobra" "k8s.io/minikube/pkg/minikube/cluster" "k8s.io/minikube/pkg/minikube/constants" @@ -104,12 +105,12 @@ func shellCfgSet(api libmachine.API) (*ShellConfig, error) { host, err := api.Load(constants.MachineName) if err != nil { - return nil, fmt.Errorf("Error getting IP: %s", err) + return nil, errors.Wrap(err, "Error getting IP: ") } ip, err := host.Driver.GetIP() if err != nil { - return nil, fmt.Errorf("Error getting host IP: %s", err) + return nil, errors.Wrap(err, "Error getting host IP: %s") } noProxyVar, noProxyValue := findNoProxyFromEnv() diff --git a/cmd/minikube/cmd/service.go b/cmd/minikube/cmd/service.go index bac32c8d1d..c98da0ec32 100644 --- a/cmd/minikube/cmd/service.go +++ b/cmd/minikube/cmd/service.go @@ -24,6 +24,7 @@ import ( "github.com/docker/machine/libmachine" "github.com/pkg/browser" + "github.com/pkg/errors" "github.com/spf13/cobra" kubeApi "k8s.io/kubernetes/pkg/api" "k8s.io/minikube/pkg/minikube/cluster" @@ -100,13 +101,13 @@ func CheckService(namespace string, service string) error { func CheckEndpointReady(endpoint *kubeApi.Endpoints) error { if len(endpoint.Subsets) == 0 { - fmt.Fprintf(os.Stderr, "Waiting, endpoint for service is not ready yet...\n") - return fmt.Errorf("Endpoint for service is not ready yet\n") + fmt.Fprintf(os.Stderr, "Waiting, endpoint for service is not ready yet...") + return errors.New("Endpoint for service is not ready yet") } for _, subset := range endpoint.Subsets { if len(subset.NotReadyAddresses) != 0 { - fmt.Fprintf(os.Stderr, "Waiting, endpoint for service is not ready yet...\n") - return fmt.Errorf("Endpoint for service is not ready yet\n") + fmt.Fprintf(os.Stderr, "Waiting, endpoint for service is not ready yet...") + return errors.New("Endpoint for service is not ready yet") } } return nil diff --git a/pkg/minikube/cluster/cluster.go b/pkg/minikube/cluster/cluster.go index e810f857a6..c9db6a117d 100644 --- a/pkg/minikube/cluster/cluster.go +++ b/pkg/minikube/cluster/cluster.go @@ -21,7 +21,6 @@ import ( "crypto/sha256" "encoding/hex" "encoding/json" - "errors" "flag" "fmt" "io" @@ -41,6 +40,7 @@ import ( "github.com/docker/machine/libmachine/host" "github.com/docker/machine/libmachine/state" "github.com/golang/glog" + "github.com/pkg/errors" kubeApi "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" @@ -66,7 +66,7 @@ func init() { func StartHost(api libmachine.API, config MachineConfig) (*host.Host, error) { exists, err := api.Exists(constants.MachineName) if err != nil { - return nil, fmt.Errorf("Error checking if host exists: %s", err) + return nil, errors.Wrapf(err, "Error checking if host exists: %s", constants.MachineName) } if !exists { return createHost(api, config) @@ -75,27 +75,26 @@ func StartHost(api libmachine.API, config MachineConfig) (*host.Host, error) { glog.Infoln("Machine exists!") h, err := api.Load(constants.MachineName) if err != nil { - return nil, fmt.Errorf( - "Error loading existing host: %s. Please try running [minikube delete], then run [minikube start] again.", err) + return nil, errors.Wrap(err, "Error loading existing host. Please try running [minikube delete], then run [minikube start] again.") } s, err := h.Driver.GetState() glog.Infoln("Machine state: ", s) if err != nil { - return nil, fmt.Errorf("Error getting state for host: %s", err) + return nil, errors.Wrap(err, "Error getting state for host") } if s != state.Running { if err := h.Driver.Start(); err != nil { - return nil, fmt.Errorf("Error starting stopped host: %s", err) + return nil, errors.Wrapf(err, "Error starting stopped host") } if err := api.Save(h); err != nil { - return nil, fmt.Errorf("Error saving started host: %s", err) + return nil, errors.Wrapf(err, "Error saving started host") } } if err := h.ConfigureAuth(); err != nil { - return nil, fmt.Errorf("Error configuring auth on host: %s", err) + return nil, errors.Wrap(err, "Error configuring auth on host: %s") } return h, nil } @@ -104,10 +103,10 @@ func StartHost(api libmachine.API, config MachineConfig) (*host.Host, error) { func StopHost(api libmachine.API) error { host, err := api.Load(constants.MachineName) if err != nil { - return err + return errors.Wrapf(err, "Error loading host: %s", constants.MachineName) } if err := host.Stop(); err != nil { - return err + return errors.Wrapf(err, "Error stopping host: %s", constants.MachineName) } return nil } @@ -116,7 +115,7 @@ func StopHost(api libmachine.API) error { func DeleteHost(api libmachine.API) error { host, err := api.Load(constants.MachineName) if err != nil { - return err + return errors.Wrapf(err, "Error deleting host: %s", constants.MachineName) } m := util.MultiError{} m.Collect(host.Driver.Remove()) @@ -129,7 +128,7 @@ func GetHostStatus(api libmachine.API) (string, error) { dne := "Does Not Exist" exists, err := api.Exists(constants.MachineName) if err != nil { - return "", err + return "", errors.Wrapf(err, "Error checking that api exists for: ", constants.MachineName) } if !exists { return dne, nil @@ -137,14 +136,17 @@ func GetHostStatus(api libmachine.API) (string, error) { host, err := api.Load(constants.MachineName) if err != nil { - return "", err + return "", errors.Wrapf(err, "Error loading api for: ", constants.MachineName) } s, err := host.Driver.GetState() if s.String() == "" { - return dne, err + return dne, nil } - return s.String(), err + if err != nil { + return "", errors.Wrap(err, "Error getting host state") + } + return s.String(), nil } // GetLocalkubeStatus gets the status of localkube from the host VM. @@ -199,7 +201,7 @@ func StartCluster(h sshAble, kubernetesConfig KubernetesConfig) error { output, err := h.RunSSHCommand(cmd) glog.Infoln(output) if err != nil { - return err + return errors.Wrapf(err, "Error running ssh command: %s", cmd) } } @@ -256,7 +258,7 @@ func NewFileAsset(assetName, targetDir, targetName, permissions string) (*FileAs } file, err := os.Open(f.AssetName) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "Error opening file asset: %s", f.AssetName) } f.reader = file return f, nil @@ -339,18 +341,18 @@ var memoryAssets = []CopyableFile{ func UpdateCluster(h sshAble, d drivers.Driver, config KubernetesConfig) error { client, err := sshutil.NewSSHClient(d) if err != nil { - return err + return errors.Wrap(err, "Error creating new ssh client") } // transfer localkube from cache/asset to vm if localkubeURIWasSpecified(config) { lCacher := localkubeCacher{config} if err = lCacher.updateLocalkubeFromURI(client); err != nil { - return err + return errors.Wrap(err, "Error updating localkube from uri") } } else { if err = updateLocalkubeFromAsset(client); err != nil { - return err + return errors.Wrap(err, "Error updating localkube from asset") } } fileAssets := []CopyableFile{} @@ -380,7 +382,7 @@ func SetupCerts(d drivers.Driver) error { localPath := constants.Minipath ipStr, err := d.GetIP() if err != nil { - return err + return errors.Wrap(err, "Error getting ip from driver") } glog.Infoln("Setting up certificates for IP: %s", ipStr) @@ -390,26 +392,26 @@ func SetupCerts(d drivers.Driver) error { publicPath := filepath.Join(localPath, "apiserver.crt") privatePath := filepath.Join(localPath, "apiserver.key") if err := GenerateCerts(caCert, caKey, publicPath, privatePath, ip); err != nil { - return err + return errors.Wrap(err, "Error generating certs") } client, err := sshutil.NewSSHClient(d) if err != nil { - return err + return errors.Wrap(err, "Error creating new ssh client") } for _, cert := range certs { p := filepath.Join(localPath, cert) data, err := ioutil.ReadFile(p) if err != nil { - return err + return errors.Wrapf(err, "Error reading file: %s", p) } perms := "0644" if strings.HasSuffix(cert, ".key") { perms = "0600" } if err := sshutil.Transfer(bytes.NewReader(data), len(data), util.DefaultCertPath, cert, perms, client); err != nil { - return err + return errors.Wrapf(err, "Error transferring data: %s", string(data)) } } return nil @@ -467,34 +469,34 @@ func (m *MachineConfig) CacheMinikubeISOFromURL() error { // store the miniube-iso inside the .minikube dir response, err := http.Get(m.MinikubeISO) if err != nil { - return err + return errors.Wrapf(err, "Error getting minikube iso at %s via http", m.MinikubeISO) } defer response.Body.Close() isoData, err := ioutil.ReadAll(response.Body) if err != nil { - return err + return errors.Wrap(err, "Error reading minikubeISO url response") } // Validate the ISO if it was the default URL, before writing it to disk. if m.MinikubeISO == constants.DefaultIsoUrl { if !isIsoChecksumValid(&isoData, constants.DefaultIsoShaUrl) { - return fmt.Errorf("Error validating ISO checksum.") + return errors.New("Error validating ISO checksum.") } } if response.StatusCode != http.StatusOK { - return fmt.Errorf("Received %d response from %s while trying to download minikube.iso", response.StatusCode, m.MinikubeISO) + return errors.Errorf("Received %d response from %s while trying to download minikube.iso", response.StatusCode, m.MinikubeISO) } out, err := os.Create(m.GetISOCacheFilepath()) if err != nil { - return err + return errors.Wrap(err, "Error creating minikube iso cache filepath") } defer out.Close() if _, err = out.Write(isoData); err != nil { - return err + return errors.Wrap(err, "Error writing iso data to file") } return nil } @@ -544,7 +546,7 @@ func createHost(api libmachine.API, config MachineConfig) (*host.Host, error) { if config.ShouldCacheMinikubeISO() { if err := config.CacheMinikubeISOFromURL(); err != nil { - return nil, err + return nil, errors.Wrap(err, "Error attempting to cache minikube iso from url") } } @@ -565,12 +567,12 @@ func createHost(api libmachine.API, config MachineConfig) (*host.Host, error) { data, err := json.Marshal(driver) if err != nil { - return nil, err + return nil, errors.Wrap(err, "Error marshalling json") } h, err := api.NewHost(config.VMDriver, data) if err != nil { - return nil, fmt.Errorf("Error creating new host: %s", err) + return nil, errors.Wrap(err, "Error creating new host: %s") } h.HostOptions.AuthOptions.CertDir = constants.Minipath @@ -580,11 +582,11 @@ func createHost(api libmachine.API, config MachineConfig) (*host.Host, error) { if err := api.Create(h); err != nil { // Wait for all the logs to reach the client time.Sleep(2 * time.Second) - return nil, fmt.Errorf("Error creating. %s", err) + return nil, errors.Wrap(err, "Error creating host") } if err := api.Save(h); err != nil { - return nil, fmt.Errorf("Error attempting to save store: %s", err) + return nil, errors.Wrap(err, "Error attempting to save") } return h, nil } @@ -593,11 +595,11 @@ func createHost(api libmachine.API, config MachineConfig) (*host.Host, error) { func GetHostDockerEnv(api libmachine.API) (map[string]string, error) { host, err := checkIfApiExistsAndLoad(api) if err != nil { - return nil, err + return nil, errors.Wrap(err, "Error checking that api exists and loading it") } ip, err := host.Driver.GetIP() if err != nil { - return nil, err + return nil, errors.Wrap(err, "Error getting ip from host") } tcpPrefix := "tcp://" @@ -616,27 +618,27 @@ func GetHostDockerEnv(api libmachine.API) (map[string]string, error) { func GetHostLogs(api libmachine.API) (string, error) { host, err := checkIfApiExistsAndLoad(api) if err != nil { - return "", err + return "", errors.Wrap(err, "Error checking that api exists and loading it") } s, err := host.RunSSHCommand(logsCommand) if err != nil { return "", err } - return s, err + return s, nil } func checkIfApiExistsAndLoad(api libmachine.API) (*host.Host, error) { exists, err := api.Exists(constants.MachineName) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "Error checking that api exists for: ", constants.MachineName) } if !exists { - return nil, fmt.Errorf("Machine does not exist for api.Exists(%s)", constants.MachineName) + return nil, errors.Errorf("Machine does not exist for api.Exists(%s)", constants.MachineName) } host, err := api.Load(constants.MachineName) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "Error loading api for: ", constants.MachineName) } return host, nil } @@ -644,21 +646,21 @@ func checkIfApiExistsAndLoad(api libmachine.API) (*host.Host, error) { func CreateSSHShell(api libmachine.API, args []string) error { host, err := checkIfApiExistsAndLoad(api) if err != nil { - return err + return errors.Wrap(err, "Error checking if api exist and loading it") } currentState, err := host.Driver.GetState() if err != nil { - return err + return errors.Wrap(err, "Error getting state of host") } if currentState != state.Running { - return fmt.Errorf("Error: Cannot run ssh command: Host %q is not running", constants.MachineName) + return errors.Errorf("Error: Cannot run ssh command: Host %q is not running", constants.MachineName) } client, err := host.CreateSSHClient() if err != nil { - return err + return errors.Wrap(err, "Error creating ssh client") } return client.Shell(strings.Join(args, " ")) } @@ -666,17 +668,17 @@ func CreateSSHShell(api libmachine.API, args []string) error { func GetServiceURL(api libmachine.API, namespace, service string) (string, error) { host, err := checkIfApiExistsAndLoad(api) if err != nil { - return "", err + return "", errors.Wrap(err, "Error checking if api exist and loading it") } ip, err := host.Driver.GetIP() if err != nil { - return "", err + return "", errors.Wrap(err, "Error getting ip from host") } port, err := getServicePort(namespace, service) if err != nil { - return "", err + return "", errors.Wrapf(err, "Error getting service port from %s, %s", namespace, service) } return fmt.Sprintf("http://%s:%d", ip, port), nil @@ -693,7 +695,7 @@ type endpointGetter interface { func getServicePort(namespace, service string) (int, error) { services, err := GetKubernetesServicesWithNamespace(namespace) if err != nil { - return 0, err + return 0, errors.Wrapf(err, "Error getting kubernetes service with namespace", namespace) } return getServicePortFromServiceGetter(services, service) } @@ -701,14 +703,14 @@ func getServicePort(namespace, service string) (int, error) { func getServicePortFromServiceGetter(services serviceGetter, service string) (int, error) { svc, err := services.Get(service) if err != nil { - return 0, fmt.Errorf("Error getting %s service: %s", service, err) + return 0, errors.Wrapf(err, "Error getting %s service: %s", service) } nodePort := 0 if len(svc.Spec.Ports) > 0 { nodePort = int(svc.Spec.Ports[0].NodePort) } if nodePort == 0 { - return 0, fmt.Errorf("Service %s does not have a node port. To have one assigned automatically, the service type must be NodePort or LoadBalancer, but this service is of type %s.", service, svc.Spec.Type) + return 0, errors.Errorf("Service %s does not have a node port. To have one assigned automatically, the service type must be NodePort or LoadBalancer, but this service is of type %s.", service, svc.Spec.Type) } return nodePort, nil } @@ -719,11 +721,11 @@ func GetKubernetesClient() (*unversioned.Client, error) { kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) config, err := kubeConfig.ClientConfig() if err != nil { - return nil, fmt.Errorf("Error creating kubeConfig: %s", err) + return nil, errors.Wrap(err, "Error creating kubeConfig: %s") } client, err := unversioned.New(config) if err != nil { - return nil, err + return nil, errors.Wrap(err, "Error creating new client from kubeConfig.ClientConfig()") } return client, nil } @@ -731,7 +733,7 @@ func GetKubernetesClient() (*unversioned.Client, error) { func GetKubernetesServicesWithNamespace(namespace string) (serviceGetter, error) { client, err := GetKubernetesClient() if err != nil { - return nil, err + return nil, errors.Wrap(err, "Error getting kubernetes client") } services := client.Services(namespace) return services, nil @@ -740,7 +742,7 @@ func GetKubernetesServicesWithNamespace(namespace string) (serviceGetter, error) func GetKubernetesEndpointsWithNamespace(namespace string) (endpointGetter, error) { client, err := GetKubernetesClient() if err != nil { - return nil, err + return nil, errors.Wrap(err, "Error getting kubernetes client") } endpoints := client.Endpoints(namespace) return endpoints, nil diff --git a/pkg/minikube/cluster/cluster_test.go b/pkg/minikube/cluster/cluster_test.go index 7a78875b20..23d34beec0 100644 --- a/pkg/minikube/cluster/cluster_test.go +++ b/pkg/minikube/cluster/cluster_test.go @@ -20,7 +20,6 @@ import ( "bytes" "crypto/sha256" "encoding/hex" - "fmt" "io" "io/ioutil" "net/http" @@ -34,6 +33,7 @@ import ( "github.com/docker/machine/libmachine/host" "github.com/docker/machine/libmachine/provision" "github.com/docker/machine/libmachine/state" + "github.com/pkg/errors" "k8s.io/kubernetes/pkg/api" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/tests" @@ -498,7 +498,7 @@ func NewMockServiceGetter() *MockServiceGetter { func (mockServiceGetter *MockServiceGetter) Get(name string) (*api.Service, error) { service, ok := mockServiceGetter.services[name] if !ok { - return nil, fmt.Errorf("Error getting %s service from mockServiceGetter", name) + return nil, errors.Errorf("Error getting %s service from mockServiceGetter", name) } return &service, nil } diff --git a/pkg/minikube/cluster/credentials.go b/pkg/minikube/cluster/credentials.go index 9157643d1b..26f42daac8 100644 --- a/pkg/minikube/cluster/credentials.go +++ b/pkg/minikube/cluster/credentials.go @@ -19,6 +19,7 @@ package cluster import ( "net" + "github.com/pkg/errors" "k8s.io/minikube/pkg/util" ) @@ -30,13 +31,13 @@ var ( func GenerateCerts(caCert, caKey, pub, priv string, ip net.IP) error { if !(util.CanReadFile(caCert) && util.CanReadFile(caKey)) { if err := util.GenerateCACert(caCert, caKey); err != nil { - return err + return errors.Wrap(err, "Error generating certificate") } } ips := []net.IP{ip, internalIP} if err := util.GenerateSignedCert(pub, priv, ips, util.GetAlternateDNS(util.DefaultDNSDomain), caCert, caKey); err != nil { - return err + return errors.Wrap(err, "Error generating signed cert") } return nil } diff --git a/pkg/minikube/cluster/localkube_caching.go b/pkg/minikube/cluster/localkube_caching.go index ef806c9bf3..21b2acc8ab 100644 --- a/pkg/minikube/cluster/localkube_caching.go +++ b/pkg/minikube/cluster/localkube_caching.go @@ -18,7 +18,6 @@ package cluster import ( "bytes" - "errors" "io" "io/ioutil" "net/http" @@ -27,7 +26,7 @@ import ( "path/filepath" "strings" - "github.com/golang/glog" + "github.com/pkg/errors" "golang.org/x/crypto/ssh" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/sshutil" @@ -37,12 +36,11 @@ import ( func updateLocalkubeFromAsset(client *ssh.Client) error { contents, err := Asset("out/localkube") if err != nil { - glog.Infof("Error loading asset out/localkube: %s", err) - return err + return errors.Wrap(err, "Error loading asset out/localkube") } if err := sshutil.Transfer(bytes.NewReader(contents), len(contents), "/usr/local/bin", "localkube", "0777", client); err != nil { - return err + return errors.Wrap(err, "Error transferring localkube via ssh") } return nil } @@ -68,12 +66,12 @@ func (l *localkubeCacher) cacheLocalkube(body io.ReadCloser) error { // store localkube inside the .minikube dir out, err := os.Create(l.getLocalkubeCacheFilepath()) if err != nil { - return err + return errors.Wrap(err, "Error creating localkube local file") } defer out.Close() defer body.Close() if _, err = io.Copy(out, body); err != nil { - return err + return errors.Wrap(err, "Error writing localkube to file") } return nil } @@ -85,17 +83,19 @@ func (l *localkubeCacher) downloadAndCacheLocalkube() error { url, err := util.GetLocalkubeDownloadURL(l.k8sConf.KubernetesVersion, constants.LocalkubeLinuxFilename) if err != nil { - return err + return errors.Wrap(err, "Error getting localkube download url") } resp, err = http.Get(url) - return err + if err != nil { + return errors.Wrap(err, "Error downloading localkube via http") + } + return nil } - if err = util.Retry(5, downloader); err != nil { - return err + return errors.Wrap(err, "Max error attempts retrying localkube downloader") } if err = l.cacheLocalkube(resp.Body); err != nil { - return err + return errors.Wrap(err, "Error caching localkube to local directory") } return nil } @@ -103,7 +103,7 @@ func (l *localkubeCacher) downloadAndCacheLocalkube() error { func (l *localkubeCacher) updateLocalkubeFromURI(client *ssh.Client) error { urlObj, err := url.Parse(l.k8sConf.KubernetesVersion) if err != nil { - return err + return errors.Wrap(err, "Error parsing --kubernetes-version url") } if urlObj.Scheme == fileScheme { return l.updateLocalkubeFromFile(client) @@ -115,11 +115,11 @@ func (l *localkubeCacher) updateLocalkubeFromURI(client *ssh.Client) error { func (l *localkubeCacher) updateLocalkubeFromURL(client *ssh.Client) error { if !l.isLocalkubeCached() { if err := l.downloadAndCacheLocalkube(); err != nil { - return err + return errors.Wrap(err, "Error attempting to download and cache localkube") } } if err := l.transferCachedLocalkubeToVM(client); err != nil { - return err + return errors.Wrap(err, "Error transferring cached localkube to VM") } return nil } @@ -127,13 +127,12 @@ func (l *localkubeCacher) updateLocalkubeFromURL(client *ssh.Client) error { func (l *localkubeCacher) transferCachedLocalkubeToVM(client *ssh.Client) error { contents, err := ioutil.ReadFile(l.getLocalkubeCacheFilepath()) if err != nil { - glog.Infof("Error loading asset out/localkube: %s", err) - return err + return errors.Wrap(err, "Error reading file: localkube cache filepath") } if err = sshutil.Transfer(bytes.NewReader(contents), len(contents), "/usr/local/bin", "localkube", "0777", client); err != nil { - return err + return errors.Wrap(err, "Error transferring cached localkube to VM via ssh") } return nil } @@ -143,12 +142,11 @@ func (l *localkubeCacher) updateLocalkubeFromFile(client *ssh.Client) error { path = filepath.FromSlash(path) contents, err := ioutil.ReadFile(path) if err != nil { - glog.Infof("Error loading file %s: %s", path, err) - return err + return errors.Wrapf(err, "Error reading localkube file at %s", path) } if err := sshutil.Transfer(bytes.NewReader(contents), len(contents), "/usr/local/bin", "localkube", "0777", client); err != nil { - return err + return errors.Wrapf(err, "Error transferring specified localkube file at %s to VM via ssh", path) } return nil } diff --git a/pkg/minikube/kubeconfig/config.go b/pkg/minikube/kubeconfig/config.go index 2ec88133de..874daa1435 100644 --- a/pkg/minikube/kubeconfig/config.go +++ b/pkg/minikube/kubeconfig/config.go @@ -17,12 +17,12 @@ limitations under the License. package kubeconfig import ( - "fmt" "io/ioutil" "os" "path/filepath" "github.com/golang/glog" + "github.com/pkg/errors" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/latest" "k8s.io/kubernetes/pkg/runtime" @@ -35,13 +35,13 @@ func ReadConfigOrNew(filename string) (*api.Config, error) { if os.IsNotExist(err) { return api.NewConfig(), nil } else if err != nil { - return nil, err + return nil, errors.Wrapf(err, "Error reading file", filename) } // decode config, empty if no bytes config, err := decode(data) if err != nil { - return nil, fmt.Errorf("could not read config: %v", err) + return nil, errors.Errorf("could not read config: %v", err) } // initialize nil maps @@ -68,20 +68,20 @@ func WriteConfig(config *api.Config, filename string) error { // encode config to YAML data, err := runtime.Encode(latest.Codec, config) if err != nil { - return fmt.Errorf("could not write to '%s': failed to encode config: %v", filename, err) + return errors.Errorf("could not write to '%s': failed to encode config: %v", filename, err) } // create parent dir if doesn't exist dir := filepath.Dir(filename) if _, err := os.Stat(dir); os.IsNotExist(err) { if err = os.MkdirAll(dir, 0755); err != nil { - return err + return errors.Wrapf(err, "Error creating directory: %s", dir) } } // write with restricted permissions if err := ioutil.WriteFile(filename, data, 0600); err != nil { - return err + return errors.Wrapf(err, "Error writing file %s", filename) } return nil } @@ -96,7 +96,7 @@ func decode(data []byte) (*api.Config, error) { config, _, err := latest.Codec.Decode(data, nil, nil) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "Error decoding config from data: %s", string(data)) } return config.(*api.Config), nil diff --git a/pkg/minikube/kubernetes_versions/kubernetes_versions.go b/pkg/minikube/kubernetes_versions/kubernetes_versions.go index c8b2a30f67..3b955e0fcb 100644 --- a/pkg/minikube/kubernetes_versions/kubernetes_versions.go +++ b/pkg/minikube/kubernetes_versions/kubernetes_versions.go @@ -23,6 +23,7 @@ import ( "net/http" "github.com/golang/glog" + "github.com/pkg/errors" ) const kubernetesVersionGCSURL = "https://storage.googleapis.com/minikube/k8s_releases.json" @@ -53,7 +54,7 @@ type k8sReleases []k8sRelease func getJson(url string, target *k8sReleases) error { r, err := http.Get(url) if err != nil { - return err + return errors.Wrapf(err, "Error getting json from url: %s via http", url) } defer r.Body.Close() @@ -63,10 +64,10 @@ func getJson(url string, target *k8sReleases) error { func getK8sVersionsFromURL(url string) (k8sReleases, error) { var k8sVersions k8sReleases if err := getJson(url, &k8sVersions); err != nil { - return k8sReleases{}, err + return k8sReleases{}, errors.Wrapf(err, "Error getting json via http with url: %s", url) } if len(k8sVersions) == 0 { - return k8sReleases{}, fmt.Errorf("There were no json k8s Releases at the url specified: %s", url) + return k8sReleases{}, errors.Errorf("There were no json k8s Releases at the url specified: %s", url) } return k8sVersions, nil } diff --git a/pkg/minikube/notify/notify.go b/pkg/minikube/notify/notify.go index 9be9a7338c..f9da86cd0d 100644 --- a/pkg/minikube/notify/notify.go +++ b/pkg/minikube/notify/notify.go @@ -27,6 +27,7 @@ import ( "github.com/blang/semver" "github.com/golang/glog" + "github.com/pkg/errors" "github.com/spf13/viper" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" @@ -90,7 +91,7 @@ type releases []release func getJson(url string, target *releases) error { r, err := http.Get(url) if err != nil { - return err + return errors.Wrap(err, "Error getting minikube version url via http") } defer r.Body.Close() @@ -101,10 +102,10 @@ func getLatestVersionFromURL(url string) (semver.Version, error) { var releases releases glog.Infof("Checking for updates...") if err := getJson(url, &releases); err != nil { - return semver.Version{}, err + return semver.Version{}, errors.Wrap(err, "Error getting json from minikube version url") } if len(releases) == 0 { - return semver.Version{}, fmt.Errorf("There were no json releases at the url specified: %s", url) + return semver.Version{}, errors.Errorf("There were no json releases at the url specified: %s", url) } latestVersionString := releases[0].Name return semver.Make(strings.TrimPrefix(latestVersionString, version.VersionPrefix)) @@ -113,7 +114,7 @@ func getLatestVersionFromURL(url string) (semver.Version, error) { func writeTimeToFile(path string, inputTime time.Time) error { err := ioutil.WriteFile(path, []byte(inputTime.Format(timeLayout)), 0644) if err != nil { - return fmt.Errorf("Error writing current update time to file: %s", err) + return errors.Wrap(err, "Error writing current update time to file: ") } return nil } diff --git a/pkg/minikube/sshutil/sshutil.go b/pkg/minikube/sshutil/sshutil.go index 2bbd90b52d..577813e45a 100644 --- a/pkg/minikube/sshutil/sshutil.go +++ b/pkg/minikube/sshutil/sshutil.go @@ -24,6 +24,7 @@ import ( "github.com/docker/machine/libmachine/drivers" machinessh "github.com/docker/machine/libmachine/ssh" + "github.com/pkg/errors" "golang.org/x/crypto/ssh" ) @@ -39,7 +40,7 @@ type SSHSession interface { func NewSSHClient(d drivers.Driver) (*ssh.Client, error) { h, err := newSSHHost(d) if err != nil { - return nil, err + return nil, errors.Wrap(err, "Error creating new ssh host from driver") } auth := &machinessh.Auth{} @@ -48,12 +49,12 @@ func NewSSHClient(d drivers.Driver) (*ssh.Client, error) { } config, err := machinessh.NewNativeConfig(h.Username, auth) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "Error creating new native config from ssh using: %s, %s", h.Username, auth) } client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", h.IP, h.Port), &config) if err != nil { - return nil, err + return nil, errors.Wrap(err, "Error dialing tcp via ssh client") } return client, nil } @@ -65,18 +66,18 @@ func Transfer(reader io.Reader, readerLen int, remotedir, filename string, perm mkdirCmd := fmt.Sprintf("sudo mkdir -p %s", remotedir) for _, cmd := range []string{deleteCmd, mkdirCmd} { if err := RunCommand(c, cmd); err != nil { - return err + return errors.Wrapf(err, "Error running command: %s", cmd) } } s, err := c.NewSession() if err != nil { - return err + return errors.Wrap(err, "Error creating new session via ssh client") } w, err := s.StdinPipe() if err != nil { - return err + return errors.Wrap(err, "Error accessing StdinPipe via ssh session") } // The scpcmd below *should not* return until all data is copied and the // StdinPipe is closed. But let's use a WaitGroup to make it expicit. @@ -92,7 +93,7 @@ func Transfer(reader io.Reader, readerLen int, remotedir, filename string, perm }() scpcmd := fmt.Sprintf("sudo /usr/local/bin/scp -t %s", remotedir) if err := s.Run(scpcmd); err != nil { - return err + return errors.Wrap(err, "Error running scp command") } wg.Wait() @@ -103,7 +104,7 @@ func RunCommand(c *ssh.Client, cmd string) error { s, err := c.NewSession() defer s.Close() if err != nil { - return err + return errors.Wrap(err, "Error creating new session for ssh client") } return s.Run(cmd) @@ -120,11 +121,11 @@ func newSSHHost(d drivers.Driver) (*sshHost, error) { ip, err := d.GetSSHHostname() if err != nil { - return nil, err + return nil, errors.Wrap(err, "Error getting ssh host name for driver") } port, err := d.GetSSHPort() if err != nil { - return nil, err + return nil, errors.Wrap(err, "Error getting ssh port for driver") } return &sshHost{ IP: ip, diff --git a/pkg/minikube/tests/api_mock.go b/pkg/minikube/tests/api_mock.go index fe291cca2b..a4d9a7ea6d 100644 --- a/pkg/minikube/tests/api_mock.go +++ b/pkg/minikube/tests/api_mock.go @@ -25,6 +25,7 @@ import ( "github.com/docker/machine/libmachine/host" "github.com/docker/machine/libmachine/mcnerror" "github.com/docker/machine/libmachine/state" + "github.com/pkg/errors" ) // MockAPI is a struct used to mock out libmachine.API @@ -51,7 +52,7 @@ func (api *MockAPI) Close() error { func (api *MockAPI) NewHost(driverName string, rawDriver []byte) (*host.Host, error) { var driver MockDriver if err := json.Unmarshal(rawDriver, &driver); err != nil { - return nil, err + return nil, errors.Wrap(err, "Error unmarshalling json") } h := &host.Host{ DriverName: driverName, diff --git a/pkg/minikube/tests/ssh_mock.go b/pkg/minikube/tests/ssh_mock.go index 36682336fe..fb3c641c19 100644 --- a/pkg/minikube/tests/ssh_mock.go +++ b/pkg/minikube/tests/ssh_mock.go @@ -25,6 +25,7 @@ import ( "strconv" "github.com/golang/glog" + "github.com/pkg/errors" "golang.org/x/crypto/ssh" ) @@ -50,11 +51,11 @@ func NewSSHServer() (*SSHServer, error) { private, err := rsa.GenerateKey(rand.Reader, 2014) if err != nil { - return nil, err + return nil, errors.Wrap(err, "Error generating RSA key") } signer, err := ssh.NewSignerFromKey(private) if err != nil { - return nil, err + return nil, errors.Wrap(err, "Error creating signer from key") } s.Config.AddHostKey(signer) return s, nil @@ -68,7 +69,7 @@ type execRequest struct { func (s *SSHServer) Start() (int, error) { listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { - return 0, err + return 0, errors.Wrap(err, "Error creating tcp listener for ssh server") } // Main loop, listen for connections and store the commands. @@ -126,11 +127,11 @@ func (s *SSHServer) Start() (int, error) { // Parse and return the port. _, p, err := net.SplitHostPort(listener.Addr().String()) if err != nil { - return 0, err + return 0, errors.Wrap(err, "Error splitting host port") } port, err := strconv.Atoi(p) if err != nil { - return 0, err + return 0, errors.Wrap(err, "Error converting port string to integer") } return port, nil } diff --git a/pkg/util/crypto.go b/pkg/util/crypto.go index 756864fe26..337372207b 100644 --- a/pkg/util/crypto.go +++ b/pkg/util/crypto.go @@ -23,19 +23,20 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/pem" - "fmt" "io/ioutil" "math/big" "net" "os" "path/filepath" "time" + + "github.com/pkg/errors" ) func GenerateCACert(certPath, keyPath string) error { priv, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { - return err + return errors.Wrap(err, "Error generating rsa key") } template := x509.Certificate{ @@ -62,27 +63,27 @@ func GenerateCACert(certPath, keyPath string) error { func GenerateSignedCert(certPath, keyPath string, ips []net.IP, alternateDNS []string, signerCertPath, signerKeyPath string) error { signerCertBytes, err := ioutil.ReadFile(signerCertPath) if err != nil { - return err + return errors.Wrap(err, "Error reading file: signerCertPath") } decodedSignerCert, _ := pem.Decode(signerCertBytes) if decodedSignerCert == nil { - return fmt.Errorf("Unable to decode certificate.") + return errors.New("Unable to decode certificate.") } signerCert, err := x509.ParseCertificate(decodedSignerCert.Bytes) if err != nil { - return err + return errors.Wrap(err, "Error parsing certificate: decodedSignerCert.Bytes") } signerKeyBytes, err := ioutil.ReadFile(signerKeyPath) if err != nil { - return err + return errors.Wrap(err, "Error reading file: signerKeyPath") } decodedSignerKey, _ := pem.Decode(signerKeyBytes) if decodedSignerKey == nil { - return fmt.Errorf("Unable to decode key.") + return errors.New("Unable to decode key.") } signerKey, err := x509.ParsePKCS1PrivateKey(decodedSignerKey.Bytes) if err != nil { - return err + return errors.Wrap(err, "Error parsing prive key: decodedSignerKey.Bytes") } template := x509.Certificate{ @@ -103,7 +104,7 @@ func GenerateSignedCert(certPath, keyPath string, ips []net.IP, alternateDNS []s priv, err := loadOrGeneratePrivateKey(keyPath) if err != nil { - return err + return errors.Wrap(err, "Error loading or generating private key: keyPath") } return writeCertsAndKeys(&template, certPath, priv, keyPath, signerCert, signerKey) @@ -122,7 +123,7 @@ func loadOrGeneratePrivateKey(keyPath string) (*rsa.PrivateKey, error) { } priv, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { - return nil, err + return nil, errors.Wrap(err, "Error generating RSA key") } return priv, nil } @@ -130,31 +131,31 @@ func loadOrGeneratePrivateKey(keyPath string) (*rsa.PrivateKey, error) { func writeCertsAndKeys(template *x509.Certificate, certPath string, signeeKey *rsa.PrivateKey, keyPath string, parent *x509.Certificate, signingKey *rsa.PrivateKey) error { derBytes, err := x509.CreateCertificate(rand.Reader, template, parent, &signeeKey.PublicKey, signingKey) if err != nil { - return err + return errors.Wrap(err, "Error creating certificate") } certBuffer := bytes.Buffer{} if err := pem.Encode(&certBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { - return err + return errors.Wrap(err, "Error encoding certificate") } keyBuffer := bytes.Buffer{} if err := pem.Encode(&keyBuffer, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(signeeKey)}); err != nil { - return err + return errors.Wrap(err, "Error encoding key") } if err := os.MkdirAll(filepath.Dir(certPath), os.FileMode(0755)); err != nil { - return err + return errors.Wrap(err, "Error creating certificate directory") } if err := ioutil.WriteFile(certPath, certBuffer.Bytes(), os.FileMode(0644)); err != nil { - return err + return errors.Wrap(err, "Error writing certificate to cert path") } if err := os.MkdirAll(filepath.Dir(keyPath), os.FileMode(0755)); err != nil { - return err + return errors.Wrap(err, "Error creating key directory") } if err := ioutil.WriteFile(keyPath, keyBuffer.Bytes(), os.FileMode(0600)); err != nil { - return err + return errors.Wrap(err, "Error writing key file") } return nil diff --git a/pkg/util/utils.go b/pkg/util/utils.go index 545a487cdd..fad0176880 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -25,6 +25,7 @@ import ( "time" "github.com/blang/semver" + "github.com/pkg/errors" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/version" ) @@ -88,7 +89,7 @@ func RetryAfter(attempts int, callback func() error, d time.Duration) (err error func GetLocalkubeDownloadURL(versionOrURL string, filename string) (string, error) { urlObj, err := url.Parse(versionOrURL) if err != nil { - return "", err + return "", errors.Wrap(err, "Error parsing localkube download url") } if urlObj.IsAbs() { // scheme was specified in input, is a valid URI. @@ -100,7 +101,7 @@ func GetLocalkubeDownloadURL(versionOrURL string, filename string) (string, erro versionOrURL = "v" + versionOrURL } if _, err = semver.Make(strings.TrimPrefix(versionOrURL, version.VersionPrefix)); err != nil { - return "", err + return "", errors.Wrap(err, "Error creating semver version from localkube version input string") } return fmt.Sprintf("%s%s/%s", constants.LocalkubeDownloadURLPrefix, versionOrURL, filename), nil } @@ -124,13 +125,23 @@ func (m MultiError) ToError() error { for _, err := range m.Errors { errStrings = append(errStrings, err.Error()) } - return fmt.Errorf(strings.Join(errStrings, "\n")) + return errors.New(strings.Join(errStrings, "\n")) +} + +type ServiceContext struct { + Service string `json:"service"` + Version string `json:"version"` +} + +type Message struct { + Message string `json:"message"` + ServiceContext `json:"serviceContext"` } func IsDirectory(path string) (bool, error) { fileInfo, err := os.Stat(path) if err != nil { - return false, err + return false, errors.Wrapf(err, "Error calling os.Stat on file %s", path) } return fileInfo.IsDir(), nil } diff --git a/pkg/util/utils_test.go b/pkg/util/utils_test.go index eae8574186..b4f50f0ec7 100644 --- a/pkg/util/utils_test.go +++ b/pkg/util/utils_test.go @@ -17,19 +17,19 @@ limitations under the License. package util import ( - "fmt" "testing" + "github.com/pkg/errors" "k8s.io/minikube/pkg/minikube/constants" ) // Returns a function that will return n errors, then return successfully forever. func errorGenerator(n int) func() error { - errors := 0 + errorCount := 0 return func() (err error) { - if errors < n { - errors += 1 - return fmt.Errorf("Error!") + if errorCount < n { + errorCount += 1 + return errors.New("Error!") } return nil } @@ -93,8 +93,8 @@ func TestGetLocalkubeDownloadURL(t *testing.T) { func TestMultiError(t *testing.T) { m := MultiError{} - m.Collect(fmt.Errorf("Error 1")) - m.Collect(fmt.Errorf("Error 2")) + m.Collect(errors.New("Error 1")) + m.Collect(errors.New("Error 2")) err := m.ToError() expected := `Error 1 diff --git a/vendor/github.com/pkg/errors/.gitignore b/vendor/github.com/pkg/errors/.gitignore new file mode 100644 index 0000000000..daf913b1b3 --- /dev/null +++ b/vendor/github.com/pkg/errors/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/pkg/errors/.travis.yml b/vendor/github.com/pkg/errors/.travis.yml new file mode 100644 index 0000000000..024e28466e --- /dev/null +++ b/vendor/github.com/pkg/errors/.travis.yml @@ -0,0 +1,10 @@ +language: go +go_import_path: github.com/pkg/errors +go: + - 1.4.3 + - 1.5.4 + - 1.6.2 + - tip + +script: + - go test -v ./... diff --git a/vendor/github.com/pkg/errors/LICENSE b/vendor/github.com/pkg/errors/LICENSE new file mode 100644 index 0000000000..835ba3e755 --- /dev/null +++ b/vendor/github.com/pkg/errors/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2015, 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 HOLDER 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/errors/README.md b/vendor/github.com/pkg/errors/README.md new file mode 100644 index 0000000000..273db3c98a --- /dev/null +++ b/vendor/github.com/pkg/errors/README.md @@ -0,0 +1,52 @@ +# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) + +Package errors provides simple error handling primitives. + +`go get github.com/pkg/errors` + +The traditional error handling idiom in Go is roughly akin to +```go +if err != nil { + return err +} +``` +which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. + +## Adding context to an error + +The errors.Wrap function returns a new error that adds context to the original error. For example +```go +_, err := ioutil.ReadAll(r) +if err != nil { + return errors.Wrap(err, "read failed") +} +``` +## Retrieving the cause of an error + +Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. +```go +type causer interface { + Cause() error +} +``` +`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: +```go +switch err := errors.Cause(err).(type) { +case *MyError: + // handle specifically +default: + // unknown error +} +``` + +[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). + +## Contributing + +We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. + +Before proposing a change, please discuss your change by raising an issue. + +## Licence + +BSD-2-Clause diff --git a/vendor/github.com/pkg/errors/appveyor.yml b/vendor/github.com/pkg/errors/appveyor.yml new file mode 100644 index 0000000000..a932eade02 --- /dev/null +++ b/vendor/github.com/pkg/errors/appveyor.yml @@ -0,0 +1,32 @@ +version: build-{build}.{branch} + +clone_folder: C:\gopath\src\github.com\pkg\errors +shallow_clone: true # for startup speed + +environment: + GOPATH: C:\gopath + +platform: + - x64 + +# http://www.appveyor.com/docs/installed-software +install: + # some helpful output for debugging builds + - go version + - go env + # pre-installed MinGW at C:\MinGW is 32bit only + # but MSYS2 at C:\msys64 has mingw64 + - set PATH=C:\msys64\mingw64\bin;%PATH% + - gcc --version + - g++ --version + +build_script: + - go install -v ./... + +test_script: + - set PATH=C:\gopath\bin;%PATH% + - go test -v ./... + +#artifacts: +# - path: '%GOPATH%\bin\*.exe' +deploy: off diff --git a/vendor/github.com/pkg/errors/errors.go b/vendor/github.com/pkg/errors/errors.go new file mode 100644 index 0000000000..1c9731ac6c --- /dev/null +++ b/vendor/github.com/pkg/errors/errors.go @@ -0,0 +1,238 @@ +// Package errors provides simple error handling primitives. +// +// The traditional error handling idiom in Go is roughly akin to +// +// if err != nil { +// return err +// } +// +// which applied recursively up the call stack results in error reports +// without context or debugging information. The errors package allows +// programmers to add context to the failure path in their code in a way +// that does not destroy the original value of the error. +// +// Adding context to an error +// +// The errors.Wrap function returns a new error that adds context to the +// original error. For example +// +// _, err := ioutil.ReadAll(r) +// if err != nil { +// return errors.Wrap(err, "read failed") +// } +// +// Retrieving the cause of an error +// +// Using errors.Wrap constructs a stack of errors, adding context to the +// preceding error. Depending on the nature of the error it may be necessary +// to reverse the operation of errors.Wrap to retrieve the original error +// for inspection. Any error value which implements this interface +// +// type causer interface { +// Cause() error +// } +// +// can be inspected by errors.Cause. errors.Cause will recursively retrieve +// the topmost error which does not implement causer, which is assumed to be +// the original cause. For example: +// +// switch err := errors.Cause(err).(type) { +// case *MyError: +// // handle specifically +// default: +// // unknown error +// } +// +// causer interface is not exported by this package, but is considered a part +// of stable public API. +// +// Formatted printing of errors +// +// All error values returned from this package implement fmt.Formatter and can +// be formatted by the fmt package. The following verbs are supported +// +// %s print the error. If the error has a Cause it will be +// printed recursively +// %v see %s +// %+v extended format. Each Frame of the error's StackTrace will +// be printed in detail. +// +// Retrieving the stack trace of an error or wrapper +// +// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are +// invoked. This information can be retrieved with the following interface. +// +// type stackTracer interface { +// StackTrace() errors.StackTrace +// } +// +// Where errors.StackTrace is defined as +// +// type StackTrace []Frame +// +// The Frame type represents a call site in the stack trace. Frame supports +// the fmt.Formatter interface that can be used for printing information about +// the stack trace of this error. For example: +// +// if err, ok := err.(stackTracer); ok { +// for _, f := range err.StackTrace() { +// fmt.Printf("%+s:%d", f) +// } +// } +// +// stackTracer interface is not exported by this package, but is considered a part +// of stable public API. +// +// See the documentation for Frame.Format for more details. +package errors + +import ( + "fmt" + "io" +) + +// New returns an error with the supplied message. +// New also records the stack trace at the point it was called. +func New(message string) error { + return &fundamental{ + msg: message, + stack: callers(), + } +} + +// Errorf formats according to a format specifier and returns the string +// as a value that satisfies error. +// Errorf also records the stack trace at the point it was called. +func Errorf(format string, args ...interface{}) error { + return &fundamental{ + msg: fmt.Sprintf(format, args...), + stack: callers(), + } +} + +// fundamental is an error that has a message and a stack, but no caller. +type fundamental struct { + msg string + *stack +} + +func (f *fundamental) Error() string { return f.msg } + +func (f *fundamental) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + io.WriteString(s, f.msg) + f.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, f.msg) + case 'q': + fmt.Fprintf(s, "%q", f.msg) + } +} + +type withStack struct { + error + *stack +} + +func (w *withStack) Cause() error { return w.error } + +func (w *withStack) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v", w.Cause()) + w.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, w.Error()) + case 'q': + fmt.Fprintf(s, "%q", w.Error()) + } +} + +// Wrap returns an error annotating err with message. +// If err is nil, Wrap returns nil. +func Wrap(err error, message string) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: message, + } + return &withStack{ + err, + callers(), + } +} + +// Wrapf returns an error annotating err with the format specifier. +// If err is nil, Wrapf returns nil. +func Wrapf(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } + return &withStack{ + err, + callers(), + } +} + +type withMessage struct { + cause error + msg string +} + +func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } +func (w *withMessage) Cause() error { return w.cause } + +func (w *withMessage) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v\n", w.Cause()) + io.WriteString(s, w.msg) + return + } + fallthrough + case 's', 'q': + io.WriteString(s, w.Error()) + } +} + +// Cause returns the underlying cause of the error, if possible. +// An error value has a cause if it implements the following +// interface: +// +// type causer interface { +// Cause() error +// } +// +// If the error does not implement Cause, the original error will +// be returned. If the error is nil, nil will be returned without further +// investigation. +func Cause(err error) error { + type causer interface { + Cause() error + } + + for err != nil { + cause, ok := err.(causer) + if !ok { + break + } + err = cause.Cause() + } + return err +} diff --git a/vendor/github.com/pkg/errors/stack.go b/vendor/github.com/pkg/errors/stack.go new file mode 100644 index 0000000000..6b1f2891a5 --- /dev/null +++ b/vendor/github.com/pkg/errors/stack.go @@ -0,0 +1,178 @@ +package errors + +import ( + "fmt" + "io" + "path" + "runtime" + "strings" +) + +// Frame represents a program counter inside a stack frame. +type Frame uintptr + +// pc returns the program counter for this frame; +// multiple frames may have the same PC value. +func (f Frame) pc() uintptr { return uintptr(f) - 1 } + +// file returns the full path to the file that contains the +// function for this Frame's pc. +func (f Frame) file() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + file, _ := fn.FileLine(f.pc()) + return file +} + +// line returns the line number of source code of the +// function for this Frame's pc. +func (f Frame) line() int { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return 0 + } + _, line := fn.FileLine(f.pc()) + return line +} + +// Format formats the frame according to the fmt.Formatter interface. +// +// %s source file +// %d source line +// %n function name +// %v equivalent to %s:%d +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+s path of source file relative to the compile time GOPATH +// %+v equivalent to %+s:%d +func (f Frame) Format(s fmt.State, verb rune) { + switch verb { + case 's': + switch { + case s.Flag('+'): + pc := f.pc() + fn := runtime.FuncForPC(pc) + if fn == nil { + io.WriteString(s, "unknown") + } else { + file, _ := fn.FileLine(pc) + fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) + } + default: + io.WriteString(s, path.Base(f.file())) + } + case 'd': + fmt.Fprintf(s, "%d", f.line()) + case 'n': + name := runtime.FuncForPC(f.pc()).Name() + io.WriteString(s, funcname(name)) + case 'v': + f.Format(s, 's') + io.WriteString(s, ":") + f.Format(s, 'd') + } +} + +// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). +type StackTrace []Frame + +func (st StackTrace) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case s.Flag('+'): + for _, f := range st { + fmt.Fprintf(s, "\n%+v", f) + } + case s.Flag('#'): + fmt.Fprintf(s, "%#v", []Frame(st)) + default: + fmt.Fprintf(s, "%v", []Frame(st)) + } + case 's': + fmt.Fprintf(s, "%s", []Frame(st)) + } +} + +// stack represents a stack of program counters. +type stack []uintptr + +func (s *stack) Format(st fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case st.Flag('+'): + for _, pc := range *s { + f := Frame(pc) + fmt.Fprintf(st, "\n%+v", f) + } + } + } +} + +func (s *stack) StackTrace() StackTrace { + f := make([]Frame, len(*s)) + for i := 0; i < len(f); i++ { + f[i] = Frame((*s)[i]) + } + return f +} + +func callers() *stack { + const depth = 32 + var pcs [depth]uintptr + n := runtime.Callers(3, pcs[:]) + var st stack = pcs[0:n] + return &st +} + +// funcname removes the path prefix component of a function's name reported by func.Name(). +func funcname(name string) string { + i := strings.LastIndex(name, "/") + name = name[i+1:] + i = strings.Index(name, ".") + return name[i+1:] +} + +func trimGOPATH(name, file string) string { + // Here we want to get the source file path relative to the compile time + // GOPATH. As of Go 1.6.x there is no direct way to know the compiled + // GOPATH at runtime, but we can infer the number of path segments in the + // GOPATH. We note that fn.Name() returns the function name qualified by + // the import path, which does not include the GOPATH. Thus we can trim + // segments from the beginning of the file path until the number of path + // separators remaining is one more than the number of path separators in + // the function name. For example, given: + // + // GOPATH /home/user + // file /home/user/src/pkg/sub/file.go + // fn.Name() pkg/sub.Type.Method + // + // We want to produce: + // + // pkg/sub/file.go + // + // From this we can easily see that fn.Name() has one less path separator + // than our desired output. We count separators from the end of the file + // path until it finds two more than in the function name and then move + // one character forward to preserve the initial path segment without a + // leading separator. + const sep = "/" + goal := strings.Count(name, sep) + 2 + i := len(file) + for n := 0; n < goal; n++ { + i = strings.LastIndex(file[:i], sep) + if i == -1 { + // not enough separators found, set i so that the slice expression + // below leaves file unmodified + i = -len(sep) + break + } + } + // get back to 0 or trim the leading separator + file = file[i+len(sep):] + return file +}