Merge pull request #533 from aaron-prindle/add-error-reporting
Added error reporting functionality and testingpull/581/head
commit
17db10a4f2
|
@ -18,8 +18,9 @@ package config
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var configGetCmd = &cobra.Command{
|
||||
|
|
|
@ -18,8 +18,9 @@ package config
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var configSetCmd = &cobra.Command{
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
|
||||
commonutil "k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
@ -48,15 +49,15 @@ var dashboardCmd = &cobra.Command{
|
|||
service := "kubernetes-dashboard"
|
||||
|
||||
if err := commonutil.RetryAfter(20, func() error { return CheckService(namespace, service) }, 6*time.Second); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not find finalized endpoint being pointed to by %s: %s\n", service, err)
|
||||
os.Exit(1)
|
||||
fmt.Fprintln(os.Stderr, "Could not find finalized endpoint being pointed to by %s: %s", service, err)
|
||||
util.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.")
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
if dashboardURLMode {
|
||||
fmt.Fprintln(os.Stdout, url)
|
||||
|
|
|
@ -18,12 +18,12 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/spf13/cobra"
|
||||
"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)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
fmt.Println("Machine deleted.")
|
||||
},
|
||||
|
|
|
@ -32,6 +32,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -240,13 +241,13 @@ var dockerEnvCmd = &cobra.Command{
|
|||
shellCfg, err = shellCfgUnset(api)
|
||||
if err != nil {
|
||||
glog.Errorln("Error setting machine env variable(s):", err)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
} else {
|
||||
shellCfg, err = shellCfgSet(api)
|
||||
if err != nil {
|
||||
glog.Errorln("Error setting machine env variable(s):", err)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,13 +18,12 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
// ipCmd represents the ip command
|
||||
|
@ -38,12 +37,12 @@ var ipCmd = &cobra.Command{
|
|||
host, err := api.Load(constants.MachineName)
|
||||
if err != nil {
|
||||
glog.Errorln("Error getting IP: ", err)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
ip, err := host.Driver.GetIP()
|
||||
if err != nil {
|
||||
glog.Errorln("Error getting IP: ", err)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
fmt.Println(ip)
|
||||
},
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
// logsCmd represents the logs command
|
||||
|
@ -38,7 +39,7 @@ var logsCmd = &cobra.Command{
|
|||
s, err := cluster.GetHostLogs(api)
|
||||
if err != nil {
|
||||
log.Println("Error getting machine logs:", err)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
fmt.Fprintln(os.Stdout, s)
|
||||
},
|
||||
|
|
|
@ -138,5 +138,6 @@ func setupViper() {
|
|||
|
||||
viper.SetDefault(config.WantUpdateNotification, true)
|
||||
viper.SetDefault(config.ReminderWaitPeriodInHours, 24)
|
||||
viper.SetDefault(config.WantReportError, false)
|
||||
setFlagsUsingViper()
|
||||
}
|
||||
|
|
|
@ -29,8 +29,7 @@ import (
|
|||
kubeApi "k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
|
||||
commonutil "k8s.io/minikube/pkg/util"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -46,8 +45,9 @@ var serviceCmd = &cobra.Command{
|
|||
Long: `Gets the kubernetes URL for the specified service in your local cluster`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) == 0 || len(args) > 1 {
|
||||
fmt.Fprintln(os.Stderr, "Please specify a service name.")
|
||||
os.Exit(1)
|
||||
errText := "Please specify a service name."
|
||||
fmt.Fprintln(os.Stderr, errText)
|
||||
util.MaybeReportErrorAndExit(errors.New(errText))
|
||||
}
|
||||
|
||||
service := args[0]
|
||||
|
@ -55,16 +55,16 @@ var serviceCmd = &cobra.Command{
|
|||
defer api.Close()
|
||||
|
||||
cluster.EnsureMinikubeRunningOrExit(api)
|
||||
if err := commonutil.RetryAfter(20, func() error { return CheckService(namespace, service) }, 6*time.Second); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not find finalized endpoint being pointed to by %s: %s\n", service, err)
|
||||
os.Exit(1)
|
||||
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)
|
||||
}
|
||||
|
||||
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).")
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
if https {
|
||||
url = strings.Replace(url, "http", "https", 1)
|
||||
|
|
|
@ -17,13 +17,12 @@ limitations under the License.
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
// sshCmd represents the docker-ssh command
|
||||
|
@ -37,7 +36,7 @@ var sshCmd = &cobra.Command{
|
|||
err := cluster.CreateSSHShell(api, args)
|
||||
if err != nil {
|
||||
glog.Errorln("Error attempting to ssh into machine: ", err)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
@ -90,13 +89,13 @@ func runStart(cmd *cobra.Command, args []string) {
|
|||
err := util.Retry(3, start)
|
||||
if err != nil {
|
||||
glog.Errorln("Error starting host: ", err)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
|
||||
ip, err := host.Driver.GetIP()
|
||||
if err != nil {
|
||||
glog.Errorln("Error starting host: ", err)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
kubernetesConfig := cluster.KubernetesConfig{
|
||||
KubernetesVersion: viper.GetString(kubernetesVersion),
|
||||
|
@ -106,17 +105,17 @@ func runStart(cmd *cobra.Command, args []string) {
|
|||
}
|
||||
if err := cluster.UpdateCluster(host, host.Driver, kubernetesConfig); err != nil {
|
||||
glog.Errorln("Error updating cluster: ", err)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
|
||||
if err := cluster.SetupCerts(host.Driver); err != nil {
|
||||
glog.Errorln("Error configuring authentication: ", err)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
|
||||
if err := cluster.StartCluster(host, kubernetesConfig); err != nil {
|
||||
glog.Errorln("Error starting cluster: ", err)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
|
||||
kubeHost, err := host.Driver.GetURL()
|
||||
|
@ -133,7 +132,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)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
fmt.Println("Kubectl is now configured to use the cluster.")
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
var statusFormat string
|
||||
|
@ -46,7 +47,7 @@ var statusCmd = &cobra.Command{
|
|||
ms, err := cluster.GetHostStatus(api)
|
||||
if err != nil {
|
||||
glog.Errorln("Error getting machine status:", err)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
ls := "N/A"
|
||||
if ms == state.Running.String() {
|
||||
|
@ -54,19 +55,19 @@ var statusCmd = &cobra.Command{
|
|||
}
|
||||
if err != nil {
|
||||
glog.Errorln("Error getting machine status:", err)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
status := Status{ms, ls}
|
||||
|
||||
tmpl, err := template.New("status").Parse(statusFormat)
|
||||
if err != nil {
|
||||
glog.Errorln("Error creating status template:", err)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
err = tmpl.Execute(os.Stdout, status)
|
||||
if err != nil {
|
||||
glog.Errorln("Error executing status template:", err)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -18,12 +18,12 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/spf13/cobra"
|
||||
"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)
|
||||
os.Exit(1)
|
||||
util.MaybeReportErrorAndExit(err)
|
||||
}
|
||||
fmt.Println("Machine stopped.")
|
||||
},
|
||||
|
|
|
@ -19,4 +19,5 @@ package config
|
|||
const (
|
||||
WantUpdateNotification = "WantUpdateNotification"
|
||||
ReminderWaitPeriodInHours = "ReminderWaitPeriodInHours"
|
||||
WantReportError = "WantReportError"
|
||||
)
|
||||
|
|
|
@ -28,3 +28,5 @@ 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,15 +17,21 @@ limitations under the License.
|
|||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"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"
|
||||
)
|
||||
|
@ -128,6 +134,14 @@ func (m MultiError) ToError() error {
|
|||
return errors.New(strings.Join(errStrings, "\n"))
|
||||
}
|
||||
|
||||
func IsDirectory(path string) (bool, error) {
|
||||
fileInfo, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "Error calling os.Stat on file %s", path)
|
||||
}
|
||||
return fileInfo.IsDir(), nil
|
||||
}
|
||||
|
||||
type ServiceContext struct {
|
||||
Service string `json:"service"`
|
||||
Version string `json:"version"`
|
||||
|
@ -138,10 +152,77 @@ type Message struct {
|
|||
ServiceContext `json:"serviceContext"`
|
||||
}
|
||||
|
||||
func IsDirectory(path string) (bool, error) {
|
||||
fileInfo, err := os.Stat(path)
|
||||
func ReportError(err error, url string) error {
|
||||
errMsg, err := FormatError(err)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "Error calling os.Stat on file %s", path)
|
||||
return errors.Wrap(err, "")
|
||||
}
|
||||
return fileInfo.IsDir(), nil
|
||||
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,10 +17,14 @@ 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.
|
||||
|
@ -108,3 +112,58 @@ 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