github.com/schwarzm/garden-linux@v0.0.0-20150507151835-33bca2147c47/linux_container/linux_container.go (about) 1 package linux_container 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "os" 8 "os/exec" 9 "path" 10 "path/filepath" 11 "strconv" 12 "strings" 13 "sync" 14 "time" 15 16 "github.com/cloudfoundry-incubator/garden" 17 "github.com/cloudfoundry-incubator/garden-linux/linux_backend" 18 "github.com/cloudfoundry-incubator/garden-linux/network" 19 "github.com/cloudfoundry-incubator/garden-linux/network/subnets" 20 "github.com/cloudfoundry-incubator/garden-linux/old/bandwidth_manager" 21 "github.com/cloudfoundry-incubator/garden-linux/old/cgroups_manager" 22 "github.com/cloudfoundry-incubator/garden-linux/old/logging" 23 "github.com/cloudfoundry-incubator/garden-linux/old/quota_manager" 24 "github.com/cloudfoundry-incubator/garden-linux/process" 25 "github.com/cloudfoundry-incubator/garden-linux/process_tracker" 26 "github.com/cloudfoundry/gunk/command_runner" 27 "github.com/pivotal-golang/lager" 28 ) 29 30 type UndefinedPropertyError struct { 31 Key string 32 } 33 34 func (err UndefinedPropertyError) Error() string { 35 return fmt.Sprintf("property does not exist: %s", err.Key) 36 } 37 38 type LinuxContainer struct { 39 logger lager.Logger 40 41 id string 42 handle string 43 path string 44 45 properties garden.Properties 46 propertiesMutex sync.RWMutex 47 48 graceTime time.Duration 49 50 state State 51 stateMutex sync.RWMutex 52 53 events []string 54 eventsMutex sync.RWMutex 55 56 resources *linux_backend.Resources 57 58 portPool PortPool 59 60 runner command_runner.CommandRunner 61 62 cgroupsManager cgroups_manager.CgroupsManager 63 quotaManager quota_manager.QuotaManager 64 bandwidthManager bandwidth_manager.BandwidthManager 65 66 processTracker process_tracker.ProcessTracker 67 68 filter network.Filter 69 70 oomMutex sync.RWMutex 71 oomNotifier *exec.Cmd 72 73 currentBandwidthLimits *garden.BandwidthLimits 74 bandwidthMutex sync.RWMutex 75 76 currentDiskLimits *garden.DiskLimits 77 diskMutex sync.RWMutex 78 79 currentMemoryLimits *garden.MemoryLimits 80 memoryMutex sync.RWMutex 81 82 currentCPULimits *garden.CPULimits 83 cpuMutex sync.RWMutex 84 85 netIns []NetInSpec 86 netInsMutex sync.RWMutex 87 88 netOuts []garden.NetOutRule 89 netOutsMutex sync.RWMutex 90 91 mtu uint32 92 93 env process.Env 94 95 processIDPool *ProcessIDPool 96 } 97 98 type ProcessIDPool struct { 99 currentProcessID uint32 100 mu sync.Mutex 101 } 102 103 func (p *ProcessIDPool) Next() uint32 { 104 p.mu.Lock() 105 defer p.mu.Unlock() 106 107 p.currentProcessID = p.currentProcessID + 1 108 return p.currentProcessID 109 } 110 111 func (p *ProcessIDPool) Restore(id uint32) { 112 p.mu.Lock() 113 defer p.mu.Unlock() 114 115 if id >= p.currentProcessID { 116 p.currentProcessID = id 117 } 118 } 119 120 type NetInSpec struct { 121 HostPort uint32 122 ContainerPort uint32 123 } 124 125 type PortPool interface { 126 Acquire() (uint32, error) 127 Remove(uint32) error 128 Release(uint32) 129 } 130 131 type State string 132 133 const ( 134 StateBorn = State("born") 135 StateActive = State("active") 136 StateStopped = State("stopped") 137 ) 138 139 func NewLinuxContainer( 140 logger lager.Logger, 141 id, handle, path string, 142 properties garden.Properties, 143 graceTime time.Duration, 144 resources *linux_backend.Resources, 145 portPool PortPool, 146 runner command_runner.CommandRunner, 147 cgroupsManager cgroups_manager.CgroupsManager, 148 quotaManager quota_manager.QuotaManager, 149 bandwidthManager bandwidth_manager.BandwidthManager, 150 processTracker process_tracker.ProcessTracker, 151 env process.Env, 152 filter network.Filter, 153 ) *LinuxContainer { 154 return &LinuxContainer{ 155 logger: logger, 156 157 id: id, 158 handle: handle, 159 path: path, 160 161 properties: properties, 162 163 graceTime: graceTime, 164 165 state: StateBorn, 166 events: []string{}, 167 168 resources: resources, 169 170 portPool: portPool, 171 172 runner: runner, 173 174 cgroupsManager: cgroupsManager, 175 quotaManager: quotaManager, 176 bandwidthManager: bandwidthManager, 177 178 processTracker: processTracker, 179 180 filter: filter, 181 182 env: env, 183 processIDPool: &ProcessIDPool{}, 184 } 185 } 186 187 func (c *LinuxContainer) ID() string { 188 return c.id 189 } 190 191 func (c *LinuxContainer) Handle() string { 192 return c.handle 193 } 194 195 func (c *LinuxContainer) GraceTime() time.Duration { 196 return c.graceTime 197 } 198 199 func (c *LinuxContainer) State() State { 200 c.stateMutex.RLock() 201 defer c.stateMutex.RUnlock() 202 203 return c.state 204 } 205 206 func (c *LinuxContainer) Events() []string { 207 c.eventsMutex.RLock() 208 defer c.eventsMutex.RUnlock() 209 210 events := make([]string, len(c.events)) 211 212 copy(events, c.events) 213 214 return events 215 } 216 217 func (c *LinuxContainer) Resources() *linux_backend.Resources { 218 return c.resources 219 } 220 221 func (c *LinuxContainer) Snapshot(out io.Writer) error { 222 cLog := c.logger.Session("snapshot") 223 224 cLog.Debug("saving") 225 226 c.bandwidthMutex.RLock() 227 defer c.bandwidthMutex.RUnlock() 228 229 c.cpuMutex.RLock() 230 defer c.cpuMutex.RUnlock() 231 232 c.diskMutex.RLock() 233 defer c.diskMutex.RUnlock() 234 235 c.memoryMutex.RLock() 236 defer c.memoryMutex.RUnlock() 237 238 c.netInsMutex.RLock() 239 defer c.netInsMutex.RUnlock() 240 241 c.netOutsMutex.RLock() 242 defer c.netOutsMutex.RUnlock() 243 244 processSnapshots := []ProcessSnapshot{} 245 246 for _, p := range c.processTracker.ActiveProcesses() { 247 processSnapshots = append( 248 processSnapshots, 249 ProcessSnapshot{ 250 ID: p.ID(), 251 }, 252 ) 253 } 254 255 properties, _ := c.Properties() 256 257 snapshot := ContainerSnapshot{ 258 ID: c.id, 259 Handle: c.handle, 260 261 GraceTime: c.graceTime, 262 263 State: string(c.State()), 264 Events: c.Events(), 265 266 Limits: LimitsSnapshot{ 267 Bandwidth: c.currentBandwidthLimits, 268 CPU: c.currentCPULimits, 269 Disk: c.currentDiskLimits, 270 Memory: c.currentMemoryLimits, 271 }, 272 273 Resources: ResourcesSnapshot{ 274 UserUID: c.resources.UserUID, 275 RootUID: c.resources.RootUID, 276 Network: c.resources.Network, 277 Bridge: c.resources.Bridge, 278 Ports: c.resources.Ports, 279 }, 280 281 NetIns: c.netIns, 282 NetOuts: c.netOuts, 283 284 Processes: processSnapshots, 285 286 Properties: properties, 287 288 EnvVars: c.env.Array(), 289 } 290 291 var err error 292 293 err = json.NewEncoder(out).Encode(snapshot) 294 if err != nil { 295 cLog.Error("failed-to-save", err, lager.Data{ 296 "snapshot": snapshot, 297 }) 298 return err 299 } 300 301 cLog.Info("saved", lager.Data{ 302 "snapshot": snapshot, 303 }) 304 305 return nil 306 } 307 308 func (c *LinuxContainer) Restore(snapshot ContainerSnapshot) error { 309 cLog := c.logger.Session("restore") 310 311 cLog.Debug("restoring") 312 313 cRunner := logging.Runner{ 314 CommandRunner: c.runner, 315 Logger: cLog, 316 } 317 318 c.setState(State(snapshot.State)) 319 320 snapshotEnv, err := process.NewEnv(snapshot.EnvVars) 321 if err != nil { 322 cLog.Error("restoring-env", err, lager.Data{ 323 "env": snapshot.EnvVars, 324 }) 325 return err 326 } 327 c.env = snapshotEnv 328 329 for _, ev := range snapshot.Events { 330 c.registerEvent(ev) 331 } 332 333 if snapshot.Limits.Memory != nil { 334 err := c.LimitMemory(*snapshot.Limits.Memory) 335 if err != nil { 336 cLog.Error("failed-to-limit-memory", err) 337 return err 338 } 339 } 340 341 for _, process := range snapshot.Processes { 342 cLog.Info("restoring-process", lager.Data{ 343 "process": process, 344 }) 345 346 c.processIDPool.Restore(process.ID) 347 348 pidfile := path.Join(c.path, "processes", fmt.Sprintf("%d.pid", process.ID)) 349 350 signaller := &linux_backend.NamespacedSignaller{ 351 Runner: c.runner, 352 ContainerPath: c.path, 353 PidFilePath: pidfile, 354 } 355 356 c.processTracker.Restore(process.ID, signaller) 357 } 358 359 net := exec.Command(path.Join(c.path, "net.sh"), "setup") 360 361 err = cRunner.Run(net) 362 if err != nil { 363 cLog.Error("failed-to-reenforce-network-rules", err) 364 return err 365 } 366 367 for _, in := range snapshot.NetIns { 368 _, _, err = c.NetIn(in.HostPort, in.ContainerPort) 369 if err != nil { 370 cLog.Error("failed-to-reenforce-port-mapping", err) 371 return err 372 } 373 } 374 375 for _, out := range snapshot.NetOuts { 376 if err := c.NetOut(out); err != nil { 377 cLog.Error("failed-to-reenforce-net-out", err) 378 return err 379 } 380 } 381 382 cLog.Info("restored") 383 384 return nil 385 } 386 387 func (c *LinuxContainer) Start() error { 388 cLog := c.logger.Session("start") 389 390 cLog.Debug("starting") 391 392 start := exec.Command(path.Join(c.path, "start.sh")) 393 start.Env = []string{ 394 "id=" + c.id, 395 "PATH=" + os.Getenv("PATH"), 396 } 397 398 cRunner := logging.Runner{ 399 CommandRunner: c.runner, 400 Logger: cLog, 401 } 402 403 err := cRunner.Run(start) 404 if err != nil { 405 cLog.Error("failed-to-start", err) 406 return fmt.Errorf("container: start: %v", err) 407 } 408 409 c.setState(StateActive) 410 411 cLog.Info("started") 412 413 return nil 414 } 415 416 func (c *LinuxContainer) Cleanup() { 417 cLog := c.logger.Session("cleanup") 418 419 cLog.Debug("stopping-oom-notifier") 420 c.stopOomNotifier() 421 422 cLog.Info("done") 423 } 424 425 func (c *LinuxContainer) Stop(kill bool) error { 426 stop := exec.Command(path.Join(c.path, "stop.sh")) 427 428 if kill { 429 stop.Args = append(stop.Args, "-w", "0") 430 } 431 432 err := c.runner.Run(stop) 433 if err != nil { 434 return err 435 } 436 437 c.stopOomNotifier() 438 439 c.setState(StateStopped) 440 441 return nil 442 } 443 444 func (c *LinuxContainer) Properties() (garden.Properties, error) { 445 c.propertiesMutex.RLock() 446 defer c.propertiesMutex.RUnlock() 447 448 return c.properties, nil 449 } 450 451 func (c *LinuxContainer) Property(key string) (string, error) { 452 c.propertiesMutex.RLock() 453 defer c.propertiesMutex.RUnlock() 454 455 value, found := c.properties[key] 456 if !found { 457 return "", UndefinedPropertyError{key} 458 } 459 460 return value, nil 461 } 462 463 func (c *LinuxContainer) SetProperty(key string, value string) error { 464 c.propertiesMutex.Lock() 465 defer c.propertiesMutex.Unlock() 466 467 props := garden.Properties{} 468 for k, v := range c.properties { 469 props[k] = v 470 } 471 472 props[key] = value 473 474 c.properties = props 475 476 return nil 477 } 478 479 func (c *LinuxContainer) RemoveProperty(key string) error { 480 c.propertiesMutex.Lock() 481 defer c.propertiesMutex.Unlock() 482 483 if _, found := c.properties[key]; !found { 484 return UndefinedPropertyError{key} 485 } 486 487 delete(c.properties, key) 488 489 return nil 490 } 491 492 func (c *LinuxContainer) HasProperties(properties garden.Properties) bool { 493 c.propertiesMutex.RLock() 494 defer c.propertiesMutex.RUnlock() 495 496 for k, v := range properties { 497 if value, ok := c.properties[k]; !ok || (ok && value != v) { 498 return false 499 } 500 } 501 502 return true 503 } 504 505 func (c *LinuxContainer) Info() (garden.ContainerInfo, error) { 506 mappedPorts := []garden.PortMapping{} 507 508 c.netInsMutex.RLock() 509 510 for _, spec := range c.netIns { 511 mappedPorts = append(mappedPorts, garden.PortMapping{ 512 HostPort: spec.HostPort, 513 ContainerPort: spec.ContainerPort, 514 }) 515 } 516 517 c.netInsMutex.RUnlock() 518 519 processIDs := []uint32{} 520 for _, process := range c.processTracker.ActiveProcesses() { 521 processIDs = append(processIDs, process.ID()) 522 } 523 524 properties, _ := c.Properties() 525 526 info := garden.ContainerInfo{ 527 State: string(c.State()), 528 Events: c.Events(), 529 Properties: properties, 530 ContainerPath: c.path, 531 ProcessIDs: processIDs, 532 MappedPorts: mappedPorts, 533 } 534 535 info.ContainerIP = c.resources.Network.IP.String() 536 info.HostIP = subnets.GatewayIP(c.resources.Network.Subnet).String() 537 info.ExternalIP = c.Resources().ExternalIP.String() 538 539 return info, nil 540 } 541 542 func (c *LinuxContainer) StreamIn(dstPath string, tarStream io.Reader) error { 543 nsTarPath := path.Join(c.path, "bin", "nstar") 544 pidPath := path.Join(c.path, "run", "wshd.pid") 545 546 pidFile, err := os.Open(pidPath) 547 if err != nil { 548 return err 549 } 550 551 var pid int 552 _, err = fmt.Fscanf(pidFile, "%d", &pid) 553 if err != nil { 554 return err 555 } 556 557 tar := exec.Command( 558 nsTarPath, 559 strconv.Itoa(pid), 560 "vcap", 561 dstPath, 562 ) 563 564 tar.Stdin = tarStream 565 566 cLog := c.logger.Session("stream-in") 567 568 cRunner := logging.Runner{ 569 CommandRunner: c.runner, 570 Logger: cLog, 571 } 572 573 return cRunner.Run(tar) 574 } 575 576 func (c *LinuxContainer) StreamOut(srcPath string) (io.ReadCloser, error) { 577 workingDir := filepath.Dir(srcPath) 578 compressArg := filepath.Base(srcPath) 579 if strings.HasSuffix(srcPath, "/") { 580 workingDir = srcPath 581 compressArg = "." 582 } 583 584 nsTarPath := path.Join(c.path, "bin", "nstar") 585 pidPath := path.Join(c.path, "run", "wshd.pid") 586 587 pidFile, err := os.Open(pidPath) 588 if err != nil { 589 return nil, err 590 } 591 592 var pid int 593 _, err = fmt.Fscanf(pidFile, "%d", &pid) 594 if err != nil { 595 return nil, err 596 } 597 598 tar := exec.Command( 599 nsTarPath, 600 strconv.Itoa(pid), 601 "vcap", 602 workingDir, 603 compressArg, 604 ) 605 606 tarRead, tarWrite, err := os.Pipe() 607 if err != nil { 608 return nil, err 609 } 610 611 tar.Stdout = tarWrite 612 613 err = c.runner.Background(tar) 614 if err != nil { 615 return nil, err 616 } 617 618 // close our end of the tar pipe 619 tarWrite.Close() 620 621 go c.runner.Wait(tar) 622 623 return tarRead, nil 624 } 625 626 func (c *LinuxContainer) NetIn(hostPort uint32, containerPort uint32) (uint32, uint32, error) { 627 if hostPort == 0 { 628 randomPort, err := c.portPool.Acquire() 629 if err != nil { 630 return 0, 0, err 631 } 632 633 c.resources.AddPort(randomPort) 634 635 hostPort = randomPort 636 } 637 638 if containerPort == 0 { 639 containerPort = hostPort 640 } 641 642 net := exec.Command(path.Join(c.path, "net.sh"), "in") 643 net.Env = []string{ 644 fmt.Sprintf("HOST_PORT=%d", hostPort), 645 fmt.Sprintf("CONTAINER_PORT=%d", containerPort), 646 "PATH=" + os.Getenv("PATH"), 647 } 648 649 err := c.runner.Run(net) 650 if err != nil { 651 return 0, 0, err 652 } 653 654 c.netInsMutex.Lock() 655 defer c.netInsMutex.Unlock() 656 657 c.netIns = append(c.netIns, NetInSpec{hostPort, containerPort}) 658 659 return hostPort, containerPort, nil 660 } 661 662 func (c *LinuxContainer) NetOut(r garden.NetOutRule) error { 663 err := c.filter.NetOut(r) 664 if err != nil { 665 return err 666 } 667 668 c.netOutsMutex.Lock() 669 defer c.netOutsMutex.Unlock() 670 671 c.netOuts = append(c.netOuts, r) 672 673 return nil 674 } 675 676 func (c *LinuxContainer) CurrentEnvVars() process.Env { 677 return c.env 678 } 679 680 func (c *LinuxContainer) setState(state State) { 681 c.stateMutex.Lock() 682 defer c.stateMutex.Unlock() 683 684 c.state = state 685 } 686 687 func (c *LinuxContainer) registerEvent(event string) { 688 c.eventsMutex.Lock() 689 defer c.eventsMutex.Unlock() 690 691 c.events = append(c.events, event) 692 }