Browse code

Added support for openshift infra network-diagnostic-pod

This cmd will be run inside a privileged container and will
perform given network checks on the node.

Ravi Sankar Penta authored on 2016/07/17 17:11:18
Showing 4 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,176 @@
0
+package diagnostics
1
+
2
+import (
3
+	"fmt"
4
+	"io"
5
+	"os"
6
+	"runtime/debug"
7
+
8
+	"github.com/spf13/cobra"
9
+	flag "github.com/spf13/pflag"
10
+
11
+	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
12
+	kutilerrors "k8s.io/kubernetes/pkg/util/errors"
13
+	"k8s.io/kubernetes/pkg/util/sets"
14
+
15
+	"github.com/openshift/origin/pkg/cmd/admin/diagnostics/options"
16
+	"github.com/openshift/origin/pkg/cmd/admin/diagnostics/util"
17
+	"github.com/openshift/origin/pkg/cmd/templates"
18
+	osclientcmd "github.com/openshift/origin/pkg/cmd/util/clientcmd"
19
+	"github.com/openshift/origin/pkg/diagnostics/log"
20
+	networkdiag "github.com/openshift/origin/pkg/diagnostics/networkpod"
21
+	"github.com/openshift/origin/pkg/diagnostics/types"
22
+)
23
+
24
+// NetworkPodDiagnosticsOptions holds values received from environment variables
25
+// for the command to operate.
26
+type NetworkPodDiagnosticsOptions struct {
27
+	// list of diagnostic names to limit what is run
28
+	RequestedDiagnostics []string
29
+	// LogOptions determine globally what the user wants to see and how.
30
+	LogOptions *log.LoggerOptions
31
+	// The Logger is built with the options and should be used for all diagnostic output.
32
+	Logger *log.Logger
33
+}
34
+
35
+var longNetworkPodDiagDescription = templates.LongDesc(`
36
+This utility is intended to run network diagnostics inside a privileged container and
37
+log the results so that the calling diagnostic can report them.
38
+`)
39
+
40
+var (
41
+	// availableNetworkPodDiagnostics contains the names of network diagnostics that can be executed
42
+	// during a single run of diagnostics. Add more diagnostics to the list as they are defined.
43
+	availableNetworkPodDiagnostics = sets.NewString(networkdiag.CheckNodeNetworkName, networkdiag.CheckPodNetworkName, networkdiag.CheckExternalNetworkName, networkdiag.CheckServiceNetworkName, networkdiag.CollectNetworkInfoName)
44
+)
45
+
46
+// NewCommandNetworkPodDiagnostics is the command for running network diagnostics.
47
+func NewCommandNetworkPodDiagnostics(name string, out io.Writer) *cobra.Command {
48
+	o := &NetworkPodDiagnosticsOptions{
49
+		RequestedDiagnostics: []string{},
50
+		LogOptions:           &log.LoggerOptions{Out: out},
51
+	}
52
+
53
+	cmd := &cobra.Command{
54
+		Use:   name,
55
+		Short: "Within a privileged pod, run network diagnostics",
56
+		Long:  fmt.Sprintf(longNetworkPodDiagDescription),
57
+		Run: func(c *cobra.Command, args []string) {
58
+			kcmdutil.CheckErr(o.Complete(args))
59
+
60
+			failed, err, warnCount, errorCount := o.BuildAndRunDiagnostics()
61
+			o.Logger.Summary(warnCount, errorCount)
62
+
63
+			kcmdutil.CheckErr(err)
64
+			if failed {
65
+				os.Exit(255)
66
+			}
67
+
68
+		},
69
+	}
70
+	cmd.SetOutput(out) // for output re: usage / help
71
+
72
+	options.BindLoggerOptionFlags(cmd.Flags(), o.LogOptions, options.RecommendedLoggerOptionFlags())
73
+
74
+	return cmd
75
+}
76
+
77
+// Complete fills in NetworkPodDiagnosticsOptions needed if the command is actually invoked.
78
+func (o *NetworkPodDiagnosticsOptions) Complete(args []string) (err error) {
79
+	o.Logger, err = o.LogOptions.NewLogger()
80
+	if err != nil {
81
+		return err
82
+	}
83
+
84
+	o.RequestedDiagnostics = append(o.RequestedDiagnostics, args...)
85
+	if len(o.RequestedDiagnostics) == 0 {
86
+		o.RequestedDiagnostics = availableNetworkPodDiagnostics.List()
87
+	}
88
+
89
+	return nil
90
+}
91
+
92
+// BuildAndRunDiagnostics builds diagnostics based on the options and executes them, returning a summary.
93
+func (o NetworkPodDiagnosticsOptions) BuildAndRunDiagnostics() (failed bool, err error, numWarnings, numErrors int) {
94
+	failed = false
95
+	errors := []error{}
96
+	diagnostics := []types.Diagnostic{}
97
+
98
+	func() { // don't trust discovery/build of diagnostics; wrap panic nicely in case of developer error
99
+		defer func() {
100
+			if r := recover(); r != nil {
101
+				failed = true
102
+				stack := debug.Stack()
103
+				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))
104
+			}
105
+		}() // deferred panic handler
106
+		networkPodDiags, ok, err := o.buildNetworkPodDiagnostics()
107
+		failed = failed || !ok
108
+		if ok {
109
+			diagnostics = append(diagnostics, networkPodDiags...)
110
+		}
111
+		if err != nil {
112
+			errors = append(errors, err...)
113
+		}
114
+	}()
115
+
116
+	if failed {
117
+		return failed, kutilerrors.NewAggregate(errors), 0, len(errors)
118
+	}
119
+
120
+	failed, err, numWarnings, numErrors = util.RunDiagnostics(o.Logger, diagnostics, 0, len(errors))
121
+	return failed, err, numWarnings, numErrors
122
+}
123
+
124
+// buildNetworkPodDiagnostics builds network Diagnostic objects based on the host environment.
125
+// Returns the Diagnostics built, "ok" bool for whether to proceed or abort, and an error if any was encountered during the building of diagnostics.
126
+func (o NetworkPodDiagnosticsOptions) buildNetworkPodDiagnostics() ([]types.Diagnostic, bool, []error) {
127
+	diagnostics := []types.Diagnostic{}
128
+	err, requestedDiagnostics := util.DetermineRequestedDiagnostics(availableNetworkPodDiagnostics.List(), o.RequestedDiagnostics, o.Logger)
129
+	if err != nil {
130
+		return diagnostics, false, []error{err} // don't waste time on discovery
131
+	}
132
+
133
+	clientFlags := flag.NewFlagSet("client", flag.ContinueOnError) // hide the extensive set of client flags
134
+	factory := osclientcmd.New(clientFlags)                        // that would otherwise be added to this command
135
+
136
+	osClient, kubeClient, clientErr := factory.Clients()
137
+	if clientErr != nil {
138
+		return diagnostics, false, []error{clientErr}
139
+	}
140
+
141
+	for _, diagnosticName := range requestedDiagnostics {
142
+		switch diagnosticName {
143
+
144
+		case networkdiag.CheckNodeNetworkName:
145
+			diagnostics = append(diagnostics, networkdiag.CheckNodeNetwork{
146
+				KubeClient: kubeClient,
147
+			})
148
+
149
+		case networkdiag.CheckPodNetworkName:
150
+			diagnostics = append(diagnostics, networkdiag.CheckPodNetwork{
151
+				KubeClient: kubeClient,
152
+				OSClient:   osClient,
153
+			})
154
+
155
+		case networkdiag.CheckExternalNetworkName:
156
+			diagnostics = append(diagnostics, networkdiag.CheckExternalNetwork{})
157
+
158
+		case networkdiag.CheckServiceNetworkName:
159
+			diagnostics = append(diagnostics, networkdiag.CheckServiceNetwork{
160
+				KubeClient: kubeClient,
161
+				OSClient:   osClient,
162
+			})
163
+
164
+		case networkdiag.CollectNetworkInfoName:
165
+			diagnostics = append(diagnostics, networkdiag.CollectNetworkInfo{
166
+				KubeClient: kubeClient,
167
+			})
168
+
169
+		default:
170
+			return diagnostics, false, []error{fmt.Errorf("unknown diagnostic: %v", diagnosticName)}
171
+		}
172
+	}
173
+
174
+	return diagnostics, true, nil
175
+}
... ...
@@ -13,6 +13,7 @@ import (
13 13
 	"k8s.io/kubernetes/pkg/util/sets"
14 14
 
15 15
 	"github.com/openshift/origin/pkg/cmd/admin/diagnostics/options"
16
+	"github.com/openshift/origin/pkg/cmd/admin/diagnostics/util"
16 17
 	"github.com/openshift/origin/pkg/cmd/templates"
17 18
 	"github.com/openshift/origin/pkg/diagnostics/log"
18 19
 	poddiag "github.com/openshift/origin/pkg/diagnostics/pod"
... ...
@@ -117,7 +118,7 @@ func (o PodDiagnosticsOptions) BuildAndRunDiagnostics() (bool, error, int, int)
117 117
 		return failed, kutilerrors.NewAggregate(errors), 0, len(errors)
118 118
 	}
119 119
 
120
-	failed, err, numWarnings, numErrors := runDiagnostics(o.Logger, diagnostics, 0, len(errors))
120
+	failed, err, numWarnings, numErrors := util.RunDiagnostics(o.Logger, diagnostics, 0, len(errors))
121 121
 	return failed, err, numWarnings, numErrors
122 122
 }
123 123
 
... ...
@@ -131,7 +132,7 @@ var (
131 131
 // Returns the Diagnostics built, "ok" bool for whether to proceed or abort, and an error if any was encountered during the building of diagnostics.
132 132
 func (o PodDiagnosticsOptions) buildPodDiagnostics() ([]types.Diagnostic, bool, []error) {
133 133
 	diagnostics := []types.Diagnostic{}
134
-	err, requestedDiagnostics := determineRequestedDiagnostics(availablePodDiagnostics.List(), o.RequestedDiagnostics, o.Logger)
134
+	err, requestedDiagnostics := util.DetermineRequestedDiagnostics(availablePodDiagnostics.List(), o.RequestedDiagnostics, o.Logger)
135 135
 	if err != nil {
136 136
 		return diagnostics, false, []error{err} // don't waste time on discovery
137 137
 	}
... ...
@@ -157,57 +158,3 @@ func (o PodDiagnosticsOptions) buildPodDiagnostics() ([]types.Diagnostic, bool,
157 157
 
158 158
 	return diagnostics, true, nil
159 159
 }
160
-
161
-// runDiagnostics performs the actual execution of diagnostics once they're built.
162
-func runDiagnostics(logger *log.Logger, diagnostics []types.Diagnostic, warnCount int, errorCount int) (bool, error, int, int) {
163
-	for _, diagnostic := range diagnostics {
164
-		func() { // wrap diagnostic panic nicely in case of developer error
165
-			defer func() {
166
-				if r := recover(); r != nil {
167
-					errorCount += 1
168
-					stack := debug.Stack()
169
-					logger.Error("CED5001",
170
-						fmt.Sprintf("While running the %s diagnostic, a panic was encountered.\nThis is a bug in diagnostics. Error and stack trace follow: \n%s\n%s",
171
-							diagnostic.Name(), fmt.Sprintf("%v", r), stack))
172
-				}
173
-			}()
174
-
175
-			if canRun, reason := diagnostic.CanRun(); !canRun {
176
-				if reason == nil {
177
-					logger.Notice("CED5002", fmt.Sprintf("Skipping diagnostic: %s\nDescription: %s", diagnostic.Name(), diagnostic.Description()))
178
-				} else {
179
-					logger.Notice("CED5003", fmt.Sprintf("Skipping diagnostic: %s\nDescription: %s\nBecause: %s", diagnostic.Name(), diagnostic.Description(), reason.Error()))
180
-				}
181
-				return
182
-			}
183
-
184
-			logger.Notice("CED5004", fmt.Sprintf("Running diagnostic: %s\nDescription: %s", diagnostic.Name(), diagnostic.Description()))
185
-			r := diagnostic.Check()
186
-			for _, entry := range r.Logs() {
187
-				logger.LogEntry(entry)
188
-			}
189
-			warnCount += len(r.Warnings())
190
-			errorCount += len(r.Errors())
191
-		}()
192
-	}
193
-	return errorCount > 0, nil, warnCount, errorCount
194
-}
195
-
196
-// determineRequestedDiagnostics determines which diagnostic the user wants to run
197
-// based on the -d list and the available diagnostics.
198
-// returns error or diagnostic names
199
-func determineRequestedDiagnostics(available []string, requested []string, logger *log.Logger) (error, []string) {
200
-	diagnostics := []string{}
201
-	if len(requested) == 0 { // not specified, use the available list
202
-		diagnostics = available
203
-	} else if diagnostics = sets.NewString(requested...).Intersection(sets.NewString(available...)).List(); len(diagnostics) == 0 {
204
-		logger.Error("CED6001", log.EvalTemplate("CED6001", "None of the requested diagnostics are available:\n  {{.requested}}\nPlease try from the following:\n  {{.available}}",
205
-			log.Hash{"requested": requested, "available": available}))
206
-		return fmt.Errorf("No requested diagnostics available"), diagnostics
207
-	} else if len(diagnostics) < len(requested) {
208
-		logger.Error("CED6002", log.EvalTemplate("CED6002", "Of the requested diagnostics:\n    {{.requested}}\nonly these are available:\n    {{.diagnostics}}\nThe list of all possible is:\n    {{.available}}",
209
-			log.Hash{"requested": requested, "diagnostics": diagnostics, "available": available}))
210
-		return fmt.Errorf("Not all requested diagnostics are available"), diagnostics
211
-	} // else it's a valid list.
212
-	return nil, diagnostics
213
-}
214 160
new file mode 100644
... ...
@@ -0,0 +1,67 @@
0
+package util
1
+
2
+import (
3
+	"fmt"
4
+	"runtime/debug"
5
+
6
+	"k8s.io/kubernetes/pkg/util/sets"
7
+
8
+	"github.com/openshift/origin/pkg/diagnostics/log"
9
+	"github.com/openshift/origin/pkg/diagnostics/types"
10
+)
11
+
12
+// DetermineRequestedDiagnostics determines which diagnostic the user wants to run
13
+// returns error or diagnostic names
14
+func DetermineRequestedDiagnostics(available []string, requested []string, logger *log.Logger) (error, []string) {
15
+	diagnostics := []string{}
16
+
17
+	if len(requested) == 0 { // not specified, use the available list
18
+		diagnostics = available
19
+	} else if diagnostics = sets.NewString(requested...).Intersection(sets.NewString(available...)).List(); len(diagnostics) == 0 {
20
+		logger.Error("CED6001", log.EvalTemplate("CED6001", "None of the requested diagnostics are available:\n  {{.requested}}\nPlease try from the following:\n  {{.available}}",
21
+			log.Hash{"requested": requested, "available": available}))
22
+		return fmt.Errorf("No requested diagnostics available"), diagnostics
23
+	} else if len(diagnostics) < len(requested) {
24
+		logger.Error("CED6002", log.EvalTemplate("CED6002", "Of the requested diagnostics:\n    {{.requested}}\nonly these are available:\n    {{.diagnostics}}\nThe list of all possible is:\n    {{.available}}",
25
+			log.Hash{"requested": requested, "diagnostics": diagnostics, "available": available}))
26
+		return fmt.Errorf("Not all requested diagnostics are available"), diagnostics
27
+	}
28
+
29
+	return nil, diagnostics
30
+}
31
+
32
+// RunDiagnostics performs the actual execution of diagnostics once they're built.
33
+func RunDiagnostics(logger *log.Logger, diagnostics []types.Diagnostic, warnCount int, errorCount int) (bool, error, int, int) {
34
+	for _, diagnostic := range diagnostics {
35
+		func() { // wrap diagnostic panic nicely in case of developer error
36
+			defer func() {
37
+				if r := recover(); r != nil {
38
+					errorCount += 1
39
+					stack := debug.Stack()
40
+					logger.Error("CED7001",
41
+						fmt.Sprintf("While running the %s diagnostic, a panic was encountered.\nThis is a bug in diagnostics. Error and stack trace follow: \n%s\n%s",
42
+							diagnostic.Name(), fmt.Sprintf("%v", r), stack))
43
+				}
44
+			}()
45
+
46
+			if canRun, reason := diagnostic.CanRun(); !canRun {
47
+				if reason == nil {
48
+					logger.Notice("CED7002", fmt.Sprintf("Skipping diagnostic: %s\nDescription: %s", diagnostic.Name(), diagnostic.Description()))
49
+				} else {
50
+					logger.Notice("CED7003", fmt.Sprintf("Skipping diagnostic: %s\nDescription: %s\nBecause: %s", diagnostic.Name(), diagnostic.Description(), reason.Error()))
51
+				}
52
+				return
53
+			}
54
+
55
+			logger.Notice("CED7004", fmt.Sprintf("Running diagnostic: %s\nDescription: %s", diagnostic.Name(), diagnostic.Description()))
56
+			r := diagnostic.Check()
57
+			for _, entry := range r.Logs() {
58
+				logger.LogEntry(entry)
59
+			}
60
+			warnCount += len(r.Warnings())
61
+			errorCount += len(r.Errors())
62
+		}()
63
+	}
64
+
65
+	return errorCount > 0, nil, warnCount, errorCount
66
+}
... ...
@@ -135,6 +135,7 @@ func NewCommandOpenShift(name string) *cobra.Command {
135 135
 		builder.NewCommandS2IBuilder("sti-build"),
136 136
 		builder.NewCommandDockerBuilder("docker-build"),
137 137
 		diagnostics.NewCommandPodDiagnostics("diagnostic-pod", out),
138
+		diagnostics.NewCommandNetworkPodDiagnostics("network-diagnostic-pod", out),
138 139
 	)
139 140
 	root.AddCommand(infra)
140 141