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