package diagnostics import ( "fmt" "io" "os" "runtime/debug" "github.com/spf13/cobra" flag "github.com/spf13/pflag" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" kutilerrors "k8s.io/kubernetes/pkg/util/errors" "k8s.io/kubernetes/pkg/util/sets" "github.com/openshift/origin/pkg/cmd/admin/diagnostics/options" "github.com/openshift/origin/pkg/cmd/admin/diagnostics/util" "github.com/openshift/origin/pkg/cmd/templates" osclientcmd "github.com/openshift/origin/pkg/cmd/util/clientcmd" "github.com/openshift/origin/pkg/diagnostics/log" networkdiag "github.com/openshift/origin/pkg/diagnostics/networkpod" "github.com/openshift/origin/pkg/diagnostics/types" ) // NetworkPodDiagnosticsOptions holds values received from environment variables // for the command to operate. type NetworkPodDiagnosticsOptions struct { // list of diagnostic names to limit what is run RequestedDiagnostics []string // LogOptions determine globally what the user wants to see and how. LogOptions *log.LoggerOptions // The Logger is built with the options and should be used for all diagnostic output. Logger *log.Logger } var longNetworkPodDiagDescription = templates.LongDesc(` This utility is intended to run network diagnostics inside a privileged container and log the results so that the calling diagnostic can report them. `) var ( // availableNetworkPodDiagnostics contains the names of network diagnostics that can be executed // during a single run of diagnostics. Add more diagnostics to the list as they are defined. availableNetworkPodDiagnostics = sets.NewString(networkdiag.CheckNodeNetworkName, networkdiag.CheckPodNetworkName, networkdiag.CheckExternalNetworkName, networkdiag.CheckServiceNetworkName, networkdiag.CollectNetworkInfoName) ) // NewCommandNetworkPodDiagnostics is the command for running network diagnostics. func NewCommandNetworkPodDiagnostics(name string, out io.Writer) *cobra.Command { o := &NetworkPodDiagnosticsOptions{ RequestedDiagnostics: []string{}, LogOptions: &log.LoggerOptions{Out: out}, } cmd := &cobra.Command{ Use: name, Short: "Within a privileged pod, run network diagnostics", Long: fmt.Sprintf(longNetworkPodDiagDescription), Run: func(c *cobra.Command, args []string) { kcmdutil.CheckErr(o.Complete(args)) failed, err, warnCount, errorCount := o.BuildAndRunDiagnostics() o.Logger.Summary(warnCount, errorCount) kcmdutil.CheckErr(err) if failed { os.Exit(255) } }, } cmd.SetOutput(out) // for output re: usage / help options.BindLoggerOptionFlags(cmd.Flags(), o.LogOptions, options.RecommendedLoggerOptionFlags()) return cmd } // Complete fills in NetworkPodDiagnosticsOptions needed if the command is actually invoked. func (o *NetworkPodDiagnosticsOptions) Complete(args []string) (err error) { o.Logger, err = o.LogOptions.NewLogger() if err != nil { return err } o.RequestedDiagnostics = append(o.RequestedDiagnostics, args...) if len(o.RequestedDiagnostics) == 0 { o.RequestedDiagnostics = availableNetworkPodDiagnostics.List() } return nil } // BuildAndRunDiagnostics builds diagnostics based on the options and executes them, returning a summary. func (o NetworkPodDiagnosticsOptions) BuildAndRunDiagnostics() (failed bool, err error, numWarnings, numErrors int) { failed = false errors := []error{} diagnostics := []types.Diagnostic{} func() { // don't trust discovery/build of diagnostics; wrap panic nicely in case of developer error defer func() { if r := recover(); r != nil { failed = true stack := debug.Stack() errors = append(errors, fmt.Errorf("While building the diagnostics, a panic was encountered.\nThis is a bug in diagnostics. Error and stack trace follow: \n%v\n%s", r, stack)) } }() // deferred panic handler networkPodDiags, ok, err := o.buildNetworkPodDiagnostics() failed = failed || !ok if ok { diagnostics = append(diagnostics, networkPodDiags...) } if err != nil { errors = append(errors, err...) } }() if failed { return failed, kutilerrors.NewAggregate(errors), 0, len(errors) } failed, err, numWarnings, numErrors = util.RunDiagnostics(o.Logger, diagnostics, 0, len(errors)) return failed, err, numWarnings, numErrors } // buildNetworkPodDiagnostics builds network Diagnostic objects based on the host environment. // Returns the Diagnostics built, "ok" bool for whether to proceed or abort, and an error if any was encountered during the building of diagnostics. func (o NetworkPodDiagnosticsOptions) buildNetworkPodDiagnostics() ([]types.Diagnostic, bool, []error) { diagnostics := []types.Diagnostic{} err, requestedDiagnostics := util.DetermineRequestedDiagnostics(availableNetworkPodDiagnostics.List(), o.RequestedDiagnostics, o.Logger) if err != nil { return diagnostics, false, []error{err} // don't waste time on discovery } clientFlags := flag.NewFlagSet("client", flag.ContinueOnError) // hide the extensive set of client flags factory := osclientcmd.New(clientFlags) // that would otherwise be added to this command osClient, _, kubeClient, clientErr := factory.Clients() if clientErr != nil { return diagnostics, false, []error{clientErr} } for _, diagnosticName := range requestedDiagnostics { switch diagnosticName { case networkdiag.CheckNodeNetworkName: diagnostics = append(diagnostics, networkdiag.CheckNodeNetwork{ KubeClient: kubeClient, }) case networkdiag.CheckPodNetworkName: diagnostics = append(diagnostics, networkdiag.CheckPodNetwork{ KubeClient: kubeClient, OSClient: osClient, }) case networkdiag.CheckExternalNetworkName: diagnostics = append(diagnostics, networkdiag.CheckExternalNetwork{}) case networkdiag.CheckServiceNetworkName: diagnostics = append(diagnostics, networkdiag.CheckServiceNetwork{ KubeClient: kubeClient, OSClient: osClient, }) case networkdiag.CollectNetworkInfoName: diagnostics = append(diagnostics, networkdiag.CollectNetworkInfo{ KubeClient: kubeClient, }) default: return diagnostics, false, []error{fmt.Errorf("unknown diagnostic: %v", diagnosticName)} } } return diagnostics, true, nil }