1
16
17 package net
18
19 import (
20 "bytes"
21 "encoding/json"
22 "fmt"
23 "io"
24 "log"
25 "net/http"
26 "os"
27 "os/signal"
28 "strings"
29 "syscall"
30 "time"
31
32 "github.com/spf13/cobra"
33
34 "k8s.io/kubernetes/test/images/agnhost/net/common"
35 "k8s.io/kubernetes/test/images/agnhost/net/nat"
36 )
37
38 type runnerMap map[string]common.Runner
39
40 var (
41
42
43 flags struct {
44 Serve string
45 Runner string
46 Options string
47 DelayShutdown int
48 }
49
50 runners = makeRunnerMap()
51 )
52
53 type logOutput struct {
54 b bytes.Buffer
55 }
56
57
58 var CmdNet = &cobra.Command{
59 Use: "net",
60 Short: "Creates webserver or runner for various networking tests",
61 Long: `The subcommand will run the network tester in server mode if the "--serve" flag is given, and the runners are triggered through HTTP requests.
62
63 Alternatively, if the "--runner" flag is given, it will execute the given runner directly. Note that "--runner" and "--serve" flags cannot be given at the same time.
64
65 Examples:
66
67 agnhost net --runner nat-closewait-client --options '{"RemoteAddr":"127.0.0.1:9999"}'
68
69 agnhost net --serve :8889 && curl -v -X POST localhost:8889/run/nat-closewait-server -d '{"LocalAddr":"127.0.0.1:9999"}'
70 `,
71 Args: cobra.MaximumNArgs(0),
72 Run: main,
73 }
74
75 func init() {
76 legalRunners := ""
77 for k := range runners {
78 legalRunners += " " + k
79 }
80 CmdNet.Flags().StringVar(&flags.Serve, "serve", "",
81 "Address and port to bind to (e.g. 127.0.0.1:8080). Setting this will "+
82 "run the network tester in server mode runner are triggered through "+
83 "HTTP requests.")
84 CmdNet.Flags().StringVar(&flags.Runner, "runner", "", "Runner to execute (available:"+legalRunners+")")
85 CmdNet.Flags().StringVar(&flags.Options, "options", "", "JSON options to the Runner")
86 CmdNet.Flags().IntVar(&flags.DelayShutdown, "delay-shutdown", 0, "Number of seconds to delay shutdown when receiving SIGTERM.")
87 }
88
89 func main(cmd *cobra.Command, args []string) {
90 if flags.Runner == "" && flags.Serve == "" {
91 log.Fatalf("Must set either --runner or --serve, see --help")
92 }
93
94 if flags.DelayShutdown > 0 {
95 termCh := make(chan os.Signal, 1)
96 signal.Notify(termCh, syscall.SIGTERM)
97 go func() {
98 <-termCh
99 log.Printf("Sleeping %d seconds before terminating...", flags.DelayShutdown)
100 time.Sleep(time.Duration(flags.DelayShutdown) * time.Second)
101 os.Exit(0)
102 }()
103 }
104
105 log.SetFlags(log.Flags() | log.Lshortfile)
106
107 if flags.Serve == "" {
108 output, err := executeRunner(flags.Runner, flags.Options)
109 if err == nil {
110 fmt.Print("output:\n\n" + output.b.String())
111 os.Exit(0)
112 } else {
113 log.Printf("Error: %v", err)
114 fmt.Print("output:\n\n" + output.b.String())
115 os.Exit(1)
116 }
117 } else {
118 http.HandleFunc("/run/", handleRunRequest)
119 log.Printf("Running server on %v", flags.Serve)
120 log.Fatal(http.ListenAndServe(flags.Serve, nil))
121 }
122 }
123
124 func makeRunnerMap() runnerMap {
125
126 return runnerMap{
127 "nat-closewait-client": nat.NewCloseWaitClient(),
128 "nat-closewait-server": nat.NewCloseWaitServer(),
129 }
130 }
131
132 func executeRunner(name string, rawOptions string) (logOutput, error) {
133 runner, ok := runners[name]
134 if ok {
135 options := runner.NewOptions()
136 if err := json.Unmarshal([]byte(rawOptions), options); err != nil {
137 return logOutput{}, fmt.Errorf("Invalid options JSON: %v", err)
138 }
139
140 log.Printf("Options: %+v", options)
141
142 output := logOutput{}
143 logger := log.New(&output.b, "# ", log.Lshortfile)
144
145 return output, runner.Run(logger, options)
146 }
147
148 return logOutput{}, fmt.Errorf("Invalid runner: '%v', see --help", runner)
149 }
150
151
152 func handleRunRequest(w http.ResponseWriter, r *http.Request) {
153 log.Printf("handleRunRequest %v", *r)
154
155 urlParts := strings.Split(r.URL.Path, "/")
156 if len(urlParts) != 3 {
157 http.Error(w, fmt.Sprintf("invalid request to run: %v", urlParts), 400)
158 return
159 }
160
161 runner := urlParts[2]
162 if r.Body == nil || r.Body == http.NoBody {
163 http.Error(w, "Missing request body", 400)
164 return
165 }
166
167 body, err := io.ReadAll(r.Body)
168 if err != nil {
169 http.Error(w, fmt.Sprintf("error reading body: %v", err), 400)
170 return
171 }
172
173 var output logOutput
174 if output, err = executeRunner(runner, string(body)); err != nil {
175 contents := fmt.Sprintf("Error from runner: %v\noutput:\n\n%s",
176 err, output.b.String())
177 http.Error(w, contents, 500)
178 return
179 }
180
181 fmt.Fprintf(w, "ok\noutput:\n\n"+output.b.String())
182 }
183
View as plain text