Merge pull request #582 from aaron-prindle/error-reporting-prompt
Added opt in to stackdriver error reportingpull/666/head
commit
a403a949dd
|
@ -25,6 +25,7 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/minikube/pkg/minikube/config"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
)
|
||||
|
||||
|
@ -92,6 +93,18 @@ var settings = []Setting{
|
|||
name: "kubernetes-version",
|
||||
set: SetString,
|
||||
},
|
||||
{
|
||||
name: config.WantUpdateNotification,
|
||||
set: SetBool,
|
||||
},
|
||||
{
|
||||
name: config.ReminderWaitPeriodInHours,
|
||||
set: SetInt,
|
||||
},
|
||||
{
|
||||
name: config.WantReportError,
|
||||
set: SetBool,
|
||||
},
|
||||
}
|
||||
|
||||
var ConfigCmd = &cobra.Command{
|
||||
|
|
|
@ -33,7 +33,7 @@ var configSetCmd = &cobra.Command{
|
|||
fmt.Fprintln(os.Stderr, "usage: minikube config set PROPERTY_NAME PROPERTY_VALUE")
|
||||
os.Exit(1)
|
||||
}
|
||||
err := set(args[0], args[1])
|
||||
err := Set(args[0], args[1])
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
|
@ -45,7 +45,7 @@ func init() {
|
|||
ConfigCmd.AddCommand(configSetCmd)
|
||||
}
|
||||
|
||||
func set(name string, value string) error {
|
||||
func Set(name string, value string) error {
|
||||
s, err := findSetting(name)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -19,7 +19,7 @@ package config
|
|||
import "testing"
|
||||
|
||||
func TestNotFound(t *testing.T) {
|
||||
err := set("nonexistant", "10")
|
||||
err := Set("nonexistant", "10")
|
||||
if err == nil {
|
||||
t.Fatalf("Set did not return error for unknown property")
|
||||
}
|
||||
|
|
|
@ -24,9 +24,9 @@ import (
|
|||
"github.com/docker/machine/libmachine"
|
||||
"github.com/pkg/browser"
|
||||
"github.com/spf13/cobra"
|
||||
cmdUtil "k8s.io/minikube/cmd/util"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
|
||||
commonutil "k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
@ -50,14 +50,14 @@ var dashboardCmd = &cobra.Command{
|
|||
|
||||
if err := commonutil.RetryAfter(20, func() error { return CheckService(namespace, service) }, 6*time.Second); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Could not find finalized endpoint being pointed to by %s: %s", service, err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
|
||||
url, err := cluster.GetServiceURL(api, namespace, service)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
fmt.Fprintln(os.Stderr, "Check that minikube is running.")
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
if dashboardURLMode {
|
||||
fmt.Fprintln(os.Stdout, url)
|
||||
|
|
|
@ -21,9 +21,9 @@ import (
|
|||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/spf13/cobra"
|
||||
cmdUtil "k8s.io/minikube/cmd/util"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
// deleteCmd represents the delete command
|
||||
|
@ -39,7 +39,7 @@ associated files.`,
|
|||
|
||||
if err := cluster.DeleteHost(api); err != nil {
|
||||
fmt.Println("Errors occurred deleting machine: ", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
fmt.Println("Machine deleted.")
|
||||
},
|
||||
|
|
|
@ -30,9 +30,9 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
cmdUtil "k8s.io/minikube/cmd/util"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -241,13 +241,13 @@ var dockerEnvCmd = &cobra.Command{
|
|||
shellCfg, err = shellCfgUnset(api)
|
||||
if err != nil {
|
||||
glog.Errorln("Error setting machine env variable(s):", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
} else {
|
||||
shellCfg, err = shellCfgSet(api)
|
||||
if err != nil {
|
||||
glog.Errorln("Error setting machine env variable(s):", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ import (
|
|||
"github.com/docker/machine/libmachine"
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
cmdUtil "k8s.io/minikube/cmd/util"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
// ipCmd represents the ip command
|
||||
|
@ -37,12 +37,12 @@ var ipCmd = &cobra.Command{
|
|||
host, err := api.Load(constants.MachineName)
|
||||
if err != nil {
|
||||
glog.Errorln("Error getting IP: ", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
ip, err := host.Driver.GetIP()
|
||||
if err != nil {
|
||||
glog.Errorln("Error getting IP: ", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
fmt.Println(ip)
|
||||
},
|
||||
|
|
|
@ -23,9 +23,9 @@ import (
|
|||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/spf13/cobra"
|
||||
cmdUtil "k8s.io/minikube/cmd/util"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
// logsCmd represents the logs command
|
||||
|
@ -39,7 +39,7 @@ var logsCmd = &cobra.Command{
|
|||
s, err := cluster.GetHostLogs(api)
|
||||
if err != nil {
|
||||
log.Println("Error getting machine logs:", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
fmt.Fprintln(os.Stdout, s)
|
||||
},
|
||||
|
|
|
@ -139,5 +139,6 @@ func setupViper() {
|
|||
viper.SetDefault(config.WantUpdateNotification, true)
|
||||
viper.SetDefault(config.ReminderWaitPeriodInHours, 24)
|
||||
viper.SetDefault(config.WantReportError, false)
|
||||
viper.SetDefault(config.WantReportErrorPrompt, true)
|
||||
setFlagsUsingViper()
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
kubeApi "k8s.io/kubernetes/pkg/api"
|
||||
cmdUtil "k8s.io/minikube/cmd/util"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
|
@ -47,7 +48,7 @@ var serviceCmd = &cobra.Command{
|
|||
if len(args) == 0 || len(args) > 1 {
|
||||
errText := "Please specify a service name."
|
||||
fmt.Fprintln(os.Stderr, errText)
|
||||
util.MaybeReportErrorAndExit(errors.New(errText))
|
||||
cmdUtil.MaybeReportErrorAndExit(errors.New(errText))
|
||||
}
|
||||
|
||||
service := args[0]
|
||||
|
@ -57,14 +58,14 @@ var serviceCmd = &cobra.Command{
|
|||
cluster.EnsureMinikubeRunningOrExit(api)
|
||||
if err := util.RetryAfter(20, func() error { return CheckService(namespace, service) }, 6*time.Second); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Could not find finalized endpoint being pointed to by %s: %s", service, err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
|
||||
url, err := cluster.GetServiceURL(api, namespace, service)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
fmt.Fprintln(os.Stderr, "Check that minikube is running and that you have specified the correct namespace (-n flag).")
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
if https {
|
||||
url = strings.Replace(url, "http", "https", 1)
|
||||
|
|
|
@ -19,10 +19,11 @@ package cmd
|
|||
import (
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/golang/glog"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
cmdUtil "k8s.io/minikube/cmd/util"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
// sshCmd represents the docker-ssh command
|
||||
|
@ -34,9 +35,10 @@ var sshCmd = &cobra.Command{
|
|||
api := libmachine.NewClient(constants.Minipath, constants.MakeMiniPath("certs"))
|
||||
defer api.Close()
|
||||
err := cluster.CreateSSHShell(api, args)
|
||||
err = errors.Wrap(err, "Error attempting to ssh/run-ssh-command")
|
||||
if err != nil {
|
||||
glog.Errorln("Error attempting to ssh into machine: ", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
glog.Errorln(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
cfg "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||
cmdUtil "k8s.io/minikube/cmd/util"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/minikube/kubeconfig"
|
||||
|
@ -90,13 +91,13 @@ func runStart(cmd *cobra.Command, args []string) {
|
|||
err := util.Retry(3, start)
|
||||
if err != nil {
|
||||
glog.Errorln("Error starting host: ", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
|
||||
ip, err := host.Driver.GetIP()
|
||||
if err != nil {
|
||||
glog.Errorln("Error starting host: ", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
kubernetesConfig := cluster.KubernetesConfig{
|
||||
KubernetesVersion: viper.GetString(kubernetesVersion),
|
||||
|
@ -107,17 +108,17 @@ func runStart(cmd *cobra.Command, args []string) {
|
|||
}
|
||||
if err := cluster.UpdateCluster(host, host.Driver, kubernetesConfig); err != nil {
|
||||
glog.Errorln("Error updating cluster: ", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
|
||||
if err := cluster.SetupCerts(host.Driver); err != nil {
|
||||
glog.Errorln("Error configuring authentication: ", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
|
||||
if err := cluster.StartCluster(host, kubernetesConfig); err != nil {
|
||||
glog.Errorln("Error starting cluster: ", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
|
||||
kubeHost, err := host.Driver.GetURL()
|
||||
|
@ -134,7 +135,7 @@ func runStart(cmd *cobra.Command, args []string) {
|
|||
clientKey := constants.MakeMiniPath("apiserver.key")
|
||||
if err := setupKubeconfig(name, kubeHost, certAuth, clientCert, clientKey); err != nil {
|
||||
glog.Errorln("Error setting up kubeconfig: ", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
fmt.Println("Kubectl is now configured to use the cluster.")
|
||||
}
|
||||
|
|
|
@ -24,9 +24,9 @@ import (
|
|||
"github.com/docker/machine/libmachine/state"
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
cmdUtil "k8s.io/minikube/cmd/util"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
var statusFormat string
|
||||
|
@ -47,7 +47,7 @@ var statusCmd = &cobra.Command{
|
|||
ms, err := cluster.GetHostStatus(api)
|
||||
if err != nil {
|
||||
glog.Errorln("Error getting machine status:", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
ls := "N/A"
|
||||
if ms == state.Running.String() {
|
||||
|
@ -55,19 +55,19 @@ var statusCmd = &cobra.Command{
|
|||
}
|
||||
if err != nil {
|
||||
glog.Errorln("Error getting machine status:", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
status := Status{ms, ls}
|
||||
|
||||
tmpl, err := template.New("status").Parse(statusFormat)
|
||||
if err != nil {
|
||||
glog.Errorln("Error creating status template:", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
err = tmpl.Execute(os.Stdout, status)
|
||||
if err != nil {
|
||||
glog.Errorln("Error executing status template:", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -21,9 +21,9 @@ import (
|
|||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/spf13/cobra"
|
||||
cmdUtil "k8s.io/minikube/cmd/util"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
// stopCmd represents the stop command
|
||||
|
@ -39,7 +39,7 @@ itself, leaving all files intact. The cluster can be started again with the "sta
|
|||
|
||||
if err := cluster.StopHost(api); err != nil {
|
||||
fmt.Println("Error stopping machine: ", err)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
cmdUtil.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
fmt.Println("Machine stopped.")
|
||||
},
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
minikubeConfig "k8s.io/minikube/cmd/minikube/cmd/config"
|
||||
"k8s.io/minikube/pkg/minikube/config"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/version"
|
||||
)
|
||||
|
||||
type ServiceContext struct {
|
||||
Service string `json:"service"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Message string `json:"message"`
|
||||
ServiceContext `json:"serviceContext"`
|
||||
}
|
||||
|
||||
func ReportError(err error, url string) error {
|
||||
errMsg, err := FormatError(err)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error formatting error message")
|
||||
}
|
||||
jsonErrorMsg, err := MarshallError(errMsg, "default", version.GetVersion())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error marshalling error message to JSON")
|
||||
}
|
||||
err = UploadError(jsonErrorMsg, url)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error uploading error message")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func FormatError(err error) (string, error) {
|
||||
if err == nil {
|
||||
return "", errors.New("Error: ReportError was called with nil error value")
|
||||
}
|
||||
|
||||
type stackTracer interface {
|
||||
StackTrace() errors.StackTrace
|
||||
}
|
||||
|
||||
errOutput := []string{}
|
||||
errOutput = append(errOutput, err.Error())
|
||||
|
||||
if err, ok := err.(stackTracer); ok {
|
||||
for _, f := range err.StackTrace() {
|
||||
errOutput = append(errOutput, fmt.Sprintf("\tat %n(%v)", f, f))
|
||||
}
|
||||
} else {
|
||||
return "", errors.New("Error msg with no stack trace cannot be reported")
|
||||
}
|
||||
return strings.Join(errOutput, "\n"), nil
|
||||
}
|
||||
|
||||
func MarshallError(errMsg, service, version string) ([]byte, error) {
|
||||
m := Message{errMsg, ServiceContext{service, version}}
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func UploadError(b []byte, url string) error {
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(b))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
} else if resp.StatusCode != 200 {
|
||||
return errors.Errorf("Error sending error report to %s, got response code %s", url, resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func MaybeReportErrorAndExit(errToReport error) {
|
||||
var err error
|
||||
if viper.GetBool(config.WantReportError) {
|
||||
err = ReportError(errToReport, constants.ReportingURL)
|
||||
} else if viper.GetBool(config.WantReportErrorPrompt) {
|
||||
fmt.Println("========================================" +
|
||||
"An error has occurred. Would you like to opt in to sending anonymized crash information to minikube to help prevent future errors?" +
|
||||
"(To opt out of these messages, run the command)\n\tminikube config set WantReportErrorPrompt false" +
|
||||
"========================================")
|
||||
if PromptUserForAccept(os.Stdin) {
|
||||
minikubeConfig.Set(config.WantReportError, "true")
|
||||
err = ReportError(errToReport, constants.ReportingURL)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
glog.Errorf(err.Error())
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func getInput(input chan string, r io.Reader) {
|
||||
reader := bufio.NewReader(r)
|
||||
fmt.Print("Please enter your response [Y/n]: \n")
|
||||
response, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
glog.Errorf(err.Error())
|
||||
}
|
||||
input <- response
|
||||
}
|
||||
|
||||
func PromptUserForAccept(r io.Reader) bool {
|
||||
if !terminal.IsTerminal(int(os.Stdout.Fd())) {
|
||||
return false
|
||||
}
|
||||
input := make(chan string, 1)
|
||||
go getInput(input, r)
|
||||
select {
|
||||
case response := <-input:
|
||||
response = strings.ToLower(strings.TrimSpace(response))
|
||||
if response == "y" || response == "yes" || response == "" {
|
||||
return true
|
||||
} else if response == "n" || response == "no" {
|
||||
return false
|
||||
} else {
|
||||
fmt.Println("Invalid response, error reporting remains disabled. Must be in form [Y/n]")
|
||||
return false
|
||||
}
|
||||
case <-time.After(30 * time.Second):
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"k8s.io/minikube/pkg/version"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestFormatError(t *testing.T) {
|
||||
var testErr error
|
||||
if _, err := FormatError(testErr); err == nil {
|
||||
t.Fatalf("FormatError should have errored with a nil error input")
|
||||
}
|
||||
testErr = fmt.Errorf("Not a valid error to format as there is no stacktrace")
|
||||
|
||||
if out, err := FormatError(testErr); err == nil {
|
||||
t.Fatalf("FormatError should have errored with a non pkg/errors error (no stacktrace info): %s", out)
|
||||
}
|
||||
|
||||
testErr = errors.New("TestFormatError 1")
|
||||
errors.Wrap(testErr, "TestFormatError 2")
|
||||
errors.Wrap(testErr, "TestFormatError 3")
|
||||
|
||||
_, err := FormatError(testErr)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshallError(t *testing.T) {
|
||||
testErr := errors.New("TestMarshallError 1")
|
||||
errors.Wrap(testErr, "TestMarshallError 2")
|
||||
errors.Wrap(testErr, "TestMarshallError 3")
|
||||
|
||||
errMsg, _ := FormatError(testErr)
|
||||
if _, err := MarshallError(errMsg, "default", version.GetVersion()); err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUploadError(t *testing.T) {
|
||||
testErr := errors.New("TestUploadError 1")
|
||||
errors.Wrap(testErr, "TestUploadError 2")
|
||||
errors.Wrap(testErr, "TestUploadError 3")
|
||||
errMsg, _ := FormatError(testErr)
|
||||
jsonErrMsg, _ := MarshallError(errMsg, "default", version.GetVersion())
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Hello, world!")
|
||||
}))
|
||||
|
||||
if err := UploadError(jsonErrMsg, server.URL); err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
|
||||
server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "failed to write report", 400)
|
||||
}))
|
||||
if err := UploadError(jsonErrMsg, server.URL); err == nil {
|
||||
t.Fatalf("UploadError should have errored from a 400 response")
|
||||
}
|
||||
}
|
|
@ -17,6 +17,9 @@ Configurable fields:
|
|||
* show-libmachine-logs
|
||||
* log_dir
|
||||
* kubernetes-version
|
||||
* WantUpdateNotification
|
||||
* ReminderWaitPeriodInHours
|
||||
* WantReportError
|
||||
|
||||
```
|
||||
minikube config SUBCOMMAND [flags]
|
||||
|
|
|
@ -20,4 +20,5 @@ const (
|
|||
WantUpdateNotification = "WantUpdateNotification"
|
||||
ReminderWaitPeriodInHours = "ReminderWaitPeriodInHours"
|
||||
WantReportError = "WantReportError"
|
||||
WantReportErrorPrompt = "WantReportErrorPrompt"
|
||||
)
|
||||
|
|
|
@ -85,3 +85,5 @@ var LocalkubeLinuxFilename = "localkube-linux-amd64"
|
|||
|
||||
// DockerAPIVersion is the API version implemented by Docker running in the minikube VM.
|
||||
const DockerAPIVersion = "1.23"
|
||||
|
||||
const ReportingURL = "https://clouderrorreporting.googleapis.com/v1beta1/projects/k8s-minikube/events:report?key=AIzaSyACUwzG0dEPcl-eOgpDKnyKoUFgHdfoFuA"
|
||||
|
|
|
@ -28,5 +28,3 @@ const (
|
|||
func GetAlternateDNS(domain string) []string {
|
||||
return []string{"kubernetes.default.svc." + domain, "kubernetes.default.svc", "kubernetes.default", "kubernetes"}
|
||||
}
|
||||
|
||||
const reportingURL = "https://clouderrorreporting.googleapis.com/v1beta1/projects/k8s-minikube/events:report?key=AIzaSyACUwzG0dEPcl-eOgpDKnyKoUFgHdfoFuA"
|
||||
|
|
|
@ -17,22 +17,17 @@ limitations under the License.
|
|||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/minikube/pkg/minikube/config"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/version"
|
||||
)
|
||||
|
@ -159,88 +154,3 @@ func IsDirectory(path string) (bool, error) {
|
|||
}
|
||||
return fileInfo.IsDir(), nil
|
||||
}
|
||||
|
||||
type ServiceContext struct {
|
||||
Service string `json:"service"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Message string `json:"message"`
|
||||
ServiceContext `json:"serviceContext"`
|
||||
}
|
||||
|
||||
func ReportError(err error, url string) error {
|
||||
errMsg, err := FormatError(err)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
jsonErrorMsg, err := MarshallError(errMsg, "default", version.GetVersion())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
err = UploadError(jsonErrorMsg, url)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func FormatError(err error) (string, error) {
|
||||
if err == nil {
|
||||
return "", errors.New("Error: ReportError was called with nil error value")
|
||||
}
|
||||
// Extract the stacktrace from the error messages in their orig format
|
||||
errMsg := fmt.Sprintf("%+v\n", err)
|
||||
|
||||
errArray := strings.Split(errMsg, "\n")
|
||||
errOutput := []string{}
|
||||
|
||||
//Error message must have at least 1 message w/ 1 stack trace(2 lines) -> 3 lines, 2 index
|
||||
if len(errArray) <= 2 {
|
||||
return "", errors.New("Error msg with no stack trace cannot be reported")
|
||||
}
|
||||
// This code is to format the error stacktraces so that StackDriver will accept them
|
||||
errOutput = append(errOutput, errArray[0])
|
||||
for i := 1; i < len(errArray)-1; i += 2 {
|
||||
errOutput = append(errOutput, fmt.Sprintf("\tat %s (%s)", errArray[i],
|
||||
filepath.Base(errArray[i+1])))
|
||||
}
|
||||
return strings.Join(errOutput, "\n") + "\n", nil
|
||||
}
|
||||
|
||||
func MarshallError(errMsg, service, version string) ([]byte, error) {
|
||||
m := Message{errMsg, ServiceContext{service, version}}
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func UploadError(b []byte, url string) error {
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(b))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "")
|
||||
} else if resp.StatusCode != 200 {
|
||||
return errors.Errorf("Error sending error report to %s, got response code %s", url, resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func MaybeReportErrorAndExit(err error) {
|
||||
if viper.GetBool(config.WantReportError) {
|
||||
ReportError(err, reportingURL)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
|
|
@ -17,14 +17,10 @@ limitations under the License.
|
|||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/version"
|
||||
)
|
||||
|
||||
// Returns a function that will return n errors, then return successfully forever.
|
||||
|
@ -112,58 +108,3 @@ Error 2`
|
|||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatError(t *testing.T) {
|
||||
var testErr error
|
||||
if _, err := FormatError(testErr); err == nil {
|
||||
t.Fatalf("FormatError should have errored with a nil error input")
|
||||
}
|
||||
testErr = fmt.Errorf("Not a valid error to format as there is no stacktrace")
|
||||
|
||||
if out, err := FormatError(testErr); err == nil {
|
||||
t.Fatalf("FormatError should have errored with a non pkg/errors error (no stacktrace info): %s", out)
|
||||
}
|
||||
|
||||
testErr = errors.New("TestFormatError 1")
|
||||
errors.Wrap(testErr, "TestFormatError 2")
|
||||
errors.Wrap(testErr, "TestFormatError 3")
|
||||
|
||||
_, err := FormatError(testErr)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshallError(t *testing.T) {
|
||||
testErr := errors.New("TestMarshallError 1")
|
||||
errors.Wrap(testErr, "TestMarshallError 2")
|
||||
errors.Wrap(testErr, "TestMarshallError 3")
|
||||
|
||||
errMsg, _ := FormatError(testErr)
|
||||
if _, err := MarshallError(errMsg, "default", version.GetVersion()); err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUploadError(t *testing.T) {
|
||||
testErr := errors.New("TestUploadError 1")
|
||||
errors.Wrap(testErr, "TestUploadError 2")
|
||||
errors.Wrap(testErr, "TestUploadError 3")
|
||||
errMsg, _ := FormatError(testErr)
|
||||
jsonErrMsg, _ := MarshallError(errMsg, "default", version.GetVersion())
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Hello, world!")
|
||||
}))
|
||||
|
||||
if err := UploadError(jsonErrMsg, server.URL); err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
|
||||
server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "failed to write report", 400)
|
||||
}))
|
||||
if err := UploadError(jsonErrMsg, server.URL); err == nil {
|
||||
t.Fatalf("UploadError should have errored from a 400 response")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue