diff --git a/cmd/dcos-upgrade.go b/cmd/dcos-upgrade.go index e250f25cd6..301f6a1efb 100644 --- a/cmd/dcos-upgrade.go +++ b/cmd/dcos-upgrade.go @@ -2,7 +2,6 @@ package cmd import ( "encoding/json" - "fmt" "io/ioutil" "os" "path" @@ -15,6 +14,7 @@ import ( "github.com/Azure/acs-engine/pkg/i18n" "github.com/Azure/acs-engine/pkg/operations/dcosupgrade" "github.com/leonelquinteros/gotext" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -76,28 +76,28 @@ func (uc *dcosUpgradeCmd) validate(cmd *cobra.Command) error { uc.locale, err = i18n.LoadTranslations() if err != nil { - return fmt.Errorf("error loading translation files: %s", err.Error()) + return errors.Wrap(err, "error loading translation files") } if len(uc.resourceGroupName) == 0 { cmd.Usage() - return fmt.Errorf("--resource-group must be specified") + return errors.New("--resource-group must be specified") } if len(uc.location) == 0 { cmd.Usage() - return fmt.Errorf("--location must be specified") + return errors.New("--location must be specified") } uc.location = helpers.NormalizeAzureRegion(uc.location) if len(uc.upgradeVersion) == 0 { cmd.Usage() - return fmt.Errorf("--upgrade-version must be specified") + return errors.New("--upgrade-version must be specified") } if len(uc.deploymentDirectory) == 0 { cmd.Usage() - return fmt.Errorf("--deployment-dir must be specified") + return errors.New("--deployment-dir must be specified") } if len(uc.sshPrivateKeyPath) == 0 { @@ -105,11 +105,11 @@ func (uc *dcosUpgradeCmd) validate(cmd *cobra.Command) error { } if uc.sshPrivateKey, err = ioutil.ReadFile(uc.sshPrivateKeyPath); err != nil { cmd.Usage() - return fmt.Errorf("ssh-private-key-path must be specified: %s", err) + return errors.Wrap(err, "ssh-private-key-path must be specified") } if err = uc.authArgs.validateAuthArgs(); err != nil { - return fmt.Errorf("%s", err) + return err } return nil } @@ -118,19 +118,19 @@ func (uc *dcosUpgradeCmd) loadCluster(cmd *cobra.Command) error { var err error if uc.client, err = uc.authArgs.getClient(); err != nil { - return fmt.Errorf("Failed to get client: %s", err) + return errors.Wrap(err, "Failed to get client") } _, err = uc.client.EnsureResourceGroup(uc.resourceGroupName, uc.location, nil) if err != nil { - return fmt.Errorf("Error ensuring resource group: %s", err) + return errors.Wrap(err, "Error ensuring resource group") } // load apimodel from the deployment directory apiModelPath := path.Join(uc.deploymentDirectory, "apimodel.json") if _, err = os.Stat(apiModelPath); os.IsNotExist(err) { - return fmt.Errorf("specified api model does not exist (%s)", apiModelPath) + return errors.Errorf("specified api model does not exist (%s)", apiModelPath) } apiloader := &api.Apiloader{ @@ -140,24 +140,24 @@ func (uc *dcosUpgradeCmd) loadCluster(cmd *cobra.Command) error { } uc.containerService, uc.apiVersion, err = apiloader.LoadContainerServiceFromFile(apiModelPath, true, true, nil) if err != nil { - return fmt.Errorf("error parsing the api model: %s", err.Error()) + return errors.Wrap(err, "error parsing the api model") } uc.currentDcosVersion = uc.containerService.Properties.OrchestratorProfile.OrchestratorVersion if uc.currentDcosVersion == uc.upgradeVersion { - return fmt.Errorf("already running DCOS %s", uc.upgradeVersion) + return errors.Errorf("already running DCOS %s", uc.upgradeVersion) } if len(uc.containerService.Location) == 0 { uc.containerService.Location = uc.location } else if uc.containerService.Location != uc.location { - return fmt.Errorf("--location does not match api model location") + return errors.New("--location does not match api model location") } // get available upgrades for container service orchestratorInfo, err := api.GetOrchestratorVersionProfile(uc.containerService.Properties.OrchestratorProfile) if err != nil { - return fmt.Errorf("error getting list of available upgrades: %s", err.Error()) + return errors.Wrap(err, "error getting list of available upgrades") } // add the current version if upgrade has failed orchestratorInfo.Upgrades = append(orchestratorInfo.Upgrades, &api.OrchestratorProfile{ @@ -174,7 +174,7 @@ func (uc *dcosUpgradeCmd) loadCluster(cmd *cobra.Command) error { } } if !found { - return fmt.Errorf("upgrade to DCOS %s is not supported", uc.upgradeVersion) + return errors.Errorf("upgrade to DCOS %s is not supported", uc.upgradeVersion) } // Read name suffix to identify nodes in the resource group that belong diff --git a/cmd/deploy.go b/cmd/deploy.go index e47457bf77..e87ee9268f 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -1,7 +1,6 @@ package cmd import ( - "errors" "fmt" "io/ioutil" "math/rand" @@ -24,6 +23,7 @@ import ( "github.com/Azure/acs-engine/pkg/i18n" "github.com/Azure/azure-sdk-for-go/arm/graphrbac" "github.com/Azure/go-autorest/autorest/to" + "github.com/pkg/errors" ) const ( @@ -74,10 +74,10 @@ func newDeployCmd() *cobra.Command { Long: deployLongDescription, RunE: func(cmd *cobra.Command, args []string) error { if err := dc.validateArgs(cmd, args); err != nil { - log.Fatalf(fmt.Sprintf("error validating deployCmd: %s", err.Error())) + log.Fatalf("error validating deployCmd: %s", err.Error()) } if err := dc.mergeAPIModel(); err != nil { - log.Fatalf(fmt.Sprintf("error merging API model in deployCmd: %s", err.Error())) + log.Fatalf("error merging API model in deployCmd: %s", err.Error()) } if err := dc.loadAPIModel(cmd, args); err != nil { log.Fatalln("failed to load apimodel: %s", err.Error()) @@ -111,7 +111,7 @@ func (dc *deployCmd) validateArgs(cmd *cobra.Command, args []string) error { dc.locale, err = i18n.LoadTranslations() if err != nil { - return fmt.Errorf(fmt.Sprintf("error loading translation files: %s", err.Error())) + return errors.Wrap(err, "error loading translation files") } if dc.apimodelPath == "" { @@ -119,19 +119,19 @@ func (dc *deployCmd) validateArgs(cmd *cobra.Command, args []string) error { dc.apimodelPath = args[0] } else if len(args) > 1 { cmd.Usage() - return fmt.Errorf(fmt.Sprintf("too many arguments were provided to 'deploy'")) + return errors.New("too many arguments were provided to 'deploy'") } else { cmd.Usage() - return fmt.Errorf(fmt.Sprintf("--api-model was not supplied, nor was one specified as a positional argument")) + return errors.New("--api-model was not supplied, nor was one specified as a positional argument") } } if _, err := os.Stat(dc.apimodelPath); os.IsNotExist(err) { - return fmt.Errorf(fmt.Sprintf("specified api model does not exist (%s)", dc.apimodelPath)) + return errors.Errorf("specified api model does not exist (%s)", dc.apimodelPath) } if dc.location == "" { - return fmt.Errorf(fmt.Sprintf("--location must be specified")) + return errors.New("--location must be specified") } dc.location = helpers.NormalizeAzureRegion(dc.location) @@ -149,7 +149,7 @@ func (dc *deployCmd) mergeAPIModel() error { // overrides the api model and generates a new file dc.apimodelPath, err = transform.MergeValuesWithAPIModel(dc.apimodelPath, m) if err != nil { - return fmt.Errorf(fmt.Sprintf("error merging --set values with the api model: %s", err.Error())) + return errors.Wrap(err, "error merging --set values with the api model: %s") } log.Infoln(fmt.Sprintf("new api model file has been generated during merge: %s", dc.apimodelPath)) @@ -172,7 +172,7 @@ func (dc *deployCmd) loadAPIModel(cmd *cobra.Command, args []string) error { // do not validate when initially loading the apimodel, validation is done later after autofilling values dc.containerService, dc.apiVersion, err = apiloader.LoadContainerServiceFromFile(dc.apimodelPath, false, false, nil) if err != nil { - return fmt.Errorf(fmt.Sprintf("error parsing the api model: %s", err.Error())) + return errors.Wrap(err, "error parsing the api model") } if dc.outputDirectory == "" { @@ -190,10 +190,10 @@ func (dc *deployCmd) loadAPIModel(cmd *cobra.Command, args []string) error { if dc.caCertificatePath != "" { if caCertificateBytes, err = ioutil.ReadFile(dc.caCertificatePath); err != nil { - return fmt.Errorf(fmt.Sprintf("failed to read CA certificate file: %s", err.Error())) + return errors.Wrap(err, "failed to read CA certificate file") } if caKeyBytes, err = ioutil.ReadFile(dc.caPrivateKeyPath); err != nil { - return fmt.Errorf(fmt.Sprintf("failed to read CA private key file: %s", err.Error())) + return errors.Wrap(err, "failed to read CA private key file") } prop := dc.containerService.Properties @@ -207,16 +207,16 @@ func (dc *deployCmd) loadAPIModel(cmd *cobra.Command, args []string) error { if dc.containerService.Location == "" { dc.containerService.Location = dc.location } else if dc.containerService.Location != dc.location { - return fmt.Errorf(fmt.Sprintf("--location does not match api model location")) + return errors.New("--location does not match api model location") } if err = dc.authArgs.validateAuthArgs(); err != nil { - return fmt.Errorf("%s", err) + return err } dc.client, err = dc.authArgs.getClient() if err != nil { - return fmt.Errorf("failed to get client: %s", err.Error()) + return errors.Wrap(err, "failed to get client") } if err = autofillApimodel(dc); err != nil { @@ -239,11 +239,11 @@ func autofillApimodel(dc *deployCmd) error { } if dc.dnsPrefix != "" && dc.containerService.Properties.MasterProfile.DNSPrefix != "" { - return fmt.Errorf("invalid configuration: the apimodel masterProfile.dnsPrefix and --dns-prefix were both specified") + return errors.New("invalid configuration: the apimodel masterProfile.dnsPrefix and --dns-prefix were both specified") } if dc.containerService.Properties.MasterProfile.DNSPrefix == "" { if dc.dnsPrefix == "" { - return fmt.Errorf("apimodel: missing masterProfile.dnsPrefix and --dns-prefix was not specified") + return errors.New("apimodel: missing masterProfile.dnsPrefix and --dns-prefix was not specified") } log.Warnf("apimodel: missing masterProfile.dnsPrefix will use %q", dc.dnsPrefix) dc.containerService.Properties.MasterProfile.DNSPrefix = dc.dnsPrefix @@ -259,7 +259,7 @@ func autofillApimodel(dc *deployCmd) error { } if _, err := os.Stat(dc.outputDirectory); !dc.forceOverwrite && err == nil { - return fmt.Errorf("Output directory already exists and forceOverwrite flag is not set: %s", dc.outputDirectory) + return errors.Errorf("Output directory already exists and forceOverwrite flag is not set: %s", dc.outputDirectory) } if dc.resourceGroup == "" { @@ -267,7 +267,7 @@ func autofillApimodel(dc *deployCmd) error { log.Warnf("--resource-group was not specified. Using the DNS prefix from the apimodel as the resource group name: %s", dnsPrefix) dc.resourceGroup = dnsPrefix if dc.location == "" { - return fmt.Errorf("--resource-group was not specified. --location must be specified in case the resource group needs creation") + return errors.New("--resource-group was not specified. --location must be specified in case the resource group needs creation") } } @@ -279,7 +279,7 @@ func autofillApimodel(dc *deployCmd) error { } _, publicKey, err := acsengine.CreateSaveSSH(dc.containerService.Properties.LinuxProfile.AdminUsername, dc.outputDirectory, translator) if err != nil { - return fmt.Errorf("Failed to generate SSH Key: %s", err.Error()) + return errors.Wrap(err, "Failed to generate SSH Key") } dc.containerService.Properties.LinuxProfile.SSH.PublicKeys = []api.PublicKey{{KeyData: publicKey}} @@ -321,7 +321,7 @@ func autofillApimodel(dc *deployCmd) error { } applicationID, servicePrincipalObjectID, secret, err := dc.client.CreateApp(appName, appURL, replyURLs, requiredResourceAccess) if err != nil { - return fmt.Errorf("apimodel invalid: ServicePrincipalProfile was empty, and we failed to create valid credentials: %q", err) + return errors.Wrap(err, "apimodel invalid: ServicePrincipalProfile was empty, and we failed to create valid credentials") } log.Warnf("created application with applicationID (%s) and servicePrincipalObjectID (%s).", applicationID, servicePrincipalObjectID) @@ -329,7 +329,7 @@ func autofillApimodel(dc *deployCmd) error { err = dc.client.CreateRoleAssignmentSimple(dc.resourceGroup, servicePrincipalObjectID) if err != nil { - return fmt.Errorf("apimodel: could not create or assign ServicePrincipal: %q", err) + return errors.Wrap(err, "apimodel: could not create or assign ServicePrincipal") } diff --git a/cmd/generate.go b/cmd/generate.go index 694163696c..0867ff321a 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -1,7 +1,6 @@ package cmd import ( - "errors" "fmt" "io/ioutil" "os" @@ -12,6 +11,7 @@ import ( "github.com/Azure/acs-engine/pkg/api" "github.com/Azure/acs-engine/pkg/i18n" "github.com/leonelquinteros/gotext" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -80,7 +80,7 @@ func (gc *generateCmd) validate(cmd *cobra.Command, args []string) error { gc.locale, err = i18n.LoadTranslations() if err != nil { - return fmt.Errorf(fmt.Sprintf("error loading translation files: %s", err.Error())) + return errors.Wrap(err, "error loading translation files") } if gc.apimodelPath == "" { @@ -96,7 +96,7 @@ func (gc *generateCmd) validate(cmd *cobra.Command, args []string) error { } if _, err := os.Stat(gc.apimodelPath); os.IsNotExist(err) { - return fmt.Errorf(fmt.Sprintf("specified api model does not exist (%s)", gc.apimodelPath)) + return errors.Errorf("specified api model does not exist (%s)", gc.apimodelPath) } return nil @@ -113,7 +113,7 @@ func (gc *generateCmd) mergeAPIModel() error { // overrides the api model and generates a new file gc.apimodelPath, err = transform.MergeValuesWithAPIModel(gc.apimodelPath, m) if err != nil { - return fmt.Errorf(fmt.Sprintf("error merging --set values with the api model: %s", err.Error())) + return errors.Wrap(err, "error merging --set values with the api model") } log.Infoln(fmt.Sprintf("new api model file has been generated during merge: %s", gc.apimodelPath)) @@ -134,7 +134,7 @@ func (gc *generateCmd) loadAPIModel(cmd *cobra.Command, args []string) error { } gc.containerService, gc.apiVersion, err = apiloader.LoadContainerServiceFromFile(gc.apimodelPath, true, false, nil) if err != nil { - return fmt.Errorf(fmt.Sprintf("error parsing the api model: %s", err.Error())) + return errors.Wrap(err, "error parsing the api model") } if gc.outputDirectory == "" { @@ -152,10 +152,10 @@ func (gc *generateCmd) loadAPIModel(cmd *cobra.Command, args []string) error { } if gc.caCertificatePath != "" { if caCertificateBytes, err = ioutil.ReadFile(gc.caCertificatePath); err != nil { - return fmt.Errorf(fmt.Sprintf("failed to read CA certificate file: %s", err.Error())) + return errors.Wrap(err, "failed to read CA certificate file") } if caKeyBytes, err = ioutil.ReadFile(gc.caPrivateKeyPath); err != nil { - return fmt.Errorf(fmt.Sprintf("failed to read CA private key file: %s", err.Error())) + return errors.Wrap(err, "failed to read CA private key file") } prop := gc.containerService.Properties diff --git a/cmd/root.go b/cmd/root.go index e1d6af7f09..a95c6578f8 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,11 +1,11 @@ package cmd import ( - "fmt" "os" "github.com/Azure/acs-engine/pkg/armhelpers" "github.com/Azure/go-autorest/autorest/azure" + "github.com/pkg/errors" uuid "github.com/satori/go.uuid" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -49,12 +49,12 @@ func NewRootCmd() *cobra.Command { var completionCmd = &cobra.Command{ Use: "completion", Short: "Generates bash completion scripts", - Long: `To load completion run - + Long: `To load completion run + source <(acs-engine completion) - + To configure your bash shell to load completions for each session, add this to your bashrc - + # ~/.bashrc or ~/.profile source <(acs-engine completion) `, @@ -98,22 +98,22 @@ func (authArgs *authArgs) validateAuthArgs() error { if authArgs.AuthMethod == "client_secret" { if authArgs.ClientID.String() == "00000000-0000-0000-0000-000000000000" || authArgs.ClientSecret == "" { - return fmt.Errorf(`--client-id and --client-secret must be specified when --auth-method="client_secret"`) + return errors.New(`--client-id and --client-secret must be specified when --auth-method="client_secret"`) } // try parse the UUID } else if authArgs.AuthMethod == "client_certificate" { if authArgs.ClientID.String() == "00000000-0000-0000-0000-000000000000" || authArgs.CertificatePath == "" || authArgs.PrivateKeyPath == "" { - return fmt.Errorf(`--client-id and --certificate-path, and --private-key-path must be specified when --auth-method="client_certificate"`) + return errors.New(`--client-id and --certificate-path, and --private-key-path must be specified when --auth-method="client_certificate"`) } } if authArgs.SubscriptionID.String() == "00000000-0000-0000-0000-000000000000" { - return fmt.Errorf("--subscription-id is required (and must be a valid UUID)") + return errors.New("--subscription-id is required (and must be a valid UUID)") } _, err := azure.EnvironmentFromName(authArgs.RawAzureEnvironment) if err != nil { - return fmt.Errorf("failed to parse --azure-env as a valid target Azure cloud environment") + return errors.New("failed to parse --azure-env as a valid target Azure cloud environment") } return nil } @@ -132,7 +132,7 @@ func (authArgs *authArgs) getClient() (*armhelpers.AzureClient, error) { case "client_certificate": client, err = armhelpers.NewAzureClientWithClientCertificateFile(env, authArgs.SubscriptionID.String(), authArgs.ClientID.String(), authArgs.CertificatePath, authArgs.PrivateKeyPath) default: - return nil, fmt.Errorf("--auth-method: ERROR: method unsupported. method=%q", authArgs.AuthMethod) + return nil, errors.Errorf("--auth-method: ERROR: method unsupported. method=%q", authArgs.AuthMethod) } if err != nil { return nil, err diff --git a/cmd/scale.go b/cmd/scale.go index df9d39999e..63c69bb043 100644 --- a/cmd/scale.go +++ b/cmd/scale.go @@ -22,6 +22,7 @@ import ( "github.com/Azure/acs-engine/pkg/openshift/filesystem" "github.com/Azure/acs-engine/pkg/operations" "github.com/leonelquinteros/gotext" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -90,29 +91,29 @@ func (sc *scaleCmd) validate(cmd *cobra.Command) error { sc.locale, err = i18n.LoadTranslations() if err != nil { - return fmt.Errorf("error loading translation files: %s", err.Error()) + return errors.Wrap(err, "error loading translation files") } if sc.resourceGroupName == "" { cmd.Usage() - return fmt.Errorf("--resource-group must be specified") + return errors.New("--resource-group must be specified") } if sc.location == "" { cmd.Usage() - return fmt.Errorf("--location must be specified") + return errors.New("--location must be specified") } sc.location = helpers.NormalizeAzureRegion(sc.location) if sc.newDesiredAgentCount == 0 { cmd.Usage() - return fmt.Errorf("--new-node-count must be specified") + return errors.New("--new-node-count must be specified") } if sc.deploymentDirectory == "" { cmd.Usage() - return fmt.Errorf("--deployment-dir must be specified") + return errors.New("--deployment-dir must be specified") } return nil @@ -127,7 +128,7 @@ func (sc *scaleCmd) load(cmd *cobra.Command) error { } if sc.client, err = sc.authArgs.getClient(); err != nil { - return fmt.Errorf("failed to get client: %s", err.Error()) + return errors.Wrap(err, "failed to get client") } _, err = sc.client.EnsureResourceGroup(sc.resourceGroupName, sc.location, nil) @@ -139,7 +140,7 @@ func (sc *scaleCmd) load(cmd *cobra.Command) error { sc.apiModelPath = path.Join(sc.deploymentDirectory, "apimodel.json") if _, err = os.Stat(sc.apiModelPath); os.IsNotExist(err) { - return fmt.Errorf("specified api model does not exist (%s)", sc.apiModelPath) + return errors.Errorf("specified api model does not exist (%s)", sc.apiModelPath) } apiloader := &api.Apiloader{ @@ -149,25 +150,25 @@ func (sc *scaleCmd) load(cmd *cobra.Command) error { } sc.containerService, sc.apiVersion, err = apiloader.LoadContainerServiceFromFile(sc.apiModelPath, true, true, nil) if err != nil { - return fmt.Errorf("error parsing the api model: %s", err.Error()) + return errors.Wrap(err, "error parsing the api model") } if sc.containerService.Location == "" { sc.containerService.Location = sc.location } else if sc.containerService.Location != sc.location { - return fmt.Errorf("--location does not match api model location") + return errors.New("--location does not match api model location") } if sc.agentPoolToScale == "" { agentPoolCount := len(sc.containerService.Properties.AgentPoolProfiles) if agentPoolCount > 1 { - return fmt.Errorf("--node-pool is required if more than one agent pool is defined in the container service") + return errors.New("--node-pool is required if more than one agent pool is defined in the container service") } else if agentPoolCount == 1 { sc.agentPool = sc.containerService.Properties.AgentPoolProfiles[0] sc.agentPoolIndex = 0 sc.agentPoolToScale = sc.containerService.Properties.AgentPoolProfiles[0].Name } else { - return fmt.Errorf("No node pools found to scale") + return errors.New("No node pools found to scale") } } else { agentPoolIndex := -1 @@ -179,7 +180,7 @@ func (sc *scaleCmd) load(cmd *cobra.Command) error { } } if agentPoolIndex == -1 { - return fmt.Errorf("node pool %s was not found in the deployed api model", sc.agentPoolToScale) + return errors.Errorf("node pool %s was not found in the deployed api model", sc.agentPoolToScale) } } @@ -200,10 +201,10 @@ func (sc *scaleCmd) load(cmd *cobra.Command) error { func (sc *scaleCmd) run(cmd *cobra.Command, args []string) error { if err := sc.validate(cmd); err != nil { - return fmt.Errorf("failed to validate scale command: %s", err.Error()) + return errors.Wrap(err, "failed to validate scale command") } if err := sc.load(cmd); err != nil { - return fmt.Errorf("failed to load existing container service: %s", err.Error()) + return errors.Wrap(err, "failed to load existing container service") } orchestratorInfo := sc.containerService.Properties.OrchestratorProfile @@ -215,9 +216,9 @@ func (sc *scaleCmd) run(cmd *cobra.Command, args []string) error { //TODO handle when there is a nextLink in the response and get more nodes vms, err := sc.client.ListVirtualMachines(sc.resourceGroupName) if err != nil { - return fmt.Errorf("failed to get vms in the resource group. Error: %s", err.Error()) + return errors.Wrap(err, "failed to get vms in the resource group") } else if len(*vms.Value) < 1 { - return fmt.Errorf("The provided resource group does not contain any vms") + return errors.New("The provided resource group does not contain any vms") } for _, vm := range *vms.Value { vmTags := *vm.Tags @@ -254,7 +255,7 @@ func (sc *scaleCmd) run(cmd *cobra.Command, args []string) error { if currentNodeCount > sc.newDesiredAgentCount { if sc.masterFQDN == "" { cmd.Usage() - return fmt.Errorf("master-FQDN is required to scale down a kubernetes cluster's agent pool") + return errors.New("master-FQDN is required to scale down a kubernetes cluster's agent pool") } vmsToDelete := make([]string, 0) @@ -266,39 +267,43 @@ func (sc *scaleCmd) run(cmd *cobra.Command, args []string) error { case api.Kubernetes: kubeConfig, err := acsengine.GenerateKubeConfig(sc.containerService.Properties, sc.location) if err != nil { - return fmt.Errorf("failed to generate kube config: %v", err) + return errors.Wrap(err, "failed to generate kube config") } err = sc.drainNodes(kubeConfig, vmsToDelete) if err != nil { - return fmt.Errorf("Got error %+v, while draining the nodes to be deleted", err) + return errors.Wrap(err, "Got error while draining the nodes to be deleted") } case api.OpenShift: bundle := bytes.NewReader(sc.containerService.Properties.OrchestratorProfile.OpenShiftConfig.ConfigBundles["master"]) fs, err := filesystem.NewTGZReader(bundle) if err != nil { - return fmt.Errorf("failed to read master bundle: %v", err) + return errors.Wrap(err, "failed to read master bundle") } kubeConfig, err := fs.ReadFile("etc/origin/master/admin.kubeconfig") if err != nil { - return fmt.Errorf("failed to read kube config: %v", err) + return errors.Wrap(err, "failed to read kube config") } err = sc.drainNodes(string(kubeConfig), vmsToDelete) if err != nil { - return fmt.Errorf("Got error %v, while draining the nodes to be deleted", err) + return errors.Wrap(err, "Got error while draining the nodes to be deleted") } } errList := operations.ScaleDownVMs(sc.client, sc.logger, sc.SubscriptionID.String(), sc.resourceGroupName, vmsToDelete...) if errList != nil { - errorMessage := "" + var err error + format := "Node '%s' failed to delete with error: '%s'" for element := errList.Front(); element != nil; element = element.Next() { vmError, ok := element.Value.(*operations.VMScalingErrorDetails) if ok { - error := fmt.Sprintf("Node '%s' failed to delete with error: '%s'", vmError.Name, vmError.Error.Error()) - errorMessage = errorMessage + error + if err == nil { + err = errors.Errorf(format, vmError.Name, vmError.Error.Error()) + } else { + err = errors.Wrapf(err, format, vmError.Name, vmError.Error.Error()) + } } } - return fmt.Errorf(errorMessage) + return err } return nil @@ -306,7 +311,7 @@ func (sc *scaleCmd) run(cmd *cobra.Command, args []string) error { } else { vmssList, err := sc.client.ListVirtualMachineScaleSets(sc.resourceGroupName) if err != nil { - return fmt.Errorf("failed to get vmss list in the resource group. Error: %s", err.Error()) + return errors.Wrap(err, "failed to get vmss list in the resource group") } for _, vmss := range *vmssList.Value { vmTags := *vmss.Tags @@ -321,6 +326,7 @@ func (sc *scaleCmd) run(cmd *cobra.Command, args []string) error { osPublisher := *vmss.VirtualMachineProfile.StorageProfile.ImageReference.Publisher if strings.EqualFold(osPublisher, "MicrosoftWindowsServer") { _, _, winPoolIndex, err = utils.WindowsVMSSNameParts(*vmss.Name) + log.Errorln(err) } currentNodeCount = int(*vmss.Sku.Capacity) @@ -335,18 +341,18 @@ func (sc *scaleCmd) run(cmd *cobra.Command, args []string) error { } templateGenerator, err := acsengine.InitializeTemplateGenerator(ctx, sc.classicMode) if err != nil { - return fmt.Errorf("failed to initialize template generator: %s", err.Error()) + return errors.Wrap(err, "failed to initialize template generator") } sc.containerService.Properties.AgentPoolProfiles = []*api.AgentPoolProfile{sc.agentPool} template, parameters, _, err := templateGenerator.GenerateTemplate(sc.containerService, acsengine.DefaultGeneratorCode, false, BuildTag) if err != nil { - return fmt.Errorf("error generating template %s: %s", sc.apiModelPath, err.Error()) + return errors.Wrapf(err, "error generating template %s", sc.apiModelPath) } if template, err = transform.PrettyPrintArmTemplate(template); err != nil { - return fmt.Errorf("error pretty printing template: %s", err.Error()) + return errors.Wrap(err, "error pretty printing template") } templateJSON := make(map[string]interface{}) @@ -354,12 +360,12 @@ func (sc *scaleCmd) run(cmd *cobra.Command, args []string) error { err = json.Unmarshal([]byte(template), &templateJSON) if err != nil { - return err + return errors.Wrap(err, "error unmarshaling template") } err = json.Unmarshal([]byte(parameters), ¶metersJSON) if err != nil { - return err + return errors.Wrap(err, "errror unmarshalling parameters") } transformer := transform.Transformer{Translator: ctx.Translator} @@ -378,7 +384,7 @@ func (sc *scaleCmd) run(cmd *cobra.Command, args []string) error { case api.OpenShift: err = transformer.NormalizeForOpenShiftVMASScalingUp(sc.logger, sc.agentPool.Name, templateJSON) if err != nil { - return fmt.Errorf("error tranforming the template for scaling template %s: %v", sc.apiModelPath, err) + return errors.Wrapf(err, "error tranforming the template for scaling template %s", sc.apiModelPath) } if sc.agentPool.IsAvailabilitySets() { addValue(parametersJSON, fmt.Sprintf("%sOffset", sc.agentPool.Name), highestUsedIndex+1) @@ -386,7 +392,7 @@ func (sc *scaleCmd) run(cmd *cobra.Command, args []string) error { case api.Kubernetes: err = transformer.NormalizeForK8sVMASScalingUp(sc.logger, templateJSON) if err != nil { - return fmt.Errorf("error tranforming the template for scaling template %s: %s", sc.apiModelPath, err.Error()) + return errors.Wrapf(err, "error tranforming the template for scaling template %s", sc.apiModelPath) } if sc.agentPool.IsAvailabilitySets() { addValue(parametersJSON, fmt.Sprintf("%sOffset", sc.agentPool.Name), highestUsedIndex+1) @@ -395,7 +401,7 @@ func (sc *scaleCmd) run(cmd *cobra.Command, args []string) error { case api.SwarmMode: case api.DCOS: if sc.agentPool.IsAvailabilitySets() { - return fmt.Errorf("scaling isn't supported for orchestrator %s, with availability sets", orchestratorInfo.OrchestratorType) + return errors.Errorf("scaling isn't supported for orchestrator %q, with availability sets", orchestratorInfo.OrchestratorType) } transformer.NormalizeForVMSSScaling(sc.logger, templateJSON) } @@ -472,7 +478,7 @@ func (sc *scaleCmd) drainNodes(kubeConfig string, vmsToDelete []string) error { for i := 0; i < numVmsToDrain; i++ { errDetails := <-errChan if errDetails != nil { - return fmt.Errorf("Node %q failed to drain with error: %v", errDetails.Name, errDetails.Error) + return errors.Wrapf(errDetails.Error, "Node %q failed to drain with error", errDetails.Name) } } diff --git a/cmd/upgrade.go b/cmd/upgrade.go index 36e458abca..a9218509d9 100644 --- a/cmd/upgrade.go +++ b/cmd/upgrade.go @@ -15,6 +15,7 @@ import ( "github.com/Azure/acs-engine/pkg/i18n" "github.com/Azure/acs-engine/pkg/operations/kubernetesupgrade" "github.com/leonelquinteros/gotext" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -77,17 +78,17 @@ func (uc *upgradeCmd) validate(cmd *cobra.Command) error { uc.locale, err = i18n.LoadTranslations() if err != nil { - return fmt.Errorf("error loading translation files: %s", err.Error()) + return errors.Wrap(err, "error loading translation files") } if uc.resourceGroupName == "" { cmd.Usage() - return fmt.Errorf("--resource-group must be specified") + return errors.New("--resource-group must be specified") } if uc.location == "" { cmd.Usage() - return fmt.Errorf("--location must be specified") + return errors.New("--location must be specified") } uc.location = helpers.NormalizeAzureRegion(uc.location) @@ -99,12 +100,12 @@ func (uc *upgradeCmd) validate(cmd *cobra.Command) error { // TODO(colemick): add in the cmd annotation to help enable autocompletion if uc.upgradeVersion == "" { cmd.Usage() - return fmt.Errorf("--upgrade-version must be specified") + return errors.New("--upgrade-version must be specified") } if uc.deploymentDirectory == "" { cmd.Usage() - return fmt.Errorf("--deployment-dir must be specified") + return errors.New("--deployment-dir must be specified") } return nil } @@ -113,23 +114,23 @@ func (uc *upgradeCmd) loadCluster(cmd *cobra.Command) error { var err error if err = uc.authArgs.validateAuthArgs(); err != nil { - return fmt.Errorf("%s", err.Error()) + return err } if uc.client, err = uc.authArgs.getClient(); err != nil { - return fmt.Errorf("Failed to get client: %s", err.Error()) + return errors.Wrap(err, "Failed to get client") } _, err = uc.client.EnsureResourceGroup(uc.resourceGroupName, uc.location, nil) if err != nil { - return fmt.Errorf("Error ensuring resource group: %s", err.Error()) + return errors.Wrap(err, "Error ensuring resource group") } // load apimodel from the deployment directory apiModelPath := path.Join(uc.deploymentDirectory, "apimodel.json") if _, err = os.Stat(apiModelPath); os.IsNotExist(err) { - return fmt.Errorf("specified api model does not exist (%s)", apiModelPath) + return errors.Errorf("specified api model does not exist (%s)", apiModelPath) } apiloader := &api.Apiloader{ @@ -139,19 +140,19 @@ func (uc *upgradeCmd) loadCluster(cmd *cobra.Command) error { } uc.containerService, uc.apiVersion, err = apiloader.LoadContainerServiceFromFile(apiModelPath, true, true, nil) if err != nil { - return fmt.Errorf("error parsing the api model: %s", err.Error()) + return errors.Wrap(err, "error parsing the api model") } if uc.containerService.Location == "" { uc.containerService.Location = uc.location } else if uc.containerService.Location != uc.location { - return fmt.Errorf("--location does not match api model location") + return errors.New("--location does not match api model location") } // get available upgrades for container service orchestratorInfo, err := api.GetOrchestratorVersionProfile(uc.containerService.Properties.OrchestratorProfile) if err != nil { - return fmt.Errorf("error getting list of available upgrades: %s", err.Error()) + return errors.Wrap(err, "error getting list of available upgrades") } // add the current version if upgrade has failed orchestratorInfo.Upgrades = append(orchestratorInfo.Upgrades, &api.OrchestratorProfile{ @@ -168,7 +169,7 @@ func (uc *upgradeCmd) loadCluster(cmd *cobra.Command) error { } } if !found { - return fmt.Errorf("version %s is not supported", uc.upgradeVersion) + return errors.Errorf("version %s is not supported", uc.upgradeVersion) } // Read name suffix to identify nodes in the resource group that belong diff --git a/glide.lock b/glide.lock index ffff64a952..77f9fc7b84 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 90d55cc9eec600a57a5c52688b814b2daca46d5b4546fcd8a98b1d64d91422bd -updated: 2018-06-29T15:54:03.527724072-07:00 +hash: f9bc1ee5fe4bbff90c78d6851f3d91b617a0166b92137c3913d879c3cdb65876 +updated: 2018-07-02T13:50:41.754767835-07:00 imports: - name: github.com/alexcesaro/statsd version: 7fea3f0d2fab1ad973e641e51dba45443a311a90 @@ -129,6 +129,8 @@ imports: - matchers/support/goraph/node - matchers/support/goraph/util - types +- name: github.com/pkg/errors + version: 645ef00459ed84a119197bfb8d8205042c6df63d - name: github.com/rjtsdl/conform version: 7b97338c67692998bc62b2572e96c8ff09803dc3 - name: github.com/satori/go.uuid diff --git a/glide.yaml b/glide.yaml index 74031e5f65..1186d0cca2 100644 --- a/glide.yaml +++ b/glide.yaml @@ -68,6 +68,8 @@ import: - package: golang.org/x/sync subpackages: - errgroup +- package: github.com/pkg/errors + version: ~0.8.0 testImport: - package: github.com/onsi/gomega - package: github.com/onsi/ginkgo diff --git a/pkg/acsengine/defaults.go b/pkg/acsengine/defaults.go index af5795c4de..d264e92e4c 100644 --- a/pkg/acsengine/defaults.go +++ b/pkg/acsengine/defaults.go @@ -15,6 +15,7 @@ import ( "github.com/Azure/acs-engine/pkg/helpers" "github.com/Azure/acs-engine/pkg/openshift/certgen" "github.com/blang/semver" + "github.com/pkg/errors" ) const ( @@ -883,7 +884,7 @@ func setDefaultCerts(a *api.Properties) (bool, error) { firstMasterIP := net.ParseIP(a.MasterProfile.FirstConsecutiveStaticIP).To4() if firstMasterIP == nil { - return false, fmt.Errorf("MasterProfile.FirstConsecutiveStaticIP '%s' is an invalid IP address", a.MasterProfile.FirstConsecutiveStaticIP) + return false, errors.Errorf("MasterProfile.FirstConsecutiveStaticIP '%s' is an invalid IP address", a.MasterProfile.FirstConsecutiveStaticIP) } ips := []net.IP{firstMasterIP} diff --git a/pkg/acsengine/engine.go b/pkg/acsengine/engine.go index 5edddb8268..f7391c7158 100644 --- a/pkg/acsengine/engine.go +++ b/pkg/acsengine/engine.go @@ -22,6 +22,7 @@ import ( "github.com/Azure/acs-engine/pkg/api/common" "github.com/Azure/acs-engine/pkg/helpers" "github.com/ghodss/yaml" + "github.com/pkg/errors" ) var commonTemplateFiles = []string{agentOutputs, agentParams, classicParams, masterOutputs, iaasOutputs, masterParams, windowsParams} @@ -65,14 +66,14 @@ func GenerateClusterID(properties *api.Properties) string { // GenerateKubeConfig returns a JSON string representing the KubeConfig func GenerateKubeConfig(properties *api.Properties, location string) (string, error) { if properties == nil { - return "", fmt.Errorf("Properties nil in GenerateKubeConfig") + return "", errors.New("Properties nil in GenerateKubeConfig") } if properties.CertificateProfile == nil { - return "", fmt.Errorf("CertificateProfile property may not be nil in GenerateKubeConfig") + return "", errors.New("CertificateProfile property may not be nil in GenerateKubeConfig") } b, err := Asset(kubeConfigJSON) if err != nil { - return "", fmt.Errorf("error reading kube config template file %s: %s", kubeConfigJSON, err.Error()) + return "", errors.Wrapf(err, "error reading kube config template file %s", kubeConfigJSON) } kubeconfig := string(b) // variable replacement @@ -85,7 +86,7 @@ func GenerateKubeConfig(properties *api.Properties, location string) (string, er // more than 1 master, use the internal lb IP firstMasterIP := net.ParseIP(properties.MasterProfile.FirstConsecutiveStaticIP).To4() if firstMasterIP == nil { - return "", fmt.Errorf("MasterProfile.FirstConsecutiveStaticIP '%s' is an invalid IP address", properties.MasterProfile.FirstConsecutiveStaticIP) + return "", errors.Errorf("MasterProfile.FirstConsecutiveStaticIP '%s' is an invalid IP address", properties.MasterProfile.FirstConsecutiveStaticIP) } lbIP := net.IP{firstMasterIP[0], firstMasterIP[1], firstMasterIP[2], firstMasterIP[3] + byte(DefaultInternalLbStaticIPOffset)} kubeconfig = strings.Replace(kubeconfig, "{{WrapAsVerbatim \"reference(concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))).dnsSettings.fqdn\"}}", lbIP.String(), -1) @@ -271,7 +272,7 @@ func addSecret(m paramsMap, k string, v interface{}, encode bool) { func getStorageAccountType(sizeName string) (string, error) { spl := strings.Split(sizeName, "_") if len(spl) < 2 { - return "", fmt.Errorf("Invalid sizeName: %s", sizeName) + return "", errors.Errorf("Invalid sizeName: %s", sizeName) } capability := spl[1] if strings.Contains(strings.ToLower(capability), "s") { @@ -1201,7 +1202,7 @@ func validateProfileOptedForExtension(extensionName string, profileExtensions [] func getLinkedTemplateTextForURL(rootURL, orchestrator, extensionName, version, query string) (string, error) { supportsExtension, err := orchestratorSupportsExtension(rootURL, orchestrator, extensionName, version, query) if !supportsExtension { - return "", fmt.Errorf("Extension not supported for orchestrator. Error: %s", err) + return "", errors.Wrap(err, "Extension not supported for orchestrator") } templateLinkBytes, err := getExtensionResource(rootURL, extensionName, version, "template-link.json", query) @@ -1221,11 +1222,11 @@ func orchestratorSupportsExtension(rootURL, orchestrator, extensionName, version var supportedOrchestrators []string err = json.Unmarshal(orchestratorBytes, &supportedOrchestrators) if err != nil { - return false, fmt.Errorf("Unable to parse supported-orchestrators.json for Extension %s Version %s", extensionName, version) + return false, errors.Errorf("Unable to parse supported-orchestrators.json for Extension %s Version %s", extensionName, version) } if !stringInSlice(orchestrator, supportedOrchestrators) { - return false, fmt.Errorf("Orchestrator: %s not in list of supported orchestrators for Extension: %s Version %s", orchestrator, extensionName, version) + return false, errors.Errorf("Orchestrator: %s not in list of supported orchestrators for Extension: %s Version %s", orchestrator, extensionName, version) } return true, nil @@ -1236,18 +1237,18 @@ func getExtensionResource(rootURL, extensionName, version, fileName, query strin res, err := http.Get(requestURL) if err != nil { - return nil, fmt.Errorf("Unable to GET extension resource for extension: %s with version %s with filename %s at URL: %s Error: %s", extensionName, version, fileName, requestURL, err) + return nil, errors.Wrapf(err, "Unable to GET extension resource for extension: %s with version %s with filename %s at URL: %s", extensionName, version, fileName, requestURL) } defer res.Body.Close() if res.StatusCode != 200 { - return nil, fmt.Errorf("Unable to GET extension resource for extension: %s with version %s with filename %s at URL: %s StatusCode: %s: Status: %s", extensionName, version, fileName, requestURL, strconv.Itoa(res.StatusCode), res.Status) + return nil, errors.Errorf("Unable to GET extension resource for extension: %s with version %s with filename %s at URL: %s StatusCode: %s: Status: %s", extensionName, version, fileName, requestURL, strconv.Itoa(res.StatusCode), res.Status) } body, err := ioutil.ReadAll(res.Body) if err != nil { - return nil, fmt.Errorf("Unable to GET extension resource for extension: %s with version %s with filename %s at URL: %s Error: %s", extensionName, version, fileName, requestURL, err) + return nil, errors.Wrapf(err, "Unable to GET extension resource for extension: %s with version %s with filename %s at URL: %s", extensionName, version, fileName, requestURL) } return body, nil diff --git a/pkg/acsengine/template_generator.go b/pkg/acsengine/template_generator.go index aefc02f023..d46923ae50 100644 --- a/pkg/acsengine/template_generator.go +++ b/pkg/acsengine/template_generator.go @@ -3,7 +3,6 @@ package acsengine import ( "bytes" "encoding/base64" - "errors" "fmt" "runtime/debug" "sort" @@ -15,6 +14,7 @@ import ( "github.com/Azure/acs-engine/pkg/api/common" "github.com/Azure/acs-engine/pkg/helpers" "github.com/Azure/acs-engine/pkg/i18n" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -80,7 +80,7 @@ func (t *TemplateGenerator) GenerateTemplate(containerService *api.ContainerServ defer func() { if r := recover(); r != nil { s := debug.Stack() - err = fmt.Errorf("%v - %s", r, s) + err = errors.Errorf("%v - %s", r, s) // invalidate the template and the parameters templateRaw = "" @@ -89,7 +89,7 @@ func (t *TemplateGenerator) GenerateTemplate(containerService *api.ContainerServ }() if !validateDistro(containerService) { - return templateRaw, parametersRaw, certsGenerated, fmt.Errorf("Invalid distro") + return templateRaw, parametersRaw, certsGenerated, errors.New("Invalid distro") } var b bytes.Buffer diff --git a/pkg/acsengine/tenantid.go b/pkg/acsengine/tenantid.go index 80fc1a64c2..15e0e26939 100644 --- a/pkg/acsengine/tenantid.go +++ b/pkg/acsengine/tenantid.go @@ -1,12 +1,12 @@ package acsengine import ( - "fmt" "net/http" "regexp" "github.com/Azure/azure-sdk-for-go/arm/resources/subscriptions" "github.com/Azure/go-autorest/autorest/azure" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -25,19 +25,16 @@ func GetTenantID(env azure.Environment, subscriptionID string) (string, error) { // network error etc) subs, err := c.Get(subscriptionID) if subs.Response.Response == nil { - log.Errorf("Request failed: %v", err) - return "", fmt.Errorf("Request failed: %v", err) + return "", errors.Wrap(err, "Request failed") } // Expecting 401 StatusUnauthorized here, just read the header if subs.StatusCode != http.StatusUnauthorized { - log.Errorf("Unexpected response from Get Subscription: %v", subs.StatusCode) - return "", fmt.Errorf("Unexpected response from Get Subscription: %v", subs.StatusCode) + return "", errors.Errorf("Unexpected response from Get Subscription: %v", subs.StatusCode) } hdr := subs.Header.Get(hdrKey) if hdr == "" { - log.Errorf("Header %v not found in Get Subscription response", hdrKey) - return "", fmt.Errorf("Header %v not found in Get Subscription response", hdrKey) + return "", errors.Errorf("Header %v not found in Get Subscription response", hdrKey) } // Example value for hdr: @@ -45,8 +42,7 @@ func GetTenantID(env azure.Environment, subscriptionID string) (string, error) { r := regexp.MustCompile(`authorization_uri=".*/([0-9a-f\-]+)"`) m := r.FindStringSubmatch(hdr) if m == nil { - log.Errorf("Could not find the tenant ID in header: %s %q", hdrKey, hdr) - return "", fmt.Errorf("Could not find the tenant ID in header: %s %q", hdrKey, hdr) + return "", errors.Errorf("Could not find the tenant ID in header: %s %q", hdrKey, hdr) } return m[1], nil } diff --git a/pkg/api/apiloader.go b/pkg/api/apiloader.go index 9745b22267..8706492391 100644 --- a/pkg/api/apiloader.go +++ b/pkg/api/apiloader.go @@ -6,8 +6,6 @@ import ( "io/ioutil" "reflect" - "fmt" - "github.com/Azure/acs-engine/pkg/api/agentPoolOnlyApi/v20170831" "github.com/Azure/acs-engine/pkg/api/agentPoolOnlyApi/v20180331" apvlabs "github.com/Azure/acs-engine/pkg/api/agentPoolOnlyApi/vlabs" @@ -19,6 +17,7 @@ import ( "github.com/Azure/acs-engine/pkg/api/vlabs" "github.com/Azure/acs-engine/pkg/helpers" "github.com/Azure/acs-engine/pkg/i18n" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -85,7 +84,7 @@ func (a *Apiloader) LoadContainerService( } setContainerServiceDefaultsv20160930(containerService) if containerService.Properties == nil { - return nil, fmt.Errorf("missing ContainerService Properties") + return nil, errors.New("missing ContainerService Properties") } if e := containerService.Properties.Validate(); validate && e != nil { return nil, e @@ -108,7 +107,7 @@ func (a *Apiloader) LoadContainerService( } setContainerServiceDefaultsv20160330(containerService) if containerService.Properties == nil { - return nil, fmt.Errorf("missing ContainerService Properties") + return nil, errors.New("missing ContainerService Properties") } if e := containerService.Properties.Validate(); validate && e != nil { return nil, e @@ -132,7 +131,7 @@ func (a *Apiloader) LoadContainerService( } setContainerServiceDefaultsv20170131(containerService) if containerService.Properties == nil { - return nil, fmt.Errorf("missing ContainerService Properties") + return nil, errors.New("missing ContainerService Properties") } if e := containerService.Properties.Validate(); validate && e != nil { return nil, e @@ -155,7 +154,7 @@ func (a *Apiloader) LoadContainerService( } } if containerService.Properties == nil { - return nil, fmt.Errorf("missing ContainerService Properties") + return nil, errors.New("missing ContainerService Properties") } if e := containerService.Properties.Validate(isUpdate); validate && e != nil { return nil, e @@ -183,7 +182,7 @@ func (a *Apiloader) LoadContainerService( } } if containerService.Properties == nil { - return nil, fmt.Errorf("missing ContainerService Properties") + return nil, errors.New("missing ContainerService Properties") } if e := containerService.Properties.Validate(isUpdate); validate && e != nil { return nil, e diff --git a/pkg/api/common/helper.go b/pkg/api/common/helper.go index 0c1182a75c..924d76e71c 100644 --- a/pkg/api/common/helper.go +++ b/pkg/api/common/helper.go @@ -1,10 +1,10 @@ package common import ( - "fmt" "regexp" "strings" + "github.com/pkg/errors" validator "gopkg.in/go-playground/validator.v9" ) @@ -19,38 +19,38 @@ func HandleValidationErrors(e validator.ValidationErrors) error { "Properties.LinuxProfile", "Properties.ServicePrincipalProfile.ClientID", "Properties.WindowsProfile.AdminUsername", "Properties.WindowsProfile.AdminPassword": - return fmt.Errorf("missing %s", ns) + return errors.Errorf("missing %s", ns) case "Properties.MasterProfile.Count": - return fmt.Errorf("MasterProfile count needs to be 1, 3, or 5") + return errors.New("MasterProfile count needs to be 1, 3, or 5") case "Properties.MasterProfile.OSDiskSizeGB": - return fmt.Errorf("Invalid os disk size of %d specified. The range of valid values are [%d, %d]", err.Value().(int), MinDiskSizeGB, MaxDiskSizeGB) + return errors.Errorf("Invalid os disk size of %d specified. The range of valid values are [%d, %d]", err.Value().(int), MinDiskSizeGB, MaxDiskSizeGB) case "Properties.MasterProfile.IPAddressCount": - return fmt.Errorf("MasterProfile.IPAddressCount needs to be in the range [%d,%d]", MinIPAddressCount, MaxIPAddressCount) + return errors.Errorf("MasterProfile.IPAddressCount needs to be in the range [%d,%d]", MinIPAddressCount, MaxIPAddressCount) case "Properties.MasterProfile.StorageProfile": - return fmt.Errorf("Unknown storageProfile '%s'. Specify either %s or %s", err.Value().(string), StorageAccount, ManagedDisks) + return errors.Errorf("Unknown storageProfile '%s'. Specify either %s or %s", err.Value().(string), StorageAccount, ManagedDisks) default: if strings.HasPrefix(ns, "Properties.AgentPoolProfiles") { switch { case strings.HasSuffix(ns, ".Name") || strings.HasSuffix(ns, "VMSize"): - return fmt.Errorf("missing %s", ns) + return errors.Errorf("missing %s", ns) case strings.HasSuffix(ns, ".Count"): - return fmt.Errorf("AgentPoolProfile count needs to be in the range [%d,%d]", MinAgentCount, MaxAgentCount) + return errors.Errorf("AgentPoolProfile count needs to be in the range [%d,%d]", MinAgentCount, MaxAgentCount) case strings.HasSuffix(ns, ".OSDiskSizeGB"): - return fmt.Errorf("Invalid os disk size of %d specified. The range of valid values are [%d, %d]", err.Value().(int), MinDiskSizeGB, MaxDiskSizeGB) + return errors.Errorf("Invalid os disk size of %d specified. The range of valid values are [%d, %d]", err.Value().(int), MinDiskSizeGB, MaxDiskSizeGB) case strings.Contains(ns, ".Ports"): - return fmt.Errorf("AgentPoolProfile Ports must be in the range[%d, %d]", MinPort, MaxPort) + return errors.Errorf("AgentPoolProfile Ports must be in the range[%d, %d]", MinPort, MaxPort) case strings.HasSuffix(ns, ".StorageProfile"): - return fmt.Errorf("Unknown storageProfile '%s'. Specify either %s or %s", err.Value().(string), StorageAccount, ManagedDisks) + return errors.Errorf("Unknown storageProfile '%s'. Specify either %s or %s", err.Value().(string), StorageAccount, ManagedDisks) case strings.Contains(ns, ".DiskSizesGB"): - return fmt.Errorf("A maximum of %d disks may be specified, The range of valid disk size values are [%d, %d]", MaxDisks, MinDiskSizeGB, MaxDiskSizeGB) + return errors.Errorf("A maximum of %d disks may be specified, The range of valid disk size values are [%d, %d]", MaxDisks, MinDiskSizeGB, MaxDiskSizeGB) case strings.HasSuffix(ns, ".IPAddressCount"): - return fmt.Errorf("AgentPoolProfile.IPAddressCount needs to be in the range [%d,%d]", MinIPAddressCount, MaxIPAddressCount) + return errors.Errorf("AgentPoolProfile.IPAddressCount needs to be in the range [%d,%d]", MinIPAddressCount, MaxIPAddressCount) default: break } } } - return fmt.Errorf("Namespace %s is not caught, %+v", ns, e) + return errors.Errorf("Namespace %s is not caught, %+v", ns, e) } // ValidateDNSPrefix is a helper function to check that a DNS Prefix is valid @@ -61,7 +61,7 @@ func ValidateDNSPrefix(dnsName string) error { return err } if !re.MatchString(dnsName) { - return fmt.Errorf("DNSPrefix '%s' is invalid. The DNSPrefix must contain between 3 and 45 characters and can contain only letters, numbers, and hyphens. It must start with a letter and must end with a letter or a number. (length was %d)", dnsName, len(dnsName)) + return errors.Errorf("DNSPrefix '%s' is invalid. The DNSPrefix must contain between 3 and 45 characters and can contain only letters, numbers, and hyphens. It must start with a letter and must end with a letter or a number. (length was %d)", dnsName, len(dnsName)) } return nil } diff --git a/pkg/api/common/net.go b/pkg/api/common/net.go index 728d4b5dde..0766c9bb67 100644 --- a/pkg/api/common/net.go +++ b/pkg/api/common/net.go @@ -1,9 +1,10 @@ package common import ( - "fmt" "net" "regexp" + + "github.com/pkg/errors" ) // CidrFirstIP returns the first IP of the provided subnet. @@ -51,7 +52,7 @@ func GetVNETSubnetIDComponents(vnetSubnetID string) (string, string, string, str } submatches := re.FindStringSubmatch(vnetSubnetID) if len(submatches) != 5 { - return "", "", "", "", fmt.Errorf("Unable to parse vnetSubnetID. Please use a vnetSubnetID with format /subscriptions/SUB_ID/resourceGroups/RG_NAME/providers/Microsoft.Network/virtualNetworks/VNET_NAME/subnets/SUBNET_NAME") + return "", "", "", "", errors.New("Unable to parse vnetSubnetID. Please use a vnetSubnetID with format /subscriptions/SUB_ID/resourceGroups/RG_NAME/providers/Microsoft.Network/virtualNetworks/VNET_NAME/subnets/SUBNET_NAME") } return submatches[1], submatches[2], submatches[3], submatches[4], nil } diff --git a/pkg/api/orchestrators.go b/pkg/api/orchestrators.go index 8666a8d340..de54e39e8a 100644 --- a/pkg/api/orchestrators.go +++ b/pkg/api/orchestrators.go @@ -1,7 +1,6 @@ package api import ( - "fmt" "strconv" "strings" @@ -9,6 +8,7 @@ import ( "github.com/Azure/acs-engine/pkg/api/v20170930" "github.com/Azure/acs-engine/pkg/api/vlabs" "github.com/blang/semver" + "github.com/pkg/errors" ) type orchestratorsFunc func(*OrchestratorProfile) ([]*OrchestratorVersionProfile, error) @@ -47,10 +47,10 @@ func validate(orchestrator, version string) (string, error) { return OpenShift, nil case orchestrator == "": if version != "" { - return "", fmt.Errorf("Must specify orchestrator for version '%s'", version) + return "", errors.Errorf("Must specify orchestrator for version '%s'", version) } default: - return "", fmt.Errorf("Unsupported orchestrator '%s'", orchestrator) + return "", errors.Errorf("Unsupported orchestrator '%s'", orchestrator) } return "", nil } @@ -120,7 +120,7 @@ func getOrchestratorVersionProfileList(orchestrator, version string) ([]*Orchest // GetOrchestratorVersionProfile returns orchestrator info for upgradable container service func GetOrchestratorVersionProfile(orch *OrchestratorProfile) (*OrchestratorVersionProfile, error) { if orch.OrchestratorVersion == "" { - return nil, fmt.Errorf("Missing Orchestrator Version") + return nil, errors.New("Missing Orchestrator Version") } switch orch.OrchestratorType { case Kubernetes, DCOS: @@ -130,11 +130,11 @@ func GetOrchestratorVersionProfile(orch *OrchestratorProfile) (*OrchestratorVers } // has to be exactly one element per specified orchestrator/version if len(arr) != 1 { - return nil, fmt.Errorf("Umbiguous Orchestrator Versions") + return nil, errors.New("Umbiguous Orchestrator Versions") } return arr[0], nil default: - return nil, fmt.Errorf("Upgrade operation is not supported for '%s'", orch.OrchestratorType) + return nil, errors.Errorf("Upgrade operation is not supported for '%s'", orch.OrchestratorType) } } @@ -159,7 +159,7 @@ func kubernetesInfo(csOrch *OrchestratorProfile) ([]*OrchestratorVersionProfile, } } else { if !isVersionSupported(csOrch) { - return nil, fmt.Errorf("Kubernetes version %s is not supported", csOrch.OrchestratorVersion) + return nil, errors.Errorf("Kubernetes version %s is not supported", csOrch.OrchestratorVersion) } upgrades, err := kubernetesUpgrades(csOrch) @@ -218,7 +218,7 @@ func dcosInfo(csOrch *OrchestratorProfile) ([]*OrchestratorVersionProfile, error } } else { if !isVersionSupported(csOrch) { - return nil, fmt.Errorf("DCOS version %s is not supported", csOrch.OrchestratorVersion) + return nil, errors.Errorf("DCOS version %s is not supported", csOrch.OrchestratorVersion) } // get info for the specified version @@ -265,7 +265,7 @@ func swarmInfo(csOrch *OrchestratorProfile) ([]*OrchestratorVersionProfile, erro } if !isVersionSupported(csOrch) { - return nil, fmt.Errorf("Swarm version %s is not supported", csOrch.OrchestratorVersion) + return nil, errors.Errorf("Swarm version %s is not supported", csOrch.OrchestratorVersion) } return []*OrchestratorVersionProfile{ { @@ -291,7 +291,7 @@ func dockerceInfo(csOrch *OrchestratorProfile) ([]*OrchestratorVersionProfile, e } if !isVersionSupported(csOrch) { - return nil, fmt.Errorf("Docker CE version %s is not supported", csOrch.OrchestratorVersion) + return nil, errors.Errorf("Docker CE version %s is not supported", csOrch.OrchestratorVersion) } return []*OrchestratorVersionProfile{ { @@ -323,7 +323,7 @@ func openShiftInfo(csOrch *OrchestratorProfile) ([]*OrchestratorVersionProfile, } } else { if !isVersionSupported(csOrch) { - return nil, fmt.Errorf("OpenShift version %s is not supported", csOrch.OrchestratorVersion) + return nil, errors.Errorf("OpenShift version %s is not supported", csOrch.OrchestratorVersion) } // TODO: populate OrchestratorVersionProfile.Upgrades diff --git a/pkg/api/strictjson.go b/pkg/api/strictjson.go index ee00396045..a88addcb37 100644 --- a/pkg/api/strictjson.go +++ b/pkg/api/strictjson.go @@ -2,9 +2,10 @@ package api import ( "encoding/json" - "fmt" "reflect" "strings" + + "github.com/pkg/errors" ) func checkJSONKeys(data []byte, types ...reflect.Type) error { @@ -21,7 +22,7 @@ func checkMapKeys(o map[string]interface{}, types ...reflect.Type) error { for k, v := range o { f, present := fieldMap[strings.ToLower(k)] if !present { - return fmt.Errorf("Unknown JSON tag %s", k) + return errors.Errorf("Unknown JSON tag %s", k) } if f.Type.Kind() == reflect.Struct && v != nil { if childMap, exists := v.(map[string]interface{}); exists { diff --git a/pkg/armhelpers/azureclient.go b/pkg/armhelpers/azureclient.go index 0a48e08c89..275ea1b222 100644 --- a/pkg/armhelpers/azureclient.go +++ b/pkg/armhelpers/azureclient.go @@ -23,6 +23,7 @@ import ( "github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/to" "github.com/mitchellh/go-homedir" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/Azure/acs-engine/pkg/acsengine" @@ -75,7 +76,7 @@ func NewAzureClientWithDeviceAuth(env azure.Environment, subscriptionID string) home, err := homedir.Dir() if err != nil { - return nil, fmt.Errorf("Failed to get user home directory to look for cached token: %q", err) + return nil, errors.Wrap(err, "Failed to get user home directory to look for cached token") } cachePath := filepath.Join(home, ApplicationDir, "cache", fmt.Sprintf("%s_%s.token.json", tenantID, acsEngineClientID)) @@ -157,22 +158,22 @@ func NewAzureClientWithClientSecret(env azure.Environment, subscriptionID, clien func NewAzureClientWithClientCertificateFile(env azure.Environment, subscriptionID, clientID, certificatePath, privateKeyPath string) (*AzureClient, error) { certificateData, err := ioutil.ReadFile(certificatePath) if err != nil { - return nil, fmt.Errorf("Failed to read certificate: %q", err) + return nil, errors.Wrap(err, "Failed to read certificate") } block, _ := pem.Decode(certificateData) if block == nil { - return nil, fmt.Errorf("Failed to decode pem block from certificate") + return nil, errors.New("Failed to decode pem block from certificate") } certificate, err := x509.ParseCertificate(block.Bytes) if err != nil { - return nil, fmt.Errorf("Failed to parse certificate: %q", err) + return nil, errors.Wrap(err, "Failed to parse certificate") } privateKey, err := parseRsaPrivateKey(privateKeyPath) if err != nil { - return nil, fmt.Errorf("Failed to parse rsa private key: %q", err) + return nil, errors.Wrap(err, "Failed to parse rsa private key") } return NewAzureClientWithClientCertificate(env, subscriptionID, clientID, certificate, privateKey) @@ -186,11 +187,11 @@ func NewAzureClientWithClientCertificate(env azure.Environment, subscriptionID, } if certificate == nil { - return nil, fmt.Errorf("certificate should not be nil") + return nil, errors.New("certificate should not be nil") } if privateKey == nil { - return nil, fmt.Errorf("privateKey should not be nil") + return nil, errors.New("privateKey should not be nil") } armSpt, err := adal.NewServicePrincipalTokenFromCertificate(*oauthConfig, clientID, certificate, privateKey, env.ServiceManagementEndpoint) @@ -231,7 +232,7 @@ func tryLoadCachedToken(cachePath string) (*adal.Token, error) { token, err := adal.LoadToken(cachePath) if err != nil { - return nil, fmt.Errorf("Failed to load token from file: %v", err) + return nil, errors.Wrap(err, "Failed to load token from file") } return token, nil @@ -313,7 +314,7 @@ func (az *AzureClient) EnsureProvidersRegistered(subscriptionID string) error { return err } if registeredProviders.Value == nil { - return fmt.Errorf("Providers list was nil. subscription=%q", subscriptionID) + return errors.Errorf("Providers list was nil. subscription=%q", subscriptionID) } m := make(map[string]bool) @@ -324,7 +325,7 @@ func (az *AzureClient) EnsureProvidersRegistered(subscriptionID string) error { for _, provider := range RequiredResourceProviders { registered, ok := m[strings.ToLower(provider)] if !ok { - return fmt.Errorf("Unknown resource provider %q", provider) + return errors.Errorf("Unknown resource provider %q", provider) } if registered { log.Debugf("Already registered for %q", provider) @@ -346,7 +347,7 @@ func parseRsaPrivateKey(path string) (*rsa.PrivateKey, error) { block, _ := pem.Decode(privateKeyData) if block == nil { - return nil, fmt.Errorf("Failed to decode a pem block from private key") + return nil, errors.New("Failed to decode a pem block from private key") } privatePkcs1Key, errPkcs1 := x509.ParsePKCS1PrivateKey(block.Bytes) @@ -358,12 +359,12 @@ func parseRsaPrivateKey(path string) (*rsa.PrivateKey, error) { if errPkcs8 == nil { privatePkcs8RsaKey, ok := privatePkcs8Key.(*rsa.PrivateKey) if !ok { - return nil, fmt.Errorf("pkcs8 contained non-RSA key. Expected RSA key") + return nil, errors.New("pkcs8 contained non-RSA key. Expected RSA key") } return privatePkcs8RsaKey, nil } - return nil, fmt.Errorf("failed to parse private key as Pkcs#1 or Pkcs#8. (%s). (%s)", errPkcs1, errPkcs8) + return nil, errors.Errorf("failed to parse private key as Pkcs#1 or Pkcs#8. (%s). (%s)", errPkcs1, errPkcs8) } //AddAcceptLanguages sets the list of languages to accept on this request diff --git a/pkg/armhelpers/utils/util.go b/pkg/armhelpers/utils/util.go index 348cdb586f..3271307a7f 100644 --- a/pkg/armhelpers/utils/util.go +++ b/pkg/armhelpers/utils/util.go @@ -9,6 +9,7 @@ import ( "github.com/Azure/acs-engine/pkg/api" "github.com/Azure/azure-sdk-for-go/arm/compute" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -53,7 +54,7 @@ func ResourceName(ID string) (string, error) { parts := strings.Split(ID, "/") name := parts[len(parts)-1] if len(name) == 0 { - return "", fmt.Errorf("resource name was missing from identifier") + return "", errors.Errorf("resource name was missing from identifier") } return name, nil @@ -79,13 +80,13 @@ func SplitBlobURI(URI string) (string, string, string, error) { func K8sLinuxVMNameParts(vmName string) (poolIdentifier, nameSuffix string, agentIndex int, err error) { vmNameParts := vmnameLinuxRegexp.FindStringSubmatch(vmName) if len(vmNameParts) != 4 { - return "", "", -1, fmt.Errorf("resource name was missing from identifier") + return "", "", -1, errors.Errorf("resource name was missing from identifier") } vmNum, err := strconv.Atoi(vmNameParts[k8sLinuxVMAgentIndexArrayIndex]) if err != nil { - return "", "", -1, fmt.Errorf("Error parsing VM Name: %v", err) + return "", "", -1, errors.Wrap(err, "Error parsing VM Name") } return vmNameParts[k8sLinuxVMAgentPoolNameIndex], vmNameParts[k8sLinuxVMAgentClusterIDIndex], vmNum, nil @@ -95,7 +96,7 @@ func K8sLinuxVMNameParts(vmName string) (poolIdentifier, nameSuffix string, agen func VmssNameParts(vmssName string) (poolIdentifier, nameSuffix string, err error) { vmssNameParts := vmssnameRegexp.FindStringSubmatch(vmssName) if len(vmssNameParts) != 3 { - return "", "", fmt.Errorf("resource name was missing from identifier") + return "", "", errors.New("resource name was missing from identifier") } return vmssNameParts[vmssAgentPoolNameIndex], vmssNameParts[vmssClusterIDIndex], nil @@ -105,7 +106,7 @@ func VmssNameParts(vmssName string) (poolIdentifier, nameSuffix string, err erro func WindowsVMNameParts(vmName string) (poolPrefix string, acsStr string, poolIndex int, agentIndex int, err error) { vmNameParts := vmnameWindowsRegexp.FindStringSubmatch(vmName) if len(vmNameParts) != 4 { - return "", "", -1, -1, fmt.Errorf("resource name was missing from identifier") + return "", "", -1, -1, errors.New("resource name was missing from identifier") } poolPrefix = vmNameParts[k8sWindowsVMAgentPoolPrefixIndex] @@ -114,7 +115,7 @@ func WindowsVMNameParts(vmName string) (poolPrefix string, acsStr string, poolIn poolIndex, err = strconv.Atoi(poolInfo[:3]) if err != nil { - return "", "", -1, -1, fmt.Errorf("Error parsing VM Name: %v", err) + return "", "", -1, -1, errors.Wrap(err, "Error parsing VM Name") } poolIndex -= 900 agentIndex, _ = strconv.Atoi(poolInfo[3:]) @@ -127,7 +128,7 @@ func WindowsVMNameParts(vmName string) (poolPrefix string, acsStr string, poolIn func WindowsVMSSNameParts(vmssName string) (poolPrefix string, acsStr string, poolIndex int, err error) { vmssNameParts := vmssnameWindowsRegexp.FindStringSubmatch(vmssName) if len(vmssNameParts) != 4 { - return "", "", -1, fmt.Errorf("resource name was missing from identifier") + return "", "", -1, errors.Errorf("resource name was missing from identifier") } poolPrefix = vmssNameParts[windowsVmssAgentPoolNameIndex] @@ -136,7 +137,7 @@ func WindowsVMSSNameParts(vmssName string) (poolPrefix string, acsStr string, po poolIndex, err = strconv.Atoi(poolInfo) if err != nil { - return "", "", -1, fmt.Errorf("Error parsing VM Name: %v", err) + return "", "", -1, errors.Wrap(err, "Error parsing VM Name") } poolIndex -= 900 @@ -176,5 +177,5 @@ func GetK8sVMName(osType api.OSType, isAKS bool, nameSuffix, agentPoolName strin if osType == api.Windows { return fmt.Sprintf("%s%s%d%d", nameSuffix[:5], prefix, 900+agentPoolIndex, agentIndex), nil } - return "", fmt.Errorf("Failed to reconstruct VM Name") + return "", errors.Errorf("Failed to reconstruct VM Name") } diff --git a/pkg/operations/cordondrainvm.go b/pkg/operations/cordondrainvm.go index 4a58db9b32..d37116003d 100644 --- a/pkg/operations/cordondrainvm.go +++ b/pkg/operations/cordondrainvm.go @@ -1,11 +1,11 @@ package operations import ( - "fmt" "strings" "time" "github.com/Azure/acs-engine/pkg/armhelpers" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -177,7 +177,7 @@ func (o *drainOperation) evictPods(pods []v1.Pod, policyGroupVersion string) err } else if apierrors.IsTooManyRequests(err) { time.Sleep(5 * time.Second) } else { - errCh <- fmt.Errorf("error when evicting pod %q: %v", pod.Name, err) + errCh <- errors.Wrapf(err, "error when evicting pod %q", pod.Name) return } } @@ -186,7 +186,7 @@ func (o *drainOperation) evictPods(pods []v1.Pod, policyGroupVersion string) err if err == nil { doneCh <- true } else { - errCh <- fmt.Errorf("error when waiting for pod %q terminating: %v", pod.Name, err) + errCh <- errors.Wrapf(err, "error when waiting for pod %q terminating", pod.Name) } }(pod, doneCh, errCh) } @@ -202,7 +202,7 @@ func (o *drainOperation) evictPods(pods []v1.Pod, policyGroupVersion string) err return nil } case <-time.After(o.timeout): - return fmt.Errorf("Drain did not complete within %v", o.timeout) + return errors.Errorf("Drain did not complete within %v", o.timeout) } } } diff --git a/pkg/operations/deletevm.go b/pkg/operations/deletevm.go index e7be1cfa14..70893d1a14 100644 --- a/pkg/operations/deletevm.go +++ b/pkg/operations/deletevm.go @@ -5,6 +5,7 @@ import ( "github.com/Azure/acs-engine/pkg/armhelpers" "github.com/Azure/acs-engine/pkg/armhelpers/utils" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -27,7 +28,7 @@ func CleanDeleteVirtualMachine(az armhelpers.ACSEngineClient, logger *log.Entry, if vhd == nil && managedDisk == nil { logger.Errorf("failed to get a valid os disk URI for VM: %s/%s", resourceGroup, name) - return fmt.Errorf("os disk does not have a VHD URI") + return errors.New("os disk does not have a VHD URI") } osDiskName := vm.VirtualMachineProperties.StorageProfile.OsDisk.Name 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..588ceca183 --- /dev/null +++ b/vendor/github.com/pkg/errors/.travis.yml @@ -0,0 +1,11 @@ +language: go +go_import_path: github.com/pkg/errors +go: + - 1.4.3 + - 1.5.4 + - 1.6.2 + - 1.7.1 + - 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/bench_test.go b/vendor/github.com/pkg/errors/bench_test.go new file mode 100644 index 0000000000..0416a3cbb8 --- /dev/null +++ b/vendor/github.com/pkg/errors/bench_test.go @@ -0,0 +1,59 @@ +// +build go1.7 + +package errors + +import ( + "fmt" + "testing" + + stderrors "errors" +) + +func noErrors(at, depth int) error { + if at >= depth { + return stderrors.New("no error") + } + return noErrors(at+1, depth) +} +func yesErrors(at, depth int) error { + if at >= depth { + return New("ye error") + } + return yesErrors(at+1, depth) +} + +func BenchmarkErrors(b *testing.B) { + var toperr error + type run struct { + stack int + std bool + } + runs := []run{ + {10, false}, + {10, true}, + {100, false}, + {100, true}, + {1000, false}, + {1000, true}, + } + for _, r := range runs { + part := "pkg/errors" + if r.std { + part = "errors" + } + name := fmt.Sprintf("%s-stack-%d", part, r.stack) + b.Run(name, func(b *testing.B) { + var err error + f := yesErrors + if r.std { + f = noErrors + } + b.ReportAllocs() + for i := 0; i < b.N; i++ { + err = f(0, r.stack) + } + b.StopTimer() + toperr = err + }) + } +} diff --git a/vendor/github.com/pkg/errors/errors.go b/vendor/github.com/pkg/errors/errors.go new file mode 100644 index 0000000000..842ee80456 --- /dev/null +++ b/vendor/github.com/pkg/errors/errors.go @@ -0,0 +1,269 @@ +// 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 by recording a stack trace at the point Wrap is called, +// and the supplied message. For example +// +// _, err := ioutil.ReadAll(r) +// if err != nil { +// return errors.Wrap(err, "read failed") +// } +// +// If additional control is required the errors.WithStack and errors.WithMessage +// functions destructure errors.Wrap into its component operations of annotating +// an error with a stack trace and an a message, respectively. +// +// 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) + } +} + +// WithStack annotates err with a stack trace at the point WithStack was called. +// If err is nil, WithStack returns nil. +func WithStack(err error) error { + if err == nil { + return nil + } + return &withStack{ + err, + callers(), + } +} + +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 a stack trace +// at the point Wrap is called, and the supplied 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 a stack trace +// at the point Wrapf is call, and 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(), + } +} + +// WithMessage annotates err with a new message. +// If err is nil, WithMessage returns nil. +func WithMessage(err error, message string) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: message, + } +} + +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/errors_test.go b/vendor/github.com/pkg/errors/errors_test.go new file mode 100644 index 0000000000..1d8c635586 --- /dev/null +++ b/vendor/github.com/pkg/errors/errors_test.go @@ -0,0 +1,226 @@ +package errors + +import ( + "errors" + "fmt" + "io" + "reflect" + "testing" +) + +func TestNew(t *testing.T) { + tests := []struct { + err string + want error + }{ + {"", fmt.Errorf("")}, + {"foo", fmt.Errorf("foo")}, + {"foo", New("foo")}, + {"string with format specifiers: %v", errors.New("string with format specifiers: %v")}, + } + + for _, tt := range tests { + got := New(tt.err) + if got.Error() != tt.want.Error() { + t.Errorf("New.Error(): got: %q, want %q", got, tt.want) + } + } +} + +func TestWrapNil(t *testing.T) { + got := Wrap(nil, "no error") + if got != nil { + t.Errorf("Wrap(nil, \"no error\"): got %#v, expected nil", got) + } +} + +func TestWrap(t *testing.T) { + tests := []struct { + err error + message string + want string + }{ + {io.EOF, "read error", "read error: EOF"}, + {Wrap(io.EOF, "read error"), "client error", "client error: read error: EOF"}, + } + + for _, tt := range tests { + got := Wrap(tt.err, tt.message).Error() + if got != tt.want { + t.Errorf("Wrap(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want) + } + } +} + +type nilError struct{} + +func (nilError) Error() string { return "nil error" } + +func TestCause(t *testing.T) { + x := New("error") + tests := []struct { + err error + want error + }{{ + // nil error is nil + err: nil, + want: nil, + }, { + // explicit nil error is nil + err: (error)(nil), + want: nil, + }, { + // typed nil is nil + err: (*nilError)(nil), + want: (*nilError)(nil), + }, { + // uncaused error is unaffected + err: io.EOF, + want: io.EOF, + }, { + // caused error returns cause + err: Wrap(io.EOF, "ignored"), + want: io.EOF, + }, { + err: x, // return from errors.New + want: x, + }, { + WithMessage(nil, "whoops"), + nil, + }, { + WithMessage(io.EOF, "whoops"), + io.EOF, + }, { + WithStack(nil), + nil, + }, { + WithStack(io.EOF), + io.EOF, + }} + + for i, tt := range tests { + got := Cause(tt.err) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("test %d: got %#v, want %#v", i+1, got, tt.want) + } + } +} + +func TestWrapfNil(t *testing.T) { + got := Wrapf(nil, "no error") + if got != nil { + t.Errorf("Wrapf(nil, \"no error\"): got %#v, expected nil", got) + } +} + +func TestWrapf(t *testing.T) { + tests := []struct { + err error + message string + want string + }{ + {io.EOF, "read error", "read error: EOF"}, + {Wrapf(io.EOF, "read error without format specifiers"), "client error", "client error: read error without format specifiers: EOF"}, + {Wrapf(io.EOF, "read error with %d format specifier", 1), "client error", "client error: read error with 1 format specifier: EOF"}, + } + + for _, tt := range tests { + got := Wrapf(tt.err, tt.message).Error() + if got != tt.want { + t.Errorf("Wrapf(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want) + } + } +} + +func TestErrorf(t *testing.T) { + tests := []struct { + err error + want string + }{ + {Errorf("read error without format specifiers"), "read error without format specifiers"}, + {Errorf("read error with %d format specifier", 1), "read error with 1 format specifier"}, + } + + for _, tt := range tests { + got := tt.err.Error() + if got != tt.want { + t.Errorf("Errorf(%v): got: %q, want %q", tt.err, got, tt.want) + } + } +} + +func TestWithStackNil(t *testing.T) { + got := WithStack(nil) + if got != nil { + t.Errorf("WithStack(nil): got %#v, expected nil", got) + } +} + +func TestWithStack(t *testing.T) { + tests := []struct { + err error + want string + }{ + {io.EOF, "EOF"}, + {WithStack(io.EOF), "EOF"}, + } + + for _, tt := range tests { + got := WithStack(tt.err).Error() + if got != tt.want { + t.Errorf("WithStack(%v): got: %v, want %v", tt.err, got, tt.want) + } + } +} + +func TestWithMessageNil(t *testing.T) { + got := WithMessage(nil, "no error") + if got != nil { + t.Errorf("WithMessage(nil, \"no error\"): got %#v, expected nil", got) + } +} + +func TestWithMessage(t *testing.T) { + tests := []struct { + err error + message string + want string + }{ + {io.EOF, "read error", "read error: EOF"}, + {WithMessage(io.EOF, "read error"), "client error", "client error: read error: EOF"}, + } + + for _, tt := range tests { + got := WithMessage(tt.err, tt.message).Error() + if got != tt.want { + t.Errorf("WithMessage(%v, %q): got: %q, want %q", tt.err, tt.message, got, tt.want) + } + } + +} + +// errors.New, etc values are not expected to be compared by value +// but the change in errors#27 made them incomparable. Assert that +// various kinds of errors have a functional equality operator, even +// if the result of that equality is always false. +func TestErrorEquality(t *testing.T) { + vals := []error{ + nil, + io.EOF, + errors.New("EOF"), + New("EOF"), + Errorf("EOF"), + Wrap(io.EOF, "EOF"), + Wrapf(io.EOF, "EOF%d", 2), + WithMessage(nil, "whoops"), + WithMessage(io.EOF, "whoops"), + WithStack(io.EOF), + WithStack(nil), + } + + for i := range vals { + for j := range vals { + _ = vals[i] == vals[j] // mustn't panic + } + } +} diff --git a/vendor/github.com/pkg/errors/example_test.go b/vendor/github.com/pkg/errors/example_test.go new file mode 100644 index 0000000000..c1fc13e384 --- /dev/null +++ b/vendor/github.com/pkg/errors/example_test.go @@ -0,0 +1,205 @@ +package errors_test + +import ( + "fmt" + + "github.com/pkg/errors" +) + +func ExampleNew() { + err := errors.New("whoops") + fmt.Println(err) + + // Output: whoops +} + +func ExampleNew_printf() { + err := errors.New("whoops") + fmt.Printf("%+v", err) + + // Example output: + // whoops + // github.com/pkg/errors_test.ExampleNew_printf + // /home/dfc/src/github.com/pkg/errors/example_test.go:17 + // testing.runExample + // /home/dfc/go/src/testing/example.go:114 + // testing.RunExamples + // /home/dfc/go/src/testing/example.go:38 + // testing.(*M).Run + // /home/dfc/go/src/testing/testing.go:744 + // main.main + // /github.com/pkg/errors/_test/_testmain.go:106 + // runtime.main + // /home/dfc/go/src/runtime/proc.go:183 + // runtime.goexit + // /home/dfc/go/src/runtime/asm_amd64.s:2059 +} + +func ExampleWithMessage() { + cause := errors.New("whoops") + err := errors.WithMessage(cause, "oh noes") + fmt.Println(err) + + // Output: oh noes: whoops +} + +func ExampleWithStack() { + cause := errors.New("whoops") + err := errors.WithStack(cause) + fmt.Println(err) + + // Output: whoops +} + +func ExampleWithStack_printf() { + cause := errors.New("whoops") + err := errors.WithStack(cause) + fmt.Printf("%+v", err) + + // Example Output: + // whoops + // github.com/pkg/errors_test.ExampleWithStack_printf + // /home/fabstu/go/src/github.com/pkg/errors/example_test.go:55 + // testing.runExample + // /usr/lib/go/src/testing/example.go:114 + // testing.RunExamples + // /usr/lib/go/src/testing/example.go:38 + // testing.(*M).Run + // /usr/lib/go/src/testing/testing.go:744 + // main.main + // github.com/pkg/errors/_test/_testmain.go:106 + // runtime.main + // /usr/lib/go/src/runtime/proc.go:183 + // runtime.goexit + // /usr/lib/go/src/runtime/asm_amd64.s:2086 + // github.com/pkg/errors_test.ExampleWithStack_printf + // /home/fabstu/go/src/github.com/pkg/errors/example_test.go:56 + // testing.runExample + // /usr/lib/go/src/testing/example.go:114 + // testing.RunExamples + // /usr/lib/go/src/testing/example.go:38 + // testing.(*M).Run + // /usr/lib/go/src/testing/testing.go:744 + // main.main + // github.com/pkg/errors/_test/_testmain.go:106 + // runtime.main + // /usr/lib/go/src/runtime/proc.go:183 + // runtime.goexit + // /usr/lib/go/src/runtime/asm_amd64.s:2086 +} + +func ExampleWrap() { + cause := errors.New("whoops") + err := errors.Wrap(cause, "oh noes") + fmt.Println(err) + + // Output: oh noes: whoops +} + +func fn() error { + e1 := errors.New("error") + e2 := errors.Wrap(e1, "inner") + e3 := errors.Wrap(e2, "middle") + return errors.Wrap(e3, "outer") +} + +func ExampleCause() { + err := fn() + fmt.Println(err) + fmt.Println(errors.Cause(err)) + + // Output: outer: middle: inner: error + // error +} + +func ExampleWrap_extended() { + err := fn() + fmt.Printf("%+v\n", err) + + // Example output: + // error + // github.com/pkg/errors_test.fn + // /home/dfc/src/github.com/pkg/errors/example_test.go:47 + // github.com/pkg/errors_test.ExampleCause_printf + // /home/dfc/src/github.com/pkg/errors/example_test.go:63 + // testing.runExample + // /home/dfc/go/src/testing/example.go:114 + // testing.RunExamples + // /home/dfc/go/src/testing/example.go:38 + // testing.(*M).Run + // /home/dfc/go/src/testing/testing.go:744 + // main.main + // /github.com/pkg/errors/_test/_testmain.go:104 + // runtime.main + // /home/dfc/go/src/runtime/proc.go:183 + // runtime.goexit + // /home/dfc/go/src/runtime/asm_amd64.s:2059 + // github.com/pkg/errors_test.fn + // /home/dfc/src/github.com/pkg/errors/example_test.go:48: inner + // github.com/pkg/errors_test.fn + // /home/dfc/src/github.com/pkg/errors/example_test.go:49: middle + // github.com/pkg/errors_test.fn + // /home/dfc/src/github.com/pkg/errors/example_test.go:50: outer +} + +func ExampleWrapf() { + cause := errors.New("whoops") + err := errors.Wrapf(cause, "oh noes #%d", 2) + fmt.Println(err) + + // Output: oh noes #2: whoops +} + +func ExampleErrorf_extended() { + err := errors.Errorf("whoops: %s", "foo") + fmt.Printf("%+v", err) + + // Example output: + // whoops: foo + // github.com/pkg/errors_test.ExampleErrorf + // /home/dfc/src/github.com/pkg/errors/example_test.go:101 + // testing.runExample + // /home/dfc/go/src/testing/example.go:114 + // testing.RunExamples + // /home/dfc/go/src/testing/example.go:38 + // testing.(*M).Run + // /home/dfc/go/src/testing/testing.go:744 + // main.main + // /github.com/pkg/errors/_test/_testmain.go:102 + // runtime.main + // /home/dfc/go/src/runtime/proc.go:183 + // runtime.goexit + // /home/dfc/go/src/runtime/asm_amd64.s:2059 +} + +func Example_stackTrace() { + type stackTracer interface { + StackTrace() errors.StackTrace + } + + err, ok := errors.Cause(fn()).(stackTracer) + if !ok { + panic("oops, err does not implement stackTracer") + } + + st := err.StackTrace() + fmt.Printf("%+v", st[0:2]) // top two frames + + // Example output: + // github.com/pkg/errors_test.fn + // /home/dfc/src/github.com/pkg/errors/example_test.go:47 + // github.com/pkg/errors_test.Example_stackTrace + // /home/dfc/src/github.com/pkg/errors/example_test.go:127 +} + +func ExampleCause_printf() { + err := errors.Wrap(func() error { + return func() error { + return errors.Errorf("hello %s", fmt.Sprintf("world")) + }() + }(), "failed") + + fmt.Printf("%v", err) + + // Output: failed: hello world +} diff --git a/vendor/github.com/pkg/errors/format_test.go b/vendor/github.com/pkg/errors/format_test.go new file mode 100644 index 0000000000..15fd7d89d7 --- /dev/null +++ b/vendor/github.com/pkg/errors/format_test.go @@ -0,0 +1,535 @@ +package errors + +import ( + "errors" + "fmt" + "io" + "regexp" + "strings" + "testing" +) + +func TestFormatNew(t *testing.T) { + tests := []struct { + error + format string + want string + }{{ + New("error"), + "%s", + "error", + }, { + New("error"), + "%v", + "error", + }, { + New("error"), + "%+v", + "error\n" + + "github.com/pkg/errors.TestFormatNew\n" + + "\t.+/github.com/pkg/errors/format_test.go:26", + }, { + New("error"), + "%q", + `"error"`, + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.error, tt.format, tt.want) + } +} + +func TestFormatErrorf(t *testing.T) { + tests := []struct { + error + format string + want string + }{{ + Errorf("%s", "error"), + "%s", + "error", + }, { + Errorf("%s", "error"), + "%v", + "error", + }, { + Errorf("%s", "error"), + "%+v", + "error\n" + + "github.com/pkg/errors.TestFormatErrorf\n" + + "\t.+/github.com/pkg/errors/format_test.go:56", + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.error, tt.format, tt.want) + } +} + +func TestFormatWrap(t *testing.T) { + tests := []struct { + error + format string + want string + }{{ + Wrap(New("error"), "error2"), + "%s", + "error2: error", + }, { + Wrap(New("error"), "error2"), + "%v", + "error2: error", + }, { + Wrap(New("error"), "error2"), + "%+v", + "error\n" + + "github.com/pkg/errors.TestFormatWrap\n" + + "\t.+/github.com/pkg/errors/format_test.go:82", + }, { + Wrap(io.EOF, "error"), + "%s", + "error: EOF", + }, { + Wrap(io.EOF, "error"), + "%v", + "error: EOF", + }, { + Wrap(io.EOF, "error"), + "%+v", + "EOF\n" + + "error\n" + + "github.com/pkg/errors.TestFormatWrap\n" + + "\t.+/github.com/pkg/errors/format_test.go:96", + }, { + Wrap(Wrap(io.EOF, "error1"), "error2"), + "%+v", + "EOF\n" + + "error1\n" + + "github.com/pkg/errors.TestFormatWrap\n" + + "\t.+/github.com/pkg/errors/format_test.go:103\n", + }, { + Wrap(New("error with space"), "context"), + "%q", + `"context: error with space"`, + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.error, tt.format, tt.want) + } +} + +func TestFormatWrapf(t *testing.T) { + tests := []struct { + error + format string + want string + }{{ + Wrapf(io.EOF, "error%d", 2), + "%s", + "error2: EOF", + }, { + Wrapf(io.EOF, "error%d", 2), + "%v", + "error2: EOF", + }, { + Wrapf(io.EOF, "error%d", 2), + "%+v", + "EOF\n" + + "error2\n" + + "github.com/pkg/errors.TestFormatWrapf\n" + + "\t.+/github.com/pkg/errors/format_test.go:134", + }, { + Wrapf(New("error"), "error%d", 2), + "%s", + "error2: error", + }, { + Wrapf(New("error"), "error%d", 2), + "%v", + "error2: error", + }, { + Wrapf(New("error"), "error%d", 2), + "%+v", + "error\n" + + "github.com/pkg/errors.TestFormatWrapf\n" + + "\t.+/github.com/pkg/errors/format_test.go:149", + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.error, tt.format, tt.want) + } +} + +func TestFormatWithStack(t *testing.T) { + tests := []struct { + error + format string + want []string + }{{ + WithStack(io.EOF), + "%s", + []string{"EOF"}, + }, { + WithStack(io.EOF), + "%v", + []string{"EOF"}, + }, { + WithStack(io.EOF), + "%+v", + []string{"EOF", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:175"}, + }, { + WithStack(New("error")), + "%s", + []string{"error"}, + }, { + WithStack(New("error")), + "%v", + []string{"error"}, + }, { + WithStack(New("error")), + "%+v", + []string{"error", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:189", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:189"}, + }, { + WithStack(WithStack(io.EOF)), + "%+v", + []string{"EOF", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:197", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:197"}, + }, { + WithStack(WithStack(Wrapf(io.EOF, "message"))), + "%+v", + []string{"EOF", + "message", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:205", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:205", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:205"}, + }, { + WithStack(Errorf("error%d", 1)), + "%+v", + []string{"error1", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:216", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:216"}, + }} + + for i, tt := range tests { + testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true) + } +} + +func TestFormatWithMessage(t *testing.T) { + tests := []struct { + error + format string + want []string + }{{ + WithMessage(New("error"), "error2"), + "%s", + []string{"error2: error"}, + }, { + WithMessage(New("error"), "error2"), + "%v", + []string{"error2: error"}, + }, { + WithMessage(New("error"), "error2"), + "%+v", + []string{ + "error", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:244", + "error2"}, + }, { + WithMessage(io.EOF, "addition1"), + "%s", + []string{"addition1: EOF"}, + }, { + WithMessage(io.EOF, "addition1"), + "%v", + []string{"addition1: EOF"}, + }, { + WithMessage(io.EOF, "addition1"), + "%+v", + []string{"EOF", "addition1"}, + }, { + WithMessage(WithMessage(io.EOF, "addition1"), "addition2"), + "%v", + []string{"addition2: addition1: EOF"}, + }, { + WithMessage(WithMessage(io.EOF, "addition1"), "addition2"), + "%+v", + []string{"EOF", "addition1", "addition2"}, + }, { + Wrap(WithMessage(io.EOF, "error1"), "error2"), + "%+v", + []string{"EOF", "error1", "error2", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:272"}, + }, { + WithMessage(Errorf("error%d", 1), "error2"), + "%+v", + []string{"error1", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:278", + "error2"}, + }, { + WithMessage(WithStack(io.EOF), "error"), + "%+v", + []string{ + "EOF", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:285", + "error"}, + }, { + WithMessage(Wrap(WithStack(io.EOF), "inside-error"), "outside-error"), + "%+v", + []string{ + "EOF", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:293", + "inside-error", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:293", + "outside-error"}, + }} + + for i, tt := range tests { + testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true) + } +} + +func TestFormatGeneric(t *testing.T) { + starts := []struct { + err error + want []string + }{ + {New("new-error"), []string{ + "new-error", + "github.com/pkg/errors.TestFormatGeneric\n" + + "\t.+/github.com/pkg/errors/format_test.go:315"}, + }, {Errorf("errorf-error"), []string{ + "errorf-error", + "github.com/pkg/errors.TestFormatGeneric\n" + + "\t.+/github.com/pkg/errors/format_test.go:319"}, + }, {errors.New("errors-new-error"), []string{ + "errors-new-error"}, + }, + } + + wrappers := []wrapper{ + { + func(err error) error { return WithMessage(err, "with-message") }, + []string{"with-message"}, + }, { + func(err error) error { return WithStack(err) }, + []string{ + "github.com/pkg/errors.(func·002|TestFormatGeneric.func2)\n\t" + + ".+/github.com/pkg/errors/format_test.go:333", + }, + }, { + func(err error) error { return Wrap(err, "wrap-error") }, + []string{ + "wrap-error", + "github.com/pkg/errors.(func·003|TestFormatGeneric.func3)\n\t" + + ".+/github.com/pkg/errors/format_test.go:339", + }, + }, { + func(err error) error { return Wrapf(err, "wrapf-error%d", 1) }, + []string{ + "wrapf-error1", + "github.com/pkg/errors.(func·004|TestFormatGeneric.func4)\n\t" + + ".+/github.com/pkg/errors/format_test.go:346", + }, + }, + } + + for s := range starts { + err := starts[s].err + want := starts[s].want + testFormatCompleteCompare(t, s, err, "%+v", want, false) + testGenericRecursive(t, err, want, wrappers, 3) + } +} + +func testFormatRegexp(t *testing.T, n int, arg interface{}, format, want string) { + got := fmt.Sprintf(format, arg) + gotLines := strings.SplitN(got, "\n", -1) + wantLines := strings.SplitN(want, "\n", -1) + + if len(wantLines) > len(gotLines) { + t.Errorf("test %d: wantLines(%d) > gotLines(%d):\n got: %q\nwant: %q", n+1, len(wantLines), len(gotLines), got, want) + return + } + + for i, w := range wantLines { + match, err := regexp.MatchString(w, gotLines[i]) + if err != nil { + t.Fatal(err) + } + if !match { + t.Errorf("test %d: line %d: fmt.Sprintf(%q, err):\n got: %q\nwant: %q", n+1, i+1, format, got, want) + } + } +} + +var stackLineR = regexp.MustCompile(`\.`) + +// parseBlocks parses input into a slice, where: +// - incase entry contains a newline, its a stacktrace +// - incase entry contains no newline, its a solo line. +// +// Detecting stack boundaries only works incase the WithStack-calls are +// to be found on the same line, thats why it is optionally here. +// +// Example use: +// +// for _, e := range blocks { +// if strings.ContainsAny(e, "\n") { +// // Match as stack +// } else { +// // Match as line +// } +// } +// +func parseBlocks(input string, detectStackboundaries bool) ([]string, error) { + var blocks []string + + stack := "" + wasStack := false + lines := map[string]bool{} // already found lines + + for _, l := range strings.Split(input, "\n") { + isStackLine := stackLineR.MatchString(l) + + switch { + case !isStackLine && wasStack: + blocks = append(blocks, stack, l) + stack = "" + lines = map[string]bool{} + case isStackLine: + if wasStack { + // Detecting two stacks after another, possible cause lines match in + // our tests due to WithStack(WithStack(io.EOF)) on same line. + if detectStackboundaries { + if lines[l] { + if len(stack) == 0 { + return nil, errors.New("len of block must not be zero here") + } + + blocks = append(blocks, stack) + stack = l + lines = map[string]bool{l: true} + continue + } + } + + stack = stack + "\n" + l + } else { + stack = l + } + lines[l] = true + case !isStackLine && !wasStack: + blocks = append(blocks, l) + default: + return nil, errors.New("must not happen") + } + + wasStack = isStackLine + } + + // Use up stack + if stack != "" { + blocks = append(blocks, stack) + } + return blocks, nil +} + +func testFormatCompleteCompare(t *testing.T, n int, arg interface{}, format string, want []string, detectStackBoundaries bool) { + gotStr := fmt.Sprintf(format, arg) + + got, err := parseBlocks(gotStr, detectStackBoundaries) + if err != nil { + t.Fatal(err) + } + + if len(got) != len(want) { + t.Fatalf("test %d: fmt.Sprintf(%s, err) -> wrong number of blocks: got(%d) want(%d)\n got: %s\nwant: %s\ngotStr: %q", + n+1, format, len(got), len(want), prettyBlocks(got), prettyBlocks(want), gotStr) + } + + for i := range got { + if strings.ContainsAny(want[i], "\n") { + // Match as stack + match, err := regexp.MatchString(want[i], got[i]) + if err != nil { + t.Fatal(err) + } + if !match { + t.Fatalf("test %d: block %d: fmt.Sprintf(%q, err):\ngot:\n%q\nwant:\n%q\nall-got:\n%s\nall-want:\n%s\n", + n+1, i+1, format, got[i], want[i], prettyBlocks(got), prettyBlocks(want)) + } + } else { + // Match as message + if got[i] != want[i] { + t.Fatalf("test %d: fmt.Sprintf(%s, err) at block %d got != want:\n got: %q\nwant: %q", n+1, format, i+1, got[i], want[i]) + } + } + } +} + +type wrapper struct { + wrap func(err error) error + want []string +} + +func prettyBlocks(blocks []string, prefix ...string) string { + var out []string + + for _, b := range blocks { + out = append(out, fmt.Sprintf("%v", b)) + } + + return " " + strings.Join(out, "\n ") +} + +func testGenericRecursive(t *testing.T, beforeErr error, beforeWant []string, list []wrapper, maxDepth int) { + if len(beforeWant) == 0 { + panic("beforeWant must not be empty") + } + for _, w := range list { + if len(w.want) == 0 { + panic("want must not be empty") + } + + err := w.wrap(beforeErr) + + // Copy required cause append(beforeWant, ..) modified beforeWant subtly. + beforeCopy := make([]string, len(beforeWant)) + copy(beforeCopy, beforeWant) + + beforeWant := beforeCopy + last := len(beforeWant) - 1 + var want []string + + // Merge two stacks behind each other. + if strings.ContainsAny(beforeWant[last], "\n") && strings.ContainsAny(w.want[0], "\n") { + want = append(beforeWant[:last], append([]string{beforeWant[last] + "((?s).*)" + w.want[0]}, w.want[1:]...)...) + } else { + want = append(beforeWant, w.want...) + } + + testFormatCompleteCompare(t, maxDepth, err, "%+v", want, false) + if maxDepth > 0 { + testGenericRecursive(t, err, want, list, maxDepth-1) + } + } +} 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 +} diff --git a/vendor/github.com/pkg/errors/stack_test.go b/vendor/github.com/pkg/errors/stack_test.go new file mode 100644 index 0000000000..510c27a9f9 --- /dev/null +++ b/vendor/github.com/pkg/errors/stack_test.go @@ -0,0 +1,292 @@ +package errors + +import ( + "fmt" + "runtime" + "testing" +) + +var initpc, _, _, _ = runtime.Caller(0) + +func TestFrameLine(t *testing.T) { + var tests = []struct { + Frame + want int + }{{ + Frame(initpc), + 9, + }, { + func() Frame { + var pc, _, _, _ = runtime.Caller(0) + return Frame(pc) + }(), + 20, + }, { + func() Frame { + var pc, _, _, _ = runtime.Caller(1) + return Frame(pc) + }(), + 28, + }, { + Frame(0), // invalid PC + 0, + }} + + for _, tt := range tests { + got := tt.Frame.line() + want := tt.want + if want != got { + t.Errorf("Frame(%v): want: %v, got: %v", uintptr(tt.Frame), want, got) + } + } +} + +type X struct{} + +func (x X) val() Frame { + var pc, _, _, _ = runtime.Caller(0) + return Frame(pc) +} + +func (x *X) ptr() Frame { + var pc, _, _, _ = runtime.Caller(0) + return Frame(pc) +} + +func TestFrameFormat(t *testing.T) { + var tests = []struct { + Frame + format string + want string + }{{ + Frame(initpc), + "%s", + "stack_test.go", + }, { + Frame(initpc), + "%+s", + "github.com/pkg/errors.init\n" + + "\t.+/github.com/pkg/errors/stack_test.go", + }, { + Frame(0), + "%s", + "unknown", + }, { + Frame(0), + "%+s", + "unknown", + }, { + Frame(initpc), + "%d", + "9", + }, { + Frame(0), + "%d", + "0", + }, { + Frame(initpc), + "%n", + "init", + }, { + func() Frame { + var x X + return x.ptr() + }(), + "%n", + `\(\*X\).ptr`, + }, { + func() Frame { + var x X + return x.val() + }(), + "%n", + "X.val", + }, { + Frame(0), + "%n", + "", + }, { + Frame(initpc), + "%v", + "stack_test.go:9", + }, { + Frame(initpc), + "%+v", + "github.com/pkg/errors.init\n" + + "\t.+/github.com/pkg/errors/stack_test.go:9", + }, { + Frame(0), + "%v", + "unknown:0", + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.Frame, tt.format, tt.want) + } +} + +func TestFuncname(t *testing.T) { + tests := []struct { + name, want string + }{ + {"", ""}, + {"runtime.main", "main"}, + {"github.com/pkg/errors.funcname", "funcname"}, + {"funcname", "funcname"}, + {"io.copyBuffer", "copyBuffer"}, + {"main.(*R).Write", "(*R).Write"}, + } + + for _, tt := range tests { + got := funcname(tt.name) + want := tt.want + if got != want { + t.Errorf("funcname(%q): want: %q, got %q", tt.name, want, got) + } + } +} + +func TestTrimGOPATH(t *testing.T) { + var tests = []struct { + Frame + want string + }{{ + Frame(initpc), + "github.com/pkg/errors/stack_test.go", + }} + + for i, tt := range tests { + pc := tt.Frame.pc() + fn := runtime.FuncForPC(pc) + file, _ := fn.FileLine(pc) + got := trimGOPATH(fn.Name(), file) + testFormatRegexp(t, i, got, "%s", tt.want) + } +} + +func TestStackTrace(t *testing.T) { + tests := []struct { + err error + want []string + }{{ + New("ooh"), []string{ + "github.com/pkg/errors.TestStackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:172", + }, + }, { + Wrap(New("ooh"), "ahh"), []string{ + "github.com/pkg/errors.TestStackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:177", // this is the stack of Wrap, not New + }, + }, { + Cause(Wrap(New("ooh"), "ahh")), []string{ + "github.com/pkg/errors.TestStackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:182", // this is the stack of New + }, + }, { + func() error { return New("ooh") }(), []string{ + `github.com/pkg/errors.(func·009|TestStackTrace.func1)` + + "\n\t.+/github.com/pkg/errors/stack_test.go:187", // this is the stack of New + "github.com/pkg/errors.TestStackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:187", // this is the stack of New's caller + }, + }, { + Cause(func() error { + return func() error { + return Errorf("hello %s", fmt.Sprintf("world")) + }() + }()), []string{ + `github.com/pkg/errors.(func·010|TestStackTrace.func2.1)` + + "\n\t.+/github.com/pkg/errors/stack_test.go:196", // this is the stack of Errorf + `github.com/pkg/errors.(func·011|TestStackTrace.func2)` + + "\n\t.+/github.com/pkg/errors/stack_test.go:197", // this is the stack of Errorf's caller + "github.com/pkg/errors.TestStackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:198", // this is the stack of Errorf's caller's caller + }, + }} + for i, tt := range tests { + x, ok := tt.err.(interface { + StackTrace() StackTrace + }) + if !ok { + t.Errorf("expected %#v to implement StackTrace() StackTrace", tt.err) + continue + } + st := x.StackTrace() + for j, want := range tt.want { + testFormatRegexp(t, i, st[j], "%+v", want) + } + } +} + +func stackTrace() StackTrace { + const depth = 8 + var pcs [depth]uintptr + n := runtime.Callers(1, pcs[:]) + var st stack = pcs[0:n] + return st.StackTrace() +} + +func TestStackTraceFormat(t *testing.T) { + tests := []struct { + StackTrace + format string + want string + }{{ + nil, + "%s", + `\[\]`, + }, { + nil, + "%v", + `\[\]`, + }, { + nil, + "%+v", + "", + }, { + nil, + "%#v", + `\[\]errors.Frame\(nil\)`, + }, { + make(StackTrace, 0), + "%s", + `\[\]`, + }, { + make(StackTrace, 0), + "%v", + `\[\]`, + }, { + make(StackTrace, 0), + "%+v", + "", + }, { + make(StackTrace, 0), + "%#v", + `\[\]errors.Frame{}`, + }, { + stackTrace()[:2], + "%s", + `\[stack_test.go stack_test.go\]`, + }, { + stackTrace()[:2], + "%v", + `\[stack_test.go:225 stack_test.go:272\]`, + }, { + stackTrace()[:2], + "%+v", + "\n" + + "github.com/pkg/errors.stackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:225\n" + + "github.com/pkg/errors.TestStackTraceFormat\n" + + "\t.+/github.com/pkg/errors/stack_test.go:276", + }, { + stackTrace()[:2], + "%#v", + `\[\]errors.Frame{stack_test.go:225, stack_test.go:284}`, + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.StackTrace, tt.format, tt.want) + } +}