github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/worker/gclient/connection/connection.go (about) 1 package connection 2 3 import ( 4 "bufio" 5 "bytes" 6 "context" 7 "encoding/json" 8 "errors" 9 "fmt" 10 "io" 11 "net" 12 "net/url" 13 "strings" 14 "time" 15 16 "code.cloudfoundry.org/garden" 17 "code.cloudfoundry.org/garden/routes" 18 "code.cloudfoundry.org/garden/transport" 19 "code.cloudfoundry.org/lager" 20 "github.com/tedsuo/rata" 21 ) 22 23 var ErrDisconnected = errors.New("disconnected") 24 var ErrInvalidMessage = errors.New("invalid message payload") 25 26 //go:generate counterfeiter . Connection 27 type Connection interface { 28 Ping() error 29 30 Capacity() (garden.Capacity, error) 31 32 Create(spec garden.ContainerSpec) (string, error) 33 List(properties garden.Properties) ([]string, error) 34 35 // Destroys the container with the given handle. If the container cannot be 36 // found, garden.ContainerNotFoundError is returned. If deletion fails for another 37 // reason, another error type is returned. 38 Destroy(handle string) error 39 40 Stop(handle string, kill bool) error 41 42 Info(handle string) (garden.ContainerInfo, error) 43 BulkInfo(handles []string) (map[string]garden.ContainerInfoEntry, error) 44 BulkMetrics(handles []string) (map[string]garden.ContainerMetricsEntry, error) 45 46 StreamIn(handle string, spec garden.StreamInSpec) error 47 StreamOut(handle string, spec garden.StreamOutSpec) (io.ReadCloser, error) 48 49 CurrentBandwidthLimits(handle string) (garden.BandwidthLimits, error) 50 CurrentCPULimits(handle string) (garden.CPULimits, error) 51 CurrentDiskLimits(handle string) (garden.DiskLimits, error) 52 CurrentMemoryLimits(handle string) (garden.MemoryLimits, error) 53 54 // NOTE: Contexts are passed in to Run/Attach as they call WorkerHijackStreamer.Hijack() 55 // Hijack() spawns multiple dedicated connections that need to get cleaned up in error 56 // scenarios. We use the cancelFunc associated to the context in order to close connections 57 Run(ctx context.Context, handle string, spec garden.ProcessSpec, io garden.ProcessIO) (garden.Process, error) 58 Attach(ctx context.Context, handle string, processID string, io garden.ProcessIO) (garden.Process, error) 59 60 NetIn(handle string, hostPort, containerPort uint32) (uint32, uint32, error) 61 NetOut(handle string, rule garden.NetOutRule) error 62 BulkNetOut(handle string, rules []garden.NetOutRule) error 63 64 SetGraceTime(handle string, graceTime time.Duration) error 65 66 Properties(handle string) (garden.Properties, error) 67 Property(handle string, name string) (string, error) 68 SetProperty(handle string, name string, value string) error 69 70 Metrics(handle string) (garden.Metrics, error) 71 RemoveProperty(handle string, name string) error 72 } 73 74 //go:generate counterfeiter . HijackStreamer 75 type HijackStreamer interface { 76 Stream(handler string, body io.Reader, params rata.Params, query url.Values, contentType string) (io.ReadCloser, error) 77 Hijack(ctx context.Context, handler string, body io.Reader, params rata.Params, query url.Values, contentType string) (net.Conn, *bufio.Reader, error) 78 } 79 80 type connection struct { 81 hijacker HijackStreamer 82 log lager.Logger 83 } 84 85 type Error struct { 86 StatusCode int 87 Message string 88 } 89 90 func (err Error) Error() string { 91 return err.Message 92 } 93 94 func NewWithHijacker(hijacker HijackStreamer, log lager.Logger) Connection { 95 return &connection{ 96 hijacker: hijacker, 97 log: log, 98 } 99 } 100 101 func (c *connection) Ping() error { 102 return c.do(routes.Ping, nil, &struct{}{}, nil, nil) 103 } 104 105 func (c *connection) Capacity() (garden.Capacity, error) { 106 capacity := garden.Capacity{} 107 err := c.do(routes.Capacity, nil, &capacity, nil, nil) 108 if err != nil { 109 return garden.Capacity{}, err 110 } 111 112 return capacity, nil 113 } 114 115 func (c *connection) Create(spec garden.ContainerSpec) (string, error) { 116 res := struct { 117 Handle string `json:"handle"` 118 }{} 119 120 err := c.do(routes.Create, spec, &res, nil, nil) 121 if err != nil { 122 return "", err 123 } 124 125 return res.Handle, nil 126 } 127 128 func (c *connection) Stop(handle string, kill bool) error { 129 return c.do( 130 routes.Stop, 131 map[string]bool{ 132 "kill": kill, 133 }, 134 &struct{}{}, 135 rata.Params{ 136 "handle": handle, 137 }, 138 nil, 139 ) 140 } 141 142 func (c *connection) Destroy(handle string) error { 143 return c.do( 144 routes.Destroy, 145 nil, 146 &struct{}{}, 147 rata.Params{ 148 "handle": handle, 149 }, 150 nil, 151 ) 152 } 153 154 func (c *connection) Run(ctx context.Context, handle string, spec garden.ProcessSpec, processIO garden.ProcessIO) (garden.Process, error) { 155 reqBody := new(bytes.Buffer) 156 157 err := transport.WriteMessage(reqBody, spec) 158 if err != nil { 159 return nil, err 160 } 161 162 hijackedConn, hijackedResponseReader, err := c.hijacker.Hijack( 163 ctx, 164 routes.Run, 165 reqBody, 166 rata.Params{ 167 "handle": handle, 168 }, 169 nil, 170 "application/json", 171 ) 172 if err != nil { 173 return nil, err 174 } 175 176 return c.streamProcess(ctx, handle, processIO, hijackedConn, hijackedResponseReader) 177 } 178 179 func (c *connection) Attach(ctx context.Context, handle string, processID string, processIO garden.ProcessIO) (garden.Process, error) { 180 reqBody := new(bytes.Buffer) 181 182 hijackedConn, hijackedResponseReader, err := c.hijacker.Hijack( 183 ctx, 184 routes.Attach, 185 reqBody, 186 rata.Params{ 187 "handle": handle, 188 "pid": processID, 189 }, 190 nil, 191 "", 192 ) 193 if err != nil { 194 return nil, err 195 } 196 197 return c.streamProcess(ctx, handle, processIO, hijackedConn, hijackedResponseReader) 198 } 199 200 func (c *connection) streamProcess(ctx context.Context, handle string, processIO garden.ProcessIO, hijackedConn net.Conn, hijackedResponseReader *bufio.Reader) (garden.Process, error) { 201 decoder := json.NewDecoder(hijackedResponseReader) 202 203 payload := &transport.ProcessPayload{} 204 if err := decoder.Decode(payload); err != nil { 205 return nil, err 206 } 207 208 processPipeline := &processStream{ 209 processID: payload.ProcessID, 210 conn: hijackedConn, 211 } 212 213 hijack := func(streamType string) (net.Conn, io.Reader, error) { 214 params := rata.Params{ 215 "handle": handle, 216 "pid": processPipeline.ProcessID(), 217 "streamid": payload.StreamID, 218 } 219 220 return c.hijacker.Hijack( 221 ctx, 222 streamType, 223 nil, 224 params, 225 nil, 226 "application/json", 227 ) 228 } 229 230 process := newProcess(payload.ProcessID, processPipeline) 231 streamHandler := newStreamHandler(c.log) 232 streamHandler.streamIn(processPipeline, processIO.Stdin) 233 234 var stdoutConn net.Conn 235 if processIO.Stdout != nil { 236 var ( 237 stdout io.Reader 238 err error 239 ) 240 stdoutConn, stdout, err = hijack(routes.Stdout) 241 if err != nil { 242 werr := fmt.Errorf("connection: failed to hijack stream %s: %s", routes.Stdout, err) 243 process.exited(0, werr) 244 hijackedConn.Close() 245 return process, nil 246 } 247 streamHandler.streamOut(processIO.Stdout, stdout) 248 } 249 250 var stderrConn net.Conn 251 if processIO.Stderr != nil { 252 var ( 253 stderr io.Reader 254 err error 255 ) 256 stderrConn, stderr, err = hijack(routes.Stderr) 257 if err != nil { 258 werr := fmt.Errorf("connection: failed to hijack stream %s: %s", routes.Stderr, err) 259 process.exited(0, werr) 260 hijackedConn.Close() 261 return process, nil 262 } 263 streamHandler.streamOut(processIO.Stderr, stderr) 264 } 265 266 go func() { 267 defer hijackedConn.Close() 268 if stdoutConn != nil { 269 defer stdoutConn.Close() 270 } 271 if stderrConn != nil { 272 defer stderrConn.Close() 273 } 274 275 select { 276 case <-ctx.Done(): 277 process.exited(-1, fmt.Errorf("stdin/stdout/stderr streams were canceled by: %w", ctx.Err())) 278 case waitedFor := <-streamHandler.wait(decoder): 279 process.exited(waitedFor.exitCode, waitedFor.err) 280 } 281 282 }() 283 284 return process, nil 285 } 286 287 func (c *connection) NetIn(handle string, hostPort, containerPort uint32) (uint32, uint32, error) { 288 res := &transport.NetInResponse{} 289 290 err := c.do( 291 routes.NetIn, 292 &transport.NetInRequest{ 293 Handle: handle, 294 HostPort: hostPort, 295 ContainerPort: containerPort, 296 }, 297 res, 298 rata.Params{ 299 "handle": handle, 300 }, 301 nil, 302 ) 303 304 if err != nil { 305 return 0, 0, err 306 } 307 308 return res.HostPort, res.ContainerPort, nil 309 } 310 311 func (c *connection) BulkNetOut(handle string, rules []garden.NetOutRule) error { 312 return c.do( 313 routes.BulkNetOut, 314 rules, 315 &struct{}{}, 316 rata.Params{ 317 "handle": handle, 318 }, 319 nil, 320 ) 321 } 322 323 func (c *connection) NetOut(handle string, rule garden.NetOutRule) error { 324 return c.do( 325 routes.NetOut, 326 rule, 327 &struct{}{}, 328 rata.Params{ 329 "handle": handle, 330 }, 331 nil, 332 ) 333 } 334 335 func (c *connection) Property(handle string, name string) (string, error) { 336 var res struct { 337 Value string `json:"value"` 338 } 339 340 err := c.do( 341 routes.Property, 342 nil, 343 &res, 344 rata.Params{ 345 "handle": handle, 346 "key": name, 347 }, 348 nil, 349 ) 350 351 return res.Value, err 352 } 353 354 func (c *connection) SetProperty(handle string, name string, value string) error { 355 err := c.do( 356 routes.SetProperty, 357 map[string]string{ 358 "value": value, 359 }, 360 &struct{}{}, 361 rata.Params{ 362 "handle": handle, 363 "key": name, 364 }, 365 nil, 366 ) 367 368 if err != nil { 369 return err 370 } 371 372 return nil 373 } 374 375 func (c *connection) RemoveProperty(handle string, name string) error { 376 err := c.do( 377 routes.RemoveProperty, 378 nil, 379 &struct{}{}, 380 rata.Params{ 381 "handle": handle, 382 "key": name, 383 }, 384 nil, 385 ) 386 387 if err != nil { 388 return err 389 } 390 391 return nil 392 } 393 394 func (c *connection) CurrentBandwidthLimits(handle string) (garden.BandwidthLimits, error) { 395 res := garden.BandwidthLimits{} 396 397 err := c.do( 398 routes.CurrentBandwidthLimits, 399 nil, 400 &res, 401 rata.Params{ 402 "handle": handle, 403 }, 404 nil, 405 ) 406 407 return res, err 408 } 409 410 func (c *connection) CurrentCPULimits(handle string) (garden.CPULimits, error) { 411 res := garden.CPULimits{} 412 413 err := c.do( 414 routes.CurrentCPULimits, 415 nil, 416 &res, 417 rata.Params{ 418 "handle": handle, 419 }, 420 nil, 421 ) 422 423 return res, err 424 } 425 426 func (c *connection) CurrentDiskLimits(handle string) (garden.DiskLimits, error) { 427 res := garden.DiskLimits{} 428 429 err := c.do( 430 routes.CurrentDiskLimits, 431 nil, 432 &res, 433 rata.Params{ 434 "handle": handle, 435 }, 436 nil, 437 ) 438 439 return res, err 440 } 441 442 func (c *connection) CurrentMemoryLimits(handle string) (garden.MemoryLimits, error) { 443 res := garden.MemoryLimits{} 444 445 err := c.do( 446 routes.CurrentMemoryLimits, 447 nil, 448 &res, 449 rata.Params{ 450 "handle": handle, 451 }, 452 nil, 453 ) 454 455 return res, err 456 } 457 458 func (c *connection) StreamIn(handle string, spec garden.StreamInSpec) error { 459 body, err := c.hijacker.Stream( 460 routes.StreamIn, 461 spec.TarStream, 462 rata.Params{ 463 "handle": handle, 464 }, 465 url.Values{ 466 "user": []string{spec.User}, 467 "destination": []string{spec.Path}, 468 }, 469 "application/x-tar", 470 ) 471 if err != nil { 472 return err 473 } 474 475 return body.Close() 476 } 477 478 func (c *connection) StreamOut(handle string, spec garden.StreamOutSpec) (io.ReadCloser, error) { 479 return c.hijacker.Stream( 480 routes.StreamOut, 481 nil, 482 rata.Params{ 483 "handle": handle, 484 }, 485 url.Values{ 486 "user": []string{spec.User}, 487 "source": []string{spec.Path}, 488 }, 489 "", 490 ) 491 } 492 493 func (c *connection) List(filterProperties garden.Properties) ([]string, error) { 494 values := url.Values{} 495 for name, val := range filterProperties { 496 values[name] = []string{val} 497 } 498 499 res := &struct { 500 Handles []string 501 }{} 502 503 if err := c.do( 504 routes.List, 505 nil, 506 &res, 507 nil, 508 values, 509 ); err != nil { 510 return nil, err 511 } 512 513 return res.Handles, nil 514 } 515 516 func (c *connection) SetGraceTime(handle string, graceTime time.Duration) error { 517 return c.do(routes.SetGraceTime, graceTime, &struct{}{}, rata.Params{"handle": handle}, nil) 518 } 519 520 func (c *connection) Properties(handle string) (garden.Properties, error) { 521 res := make(garden.Properties) 522 err := c.do(routes.Properties, nil, &res, rata.Params{"handle": handle}, nil) 523 return res, err 524 } 525 526 func (c *connection) Metrics(handle string) (garden.Metrics, error) { 527 res := garden.Metrics{} 528 err := c.do(routes.Metrics, nil, &res, rata.Params{"handle": handle}, nil) 529 return res, err 530 } 531 532 func (c *connection) Info(handle string) (garden.ContainerInfo, error) { 533 res := garden.ContainerInfo{} 534 535 err := c.do(routes.Info, nil, &res, rata.Params{"handle": handle}, nil) 536 if err != nil { 537 return garden.ContainerInfo{}, err 538 } 539 540 return res, nil 541 } 542 543 func (c *connection) BulkInfo(handles []string) (map[string]garden.ContainerInfoEntry, error) { 544 res := make(map[string]garden.ContainerInfoEntry) 545 queryParams := url.Values{ 546 "handles": []string{strings.Join(handles, ",")}, 547 } 548 err := c.do(routes.BulkInfo, nil, &res, nil, queryParams) 549 return res, err 550 } 551 552 func (c *connection) BulkMetrics(handles []string) (map[string]garden.ContainerMetricsEntry, error) { 553 res := make(map[string]garden.ContainerMetricsEntry) 554 queryParams := url.Values{ 555 "handles": []string{strings.Join(handles, ",")}, 556 } 557 err := c.do(routes.BulkMetrics, nil, &res, nil, queryParams) 558 return res, err 559 } 560 561 func (c *connection) do( 562 handler string, 563 req, res interface{}, 564 params rata.Params, 565 query url.Values, 566 ) error { 567 var body io.Reader 568 569 if req != nil { 570 buf := new(bytes.Buffer) 571 572 err := transport.WriteMessage(buf, req) 573 if err != nil { 574 return err 575 } 576 577 body = buf 578 } 579 580 contentType := "" 581 if req != nil { 582 contentType = "application/json" 583 } 584 585 response, err := c.hijacker.Stream( 586 handler, 587 body, 588 params, 589 query, 590 contentType, 591 ) 592 if err != nil { 593 return err 594 } 595 596 defer response.Close() 597 598 return json.NewDecoder(response).Decode(res) 599 }