github.com/vmware/govmomi@v0.51.0/toolbox/process/process_test.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package process 6 7 import ( 8 "bufio" 9 "bytes" 10 "context" 11 "errors" 12 "fmt" 13 "io" 14 "net/http" 15 "net/http/httptest" 16 "net/url" 17 "runtime" 18 "strconv" 19 "testing" 20 "time" 21 22 "github.com/vmware/govmomi/toolbox/vix" 23 ) 24 25 func checkGOOS(t *testing.T) { 26 switch runtime.GOOS { 27 case "linux", "darwin": 28 default: 29 t.Skipf("GOOS=%s", runtime.GOOS) 30 } 31 } 32 33 func TestProcessFunction(t *testing.T) { 34 checkGOOS(t) 35 36 m := NewManager() 37 var pids []int64 38 39 for i := 0; i <= 2; i++ { 40 r := &vix.StartProgramRequest{ 41 ProgramPath: "test", 42 Arguments: strconv.Itoa(i), 43 } 44 45 pid, _ := m.Start(r, NewFunc(func(_ context.Context, arg string) error { 46 rc, _ := strconv.Atoi(arg) 47 if rc == 0 { 48 return nil 49 50 } 51 return &Error{Err: errors.New("fail"), ExitCode: int32(rc)} 52 })) 53 54 if pid == 0 { 55 t.Fatalf("no pid") 56 } 57 58 pids = append(pids, pid) 59 } 60 61 m.wg.Wait() 62 63 _ = m.ListProcesses(pids) 64 65 for i, pid := range pids { 66 p := m.entries[pid] 67 if p.ExitCode != int32(i) { 68 t.Errorf("%d: %d != %d", pid, p.ExitCode, i) 69 } 70 } 71 } 72 73 func TestProcessCommand(t *testing.T) { 74 checkGOOS(t) 75 76 m := NewManager() 77 var pids []int64 78 79 for i := 0; i <= 2; i++ { 80 r := &vix.StartProgramRequest{ 81 ProgramPath: shell, 82 Arguments: fmt.Sprintf(`-c "exit %d"`, i), 83 } 84 85 pid, err := m.Start(r, New()) 86 if err != nil { 87 t.Fatal(err) 88 } 89 pids = append(pids, pid) 90 } 91 92 m.wg.Wait() 93 94 _ = m.ListProcesses(nil) 95 96 for i, pid := range pids { 97 p := m.entries[pid] 98 if p.ExitCode != int32(i) { 99 t.Errorf("%d: %d != %d", pid, p.ExitCode, i) 100 } 101 } 102 103 r := &vix.StartProgramRequest{ 104 ProgramPath: shell, 105 } 106 107 shell = "/enoent/enoent" 108 _, err := m.Start(r, New()) 109 if err == nil { 110 t.Error("expected error") 111 } 112 shell = r.ProgramPath 113 114 r.ProgramPath = "/enoent/enoent" 115 _, err = m.Start(r, New()) 116 if err == nil { 117 t.Error("expected error") 118 } 119 } 120 121 func TestProcessKill(t *testing.T) { 122 checkGOOS(t) 123 124 m := NewManager() 125 var pids []int64 126 127 procs := []struct { 128 r *vix.StartProgramRequest 129 p *Process 130 }{ 131 { 132 &vix.StartProgramRequest{ 133 ProgramPath: "test", 134 Arguments: "none", 135 }, 136 NewFunc(func(ctx context.Context, _ string) error { 137 select { 138 case <-ctx.Done(): 139 return &Error{Err: ctx.Err(), ExitCode: 42} 140 case <-time.After(time.Minute): 141 } 142 143 return nil 144 }), 145 }, 146 { 147 &vix.StartProgramRequest{ 148 ProgramPath: shell, 149 Arguments: fmt.Sprintf(`-c "while true; do sleep 1; done"`), 150 }, 151 New(), 152 }, 153 } 154 155 for _, test := range procs { 156 pid, err := m.Start(test.r, test.p) 157 if err != nil { 158 t.Fatal(err) 159 } 160 161 pids = append(pids, pid) 162 } 163 164 for { 165 b := m.ListProcesses(pids) 166 if bytes.Count(b, []byte("<proc>")) == len(pids) { 167 break 168 } 169 170 <-time.After(time.Millisecond * 100) 171 } 172 173 for _, pid := range pids { 174 if !m.Kill(pid) { 175 t.Errorf("kill %d", pid) 176 } 177 } 178 179 m.wg.Wait() 180 181 for _, pid := range pids { 182 p := m.entries[pid] 183 184 if p.ExitCode == 0 { 185 t.Errorf("%s: exit=%d", p.Name, p.ExitCode) 186 } 187 } 188 189 if m.Kill(-1) { 190 t.Error("kill -1") 191 } 192 } 193 194 func TestProcessRemove(t *testing.T) { 195 checkGOOS(t) 196 197 m := NewManager() 198 199 m.expire = time.Millisecond 200 201 r := &vix.StartProgramRequest{ 202 ProgramPath: "test", 203 } 204 205 pid, _ := m.Start(r, NewFunc(func(_ context.Context, arg string) error { 206 return nil 207 })) 208 209 m.wg.Wait() 210 211 <-time.After(m.expire * 20) 212 // pid should be removed by now 213 b := m.ListProcesses([]int64{pid}) 214 if len(b) != 0 { 215 t.Error("expected 0 processes") 216 } 217 } 218 219 func TestEscapeXML(t *testing.T) { 220 tests := []struct { 221 in string 222 out string 223 }{ 224 {`echo "foo bar" > /dev/null`, "echo %22foo bar%22 %3E /dev/null"}, 225 } 226 227 for i, test := range tests { 228 e := EscapeXML.Replace(test.in) 229 if e != test.out { 230 t.Errorf("%d: %s != %s", i, e, test.out) 231 } 232 } 233 } 234 235 func TestProcessError(t *testing.T) { 236 fault := errors.New("fail") 237 var err error = &Error{Err: fault} 238 239 if err.Error() != fault.Error() { 240 t.Fatal() 241 } 242 } 243 244 func TestProcessIO(t *testing.T) { 245 checkGOOS(t) 246 247 m := NewManager() 248 249 r := &vix.StartProgramRequest{ 250 ProgramPath: "/bin/date", 251 } 252 253 p := New().WithIO() 254 255 _, err := m.Start(r, p) 256 if err != nil { 257 t.Fatal(err) 258 } 259 260 m.wg.Wait() 261 262 var buf bytes.Buffer 263 264 _, _ = io.Copy(&buf, p.IO.Out) 265 266 if buf.Len() == 0 { 267 t.Error("no data") 268 } 269 } 270 271 type testRoundTripper struct { 272 *Process 273 } 274 275 func (c *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { 276 req.Header.Set("Connection", "close") // we need the server to close the connection after 1 request 277 278 err := req.Write(c.IO.In.Writer) 279 if err != nil { 280 return nil, err 281 } 282 283 _ = c.IO.In.Close() 284 285 <-c.ctx.Done() 286 287 return http.ReadResponse(bufio.NewReader(c.IO.Out), req) 288 } 289 290 func TestProcessRoundTripper(t *testing.T) { 291 checkGOOS(t) 292 293 echo := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 294 _ = r.Write(w) 295 })) 296 297 u, _ := url.Parse(echo.URL) 298 299 m := NewManager() 300 301 r := &vix.StartProgramRequest{ 302 ProgramPath: "http.RoundTrip", 303 Arguments: u.Host, 304 } 305 306 p := NewRoundTrip() 307 308 _, err := m.Start(r, p) 309 if err != nil { 310 t.Fatal(err) 311 } 312 313 res, err := (&http.Client{Transport: &testRoundTripper{p}}).Get(echo.URL) 314 if err != nil { 315 t.Logf("Err: %s", p.IO.Err.String()) 316 t.Fatal(err) 317 } 318 319 if res.ContentLength == 0 { 320 t.Errorf("len=%d", res.ContentLength) 321 } 322 }