github.com/geofffranks/garden-linux@v0.0.0-20160715111146-26c893169cfa/resource_pool/resource_pool.go (about) 1 package resource_pool 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "net" 10 "net/url" 11 "os" 12 "os/exec" 13 "path" 14 "path/filepath" 15 "strconv" 16 "strings" 17 "time" 18 19 "github.com/blang/semver" 20 "code.cloudfoundry.org/garden" 21 "github.com/cloudfoundry/gunk/command_runner" 22 "code.cloudfoundry.org/lager" 23 24 "code.cloudfoundry.org/garden-linux/linux_backend" 25 "code.cloudfoundry.org/garden-linux/linux_container" 26 "code.cloudfoundry.org/garden-linux/logging" 27 "code.cloudfoundry.org/garden-linux/network" 28 "code.cloudfoundry.org/garden-linux/network/bridgemgr" 29 "code.cloudfoundry.org/garden-linux/network/iptables" 30 "code.cloudfoundry.org/garden-linux/network/subnets" 31 "code.cloudfoundry.org/garden-linux/process" 32 "code.cloudfoundry.org/garden-linux/sysconfig" 33 "code.cloudfoundry.org/garden-shed/layercake" 34 "code.cloudfoundry.org/garden-shed/rootfs_provider" 35 ) 36 37 var ( 38 ErrUnknownRootFSProvider = errors.New("unknown rootfs provider") 39 ) 40 41 //go:generate counterfeiter -o fake_filter_provider/FakeFilterProvider.go . FilterProvider 42 type FilterProvider interface { 43 ProvideFilter(containerId string) network.Filter 44 } 45 46 //go:generate counterfeiter -o fake_subnet_pool/FakeSubnetPool.go . SubnetPool 47 type SubnetPool interface { 48 Acquire(subnet subnets.SubnetSelector, ip subnets.IPSelector, logger lager.Logger) (*linux_backend.Network, error) 49 Release(network *linux_backend.Network, logger lager.Logger) error 50 Remove(network *linux_backend.Network, logger lager.Logger) error 51 Capacity() int 52 } 53 54 //go:generate counterfeiter -o fake_rootfs_provider/FakeRootFSProvider.go . RootFSProvider 55 type RootFSProvider interface { 56 Create(log lager.Logger, id string, spec rootfs_provider.Spec) (mountpoint string, envvar []string, err error) 57 Destroy(log lager.Logger, id string) error 58 GC(log lager.Logger) error 59 } 60 61 //go:generate counterfeiter -o fake_rootfs_cleaner/FakeRootFSCleaner.go . RootFSCleaner 62 type RootFSCleaner interface { 63 Clean(log lager.Logger, path string) error 64 } 65 66 type Remover interface { 67 Remove(id layercake.ID) error 68 } 69 70 //go:generate counterfeiter -o fake_mkdir_chowner/FakeMkdirChowner.go . MkdirChowner 71 type MkdirChowner interface { 72 MkdirChown(path string, uid, gid uint32, mode os.FileMode) error 73 } 74 75 type LinuxResourcePool struct { 76 logger lager.Logger 77 78 binPath string 79 depotPath string 80 81 sysconfig sysconfig.Config 82 83 denyNetworks []string 84 allowNetworks []string 85 86 rootFSProvider RootFSProvider 87 rootFSCleaner RootFSCleaner 88 mappingList rootfs_provider.MappingList 89 90 subnetPool SubnetPool 91 92 externalIP net.IP 93 mtu int 94 95 portPool linux_container.PortPool 96 97 bridges bridgemgr.BridgeManager 98 iptablesMgr linux_container.IPTablesManager 99 100 filterProvider FilterProvider 101 defaultChain iptables.Chain 102 103 runner command_runner.CommandRunner 104 105 quotaManager linux_container.QuotaManager 106 107 containerIDs chan string 108 109 currentContainerVersion semver.Version 110 111 mkdirChowner MkdirChowner 112 } 113 114 func New( 115 logger lager.Logger, 116 binPath, depotPath string, 117 sysconfig sysconfig.Config, 118 rootFSProvider RootFSProvider, 119 rootFSCleaner RootFSCleaner, 120 mappingList rootfs_provider.MappingList, 121 externalIP net.IP, 122 mtu int, 123 subnetPool SubnetPool, 124 bridges bridgemgr.BridgeManager, 125 iptablesMgr linux_container.IPTablesManager, 126 filterProvider FilterProvider, 127 defaultChain iptables.Chain, 128 portPool linux_container.PortPool, 129 denyNetworks, allowNetworks []string, 130 runner command_runner.CommandRunner, 131 quotaManager linux_container.QuotaManager, 132 currentContainerVersion semver.Version, 133 mkdirChowner MkdirChowner, 134 ) *LinuxResourcePool { 135 pool := &LinuxResourcePool{ 136 logger: logger.Session("pool"), 137 138 binPath: binPath, 139 depotPath: depotPath, 140 141 sysconfig: sysconfig, 142 143 rootFSProvider: rootFSProvider, 144 rootFSCleaner: rootFSCleaner, 145 mappingList: mappingList, 146 147 allowNetworks: allowNetworks, 148 denyNetworks: denyNetworks, 149 150 externalIP: externalIP, 151 mtu: mtu, 152 153 subnetPool: subnetPool, 154 155 bridges: bridges, 156 iptablesMgr: iptablesMgr, 157 158 filterProvider: filterProvider, 159 defaultChain: defaultChain, 160 161 portPool: portPool, 162 163 runner: runner, 164 165 quotaManager: quotaManager, 166 167 containerIDs: make(chan string), 168 currentContainerVersion: currentContainerVersion, 169 170 mkdirChowner: mkdirChowner, 171 } 172 173 go pool.generateContainerIDs() 174 175 return pool 176 } 177 178 func (p *LinuxResourcePool) MaxContainers() int { 179 return p.subnetPool.Capacity() 180 } 181 182 func (p *LinuxResourcePool) Setup() error { 183 setup := exec.Command(path.Join(p.binPath, "setup.sh")) 184 setup.Env = []string{ 185 "CONTAINER_DEPOT_PATH=" + p.depotPath, 186 "PATH=" + os.Getenv("PATH"), 187 } 188 189 err := p.runner.Run(setup) 190 if err != nil { 191 return err 192 } 193 194 if err := p.quotaManager.Setup(); err != nil { 195 return fmt.Errorf("resource_pool: enable disk quotas: %s", err) 196 } 197 198 return p.setupIPTables() 199 } 200 201 func (p *LinuxResourcePool) setupIPTables() error { 202 for _, n := range p.allowNetworks { 203 if n == "" { 204 continue 205 } 206 207 if err := p.defaultChain.AppendRule("", n, iptables.Return); err != nil { 208 return fmt.Errorf("resource_pool: setting up allow rules in iptables: %v", err) 209 } 210 } 211 212 for _, n := range p.denyNetworks { 213 if n == "" { 214 continue 215 } 216 217 if err := p.defaultChain.AppendRule("", n, iptables.Reject); err != nil { 218 return fmt.Errorf("resource_pool: setting up deny rules in iptables: %v", err) 219 } 220 } 221 222 return nil 223 } 224 225 func (p *LinuxResourcePool) Prune(keep map[string]bool) error { 226 entries, err := ioutil.ReadDir(p.depotPath) 227 if err != nil { 228 p.logger.Error("prune-container-pool-path-error", err, lager.Data{"depotPath": p.depotPath}) 229 return fmt.Errorf("Cannot read path %q: %s", p.depotPath, err) 230 } 231 232 for _, entry := range entries { 233 id := entry.Name() 234 if id == "tmp" { // ignore temporary directory in depotPath 235 continue 236 } 237 238 _, found := keep[id] 239 if found { 240 continue 241 } 242 243 p.pruneEntry(id) 244 } 245 246 if err := p.bridges.Prune(); err != nil { 247 p.logger.Error("prune-bridges", err) 248 } 249 250 return nil 251 } 252 253 // pruneEntry does not report errors, only log them 254 func (p *LinuxResourcePool) pruneEntry(id string) { 255 pLog := p.logger.Session("prune", lager.Data{"id": id}) 256 257 pLog.Info("prune") 258 259 err := p.releaseSystemResources(pLog, id) 260 if err != nil { 261 pLog.Error("release-system-resources-error", err) 262 } 263 264 pLog.Info("end of prune") 265 } 266 267 func (p *LinuxResourcePool) Acquire(spec garden.ContainerSpec) (linux_backend.LinuxContainerSpec, error) { 268 id := <-p.containerIDs 269 containerPath := path.Join(p.depotPath, id) 270 handle := getHandle(spec.Handle, id) 271 pLog := p.logger.Session("acquire", lager.Data{"handle": handle, "id": id}) 272 273 iptablesCh := make(chan error, 1) 274 275 go func(iptablesCh chan error) { 276 pLog.Debug("setup-iptables-starting") 277 if err := p.filterProvider.ProvideFilter(id).Setup(handle); err != nil { 278 pLog.Error("setup-iptables-failed", err) 279 iptablesCh <- fmt.Errorf("resource_pool: set up filter: %v", err) 280 } else { 281 pLog.Debug("setup-iptables-ended") 282 iptablesCh <- nil 283 } 284 }(iptablesCh) 285 286 pLog.Info("creating") 287 288 resources, err := p.acquirePoolResources(spec, id, pLog) 289 if err != nil { 290 return linux_backend.LinuxContainerSpec{}, err 291 } 292 defer cleanup(&err, func() { 293 p.releasePoolResources(resources, pLog) 294 }) 295 296 pLog.Info("acquired-pool-resources") 297 298 pLog.Info("running-graph-cleanup") 299 if err := p.rootFSProvider.GC(pLog); err != nil { 300 pLog.Error("graph-cleanup-failed", err) 301 } 302 303 containerRootFSPath, rootFSEnv, err := p.acquireSystemResources( 304 spec, id, resources, pLog, 305 ) 306 if err != nil { 307 return linux_backend.LinuxContainerSpec{}, err 308 } 309 310 err = <-iptablesCh 311 if err != nil { 312 p.tryReleaseSystemResources(p.logger, id) 313 return linux_backend.LinuxContainerSpec{}, err 314 } 315 316 pLog.Info("created") 317 318 specEnv, err := process.NewEnv(spec.Env) 319 if err != nil { 320 p.tryReleaseSystemResources(p.logger, id) 321 return linux_backend.LinuxContainerSpec{}, err 322 } 323 324 spec.Env = rootFSEnv.Merge(specEnv).Array() 325 spec.Handle = handle 326 327 return linux_backend.LinuxContainerSpec{ 328 ID: id, 329 ContainerPath: containerPath, 330 ContainerRootFSPath: containerRootFSPath, 331 Resources: resources, 332 Events: []string{}, 333 Version: p.currentContainerVersion, 334 State: linux_backend.StateBorn, 335 336 ContainerSpec: spec, 337 }, nil 338 } 339 340 func (p *LinuxResourcePool) Restore(snapshot io.Reader) (linux_backend.LinuxContainerSpec, error) { 341 var containerSnapshot linux_container.ContainerSnapshot 342 343 err := json.NewDecoder(snapshot).Decode(&containerSnapshot) 344 if err != nil { 345 return linux_backend.LinuxContainerSpec{}, err 346 } 347 348 id := containerSnapshot.ID 349 rLog := p.logger.Session("restore", lager.Data{ 350 "handle": containerSnapshot.Handle, 351 "id": id, 352 }) 353 354 rLog.Debug("restoring") 355 356 resources := containerSnapshot.Resources 357 subnetLogger := rLog.Session("subnet-pool") 358 359 if err = p.subnetPool.Remove(resources.Network, subnetLogger); err != nil { 360 return linux_backend.LinuxContainerSpec{}, err 361 } 362 363 if err = p.bridges.Rereserve(resources.Bridge, resources.Network.Subnet, id); err != nil { 364 p.subnetPool.Release(resources.Network, subnetLogger) 365 return linux_backend.LinuxContainerSpec{}, err 366 } 367 368 for _, port := range resources.Ports { 369 err = p.portPool.Remove(port) 370 if err != nil { 371 p.subnetPool.Release(resources.Network, subnetLogger) 372 373 for _, port := range resources.Ports { 374 p.portPool.Release(port) 375 } 376 377 return linux_backend.LinuxContainerSpec{}, err 378 } 379 } 380 381 version, err := p.restoreContainerVersion(id) 382 if err != nil { 383 return linux_backend.LinuxContainerSpec{}, err 384 } 385 386 spec := linux_backend.LinuxContainerSpec{ 387 ID: id, 388 ContainerPath: path.Join(p.depotPath, id), 389 ContainerRootFSPath: containerSnapshot.RootFSPath, 390 391 State: linux_backend.State(containerSnapshot.State), 392 Events: containerSnapshot.Events, 393 ContainerSpec: garden.ContainerSpec{ 394 Handle: containerSnapshot.Handle, 395 GraceTime: containerSnapshot.GraceTime, 396 Properties: containerSnapshot.Properties, 397 }, 398 399 Resources: linux_backend.NewResources( 400 resources.RootUID, 401 resources.Network, 402 resources.Bridge, 403 resources.Ports, 404 p.externalIP, 405 ), 406 407 Limits: containerSnapshot.Limits, 408 NetIns: containerSnapshot.NetIns, 409 NetOuts: containerSnapshot.NetOuts, 410 Processes: containerSnapshot.Processes, 411 Version: version, 412 } 413 414 return spec, nil 415 } 416 417 func (p *LinuxResourcePool) Release(container linux_backend.LinuxContainerSpec) error { 418 pLog := p.logger.Session("release", lager.Data{ 419 "handle": container.Handle, 420 "id": container.ID, 421 }) 422 423 pLog.Info("releasing") 424 425 err := p.releaseSystemResources(pLog, container.ID) 426 if err != nil { 427 pLog.Error("release-system-resources", err) 428 return err 429 } 430 431 p.releasePoolResources(container.Resources, pLog) 432 433 pLog.Info("released") 434 435 return nil 436 } 437 438 func (p *LinuxResourcePool) generateContainerIDs() { 439 for containerNum := time.Now().UnixNano(); ; containerNum++ { 440 containerID := []byte{} 441 442 var i uint 443 for i = 0; i < 11; i++ { 444 containerID = strconv.AppendInt( 445 containerID, 446 (containerNum>>(55-(i+1)*5))&31, 447 32, 448 ) 449 } 450 451 p.containerIDs <- string(containerID) 452 } 453 } 454 455 func (p *LinuxResourcePool) writeBindMounts(containerPath string, 456 rootFSPath string, 457 bindMounts []garden.BindMount, 458 mkdirUID int) error { 459 hook := path.Join(containerPath, "lib", "hook-parent-before-clone.sh") 460 461 for _, bm := range bindMounts { 462 dstMount := path.Join(rootFSPath, bm.DstPath) 463 srcPath := bm.SrcPath 464 465 if bm.Origin == garden.BindMountOriginContainer { 466 srcPath = path.Join(rootFSPath, srcPath) 467 } 468 469 mode := "ro" 470 if bm.Mode == garden.BindMountModeRW { 471 mode = "rw" 472 } 473 474 linebreak := exec.Command("bash", "-c", "echo >> "+hook) 475 if err := p.runner.Run(linebreak); err != nil { 476 return err 477 } 478 479 if err := p.mkdirChowner.MkdirChown(dstMount, uint32(mkdirUID), uint32(mkdirUID), 0755); err != nil { 480 return err 481 } 482 483 mount := exec.Command("bash", "-c", "echo mount -n --bind "+srcPath+" "+dstMount+" >> "+hook) 484 if err := p.runner.Run(mount); err != nil { 485 return err 486 } 487 488 remount := exec.Command("bash", "-c", "echo mount -n --bind -o remount,"+mode+" "+srcPath+" "+dstMount+" >> "+hook) 489 if err := p.runner.Run(remount); err != nil { 490 return err 491 } 492 } 493 494 return nil 495 } 496 497 func (p *LinuxResourcePool) saveBridgeName(id string, bridgeName string) error { 498 bridgeNameFile := path.Join(p.depotPath, id, "bridge-name") 499 return ioutil.WriteFile(bridgeNameFile, []byte(bridgeName), 0644) 500 } 501 502 func (p *LinuxResourcePool) saveRootFSProvider(id string, provider string) error { 503 providerFile := path.Join(p.depotPath, id, "rootfs-provider") 504 return ioutil.WriteFile(providerFile, []byte(provider), 0644) 505 } 506 507 func (p *LinuxResourcePool) saveContainerVersion(id string) error { 508 versionFile := path.Join(p.depotPath, id, "version") 509 return ioutil.WriteFile(versionFile, []byte(p.currentContainerVersion.String()), 0644) 510 } 511 512 func (p *LinuxResourcePool) restoreContainerVersion(id string) (semver.Version, error) { 513 content, err := ioutil.ReadFile(filepath.Join(p.depotPath, id, "version")) 514 if err != nil { 515 if os.IsNotExist(err) { 516 return linux_container.MissingVersion, nil 517 } 518 return semver.Version{}, err 519 } 520 521 return semver.Make(string(content)) 522 } 523 524 func (p *LinuxResourcePool) acquirePoolResources(spec garden.ContainerSpec, id string, logger lager.Logger) (*linux_backend.Resources, error) { 525 resources := linux_backend.NewResources(0, nil, "", nil, p.externalIP) 526 527 subnet, ip, err := parseNetworkSpec(spec.Network) 528 if err != nil { 529 return nil, fmt.Errorf("create container: invalid network spec: %v", err) 530 } 531 532 if err := p.acquireUID(resources, spec.Privileged); err != nil { 533 return nil, err 534 } 535 536 if resources.Network, err = p.subnetPool.Acquire(subnet, ip, logger.Session("subnet-pool")); err != nil { 537 p.releasePoolResources(resources, logger) 538 return nil, err 539 } 540 541 return resources, nil 542 } 543 544 func (p *LinuxResourcePool) acquireUID(resources *linux_backend.Resources, privileged bool) error { 545 if !privileged { 546 resources.RootUID = p.mappingList.Map(0) 547 return nil 548 } 549 550 resources.RootUID = 0 551 return nil 552 } 553 554 func (p *LinuxResourcePool) releasePoolResources(resources *linux_backend.Resources, logger lager.Logger) { 555 for _, port := range resources.Ports { 556 p.portPool.Release(port) 557 } 558 559 if resources.Network != nil { 560 p.subnetPool.Release(resources.Network, logger.Session("subnet-pool")) 561 } 562 } 563 564 func (p *LinuxResourcePool) acquireSystemResources(spec garden.ContainerSpec, id string, resources *linux_backend.Resources, pLog lager.Logger) (string, process.Env, error) { 565 containerPath := path.Join(p.depotPath, id) 566 if err := os.MkdirAll(containerPath, 0755); err != nil { 567 return "", nil, fmt.Errorf("resource_pool: creating container directory: %v", err) 568 } 569 570 rootFSPath, rootFSEnvVars, err := p.setupContainerDirectories(spec, id, resources, pLog) 571 if err != nil { 572 os.RemoveAll(containerPath) 573 return "", nil, err 574 } 575 576 createCmd := path.Join(p.binPath, "create.sh") 577 create := exec.Command(createCmd, containerPath) 578 suff, _ := resources.Network.Subnet.Mask.Size() 579 env := process.Env{ 580 "id": id, 581 "rootfs_path": rootFSPath, 582 "network_host_ip": subnets.GatewayIP(resources.Network.Subnet).String(), 583 "network_container_ip": resources.Network.IP.String(), 584 "network_cidr_suffix": strconv.Itoa(suff), 585 "network_cidr": resources.Network.Subnet.String(), 586 "external_ip": p.externalIP.String(), 587 "container_iface_mtu": fmt.Sprintf("%d", p.mtu), 588 "bridge_iface": resources.Bridge, 589 "root_uid": strconv.FormatUint(uint64(resources.RootUID), 10), 590 "PATH": os.Getenv("PATH"), 591 } 592 create.Env = env.Array() 593 594 pRunner := logging.Runner{ 595 CommandRunner: p.runner, 596 Logger: pLog.Session("create-script"), 597 } 598 599 err = pRunner.Run(create) 600 defer cleanup(&err, func() { 601 p.tryReleaseSystemResources(pLog, id) 602 }) 603 604 if err != nil { 605 pLog.Error("create-command-failed", err, lager.Data{ 606 "CreateCmd": createCmd, 607 "Env": create.Env, 608 }) 609 return "", nil, err 610 } 611 612 err = p.saveRootFSProvider(id, "docker-composite") 613 if err != nil { 614 pLog.Error("save-rootfs-provider-failed", err, lager.Data{ 615 "Id": id, 616 "rootfs": spec.RootFSPath, 617 }) 618 return "", nil, err 619 } 620 621 err = p.saveContainerVersion(id) 622 if err != nil { 623 pLog.Error("save-container-version-failed", err, lager.Data{ 624 "Id": id, 625 "ContainerPath": containerPath, 626 }) 627 return "", nil, err 628 } 629 630 err = p.writeBindMounts(containerPath, rootFSPath, spec.BindMounts, resources.RootUID) 631 if err != nil { 632 pLog.Error("bind-mounts-failed", err) 633 return "", nil, err 634 } 635 636 return rootFSPath, rootFSEnvVars, nil 637 } 638 639 func (p *LinuxResourcePool) setupRootfs(spec garden.ContainerSpec, id string, resources *linux_backend.Resources, pLog lager.Logger) (string, process.Env, error) { 640 rootFSURL, err := url.Parse(spec.RootFSPath) 641 if err != nil { 642 pLog.Error("parse-rootfs-path-failed", err, lager.Data{ 643 "RootFSPath": spec.RootFSPath, 644 }) 645 646 return "", nil, err 647 } 648 649 rootFSSpec := rootfs_provider.Spec{ 650 RootFS: rootFSURL, 651 Namespaced: resources.RootUID != 0, 652 QuotaSize: int64(spec.Limits.Disk.ByteHard), 653 QuotaScope: spec.Limits.Disk.Scope, 654 } 655 656 pLog.Debug("provide-rootfs-starting") 657 rootFSPath, rootFSEnvVars, err := p.rootFSProvider.Create(pLog, id, rootFSSpec) 658 if err != nil { 659 pLog.Error("provide-rootfs-failed", err) 660 661 return "", nil, err 662 } 663 pLog.Debug("provide-rootfs-ended") 664 665 pLog.Debug("clean-rootfs-starting") 666 if err := p.rootFSCleaner.Clean(pLog, rootFSPath); err != nil { 667 return "", nil, err 668 } 669 pLog.Debug("clean-rootfs-ended") 670 671 rootFSProcessEnv, err := process.NewEnv(rootFSEnvVars) 672 if err != nil { 673 pLog.Error("rootfs-env-malformed", err) 674 675 return "", nil, err 676 } 677 678 return rootFSPath, rootFSProcessEnv, nil 679 } 680 681 func (p *LinuxResourcePool) setupContainerDirectories(spec garden.ContainerSpec, id string, resources *linux_backend.Resources, pLog lager.Logger) (string, process.Env, error) { 682 rootFSPath, rootFSEnvVars, err := p.setupRootfs(spec, id, resources, pLog) 683 if err != nil { 684 return "", nil, err 685 } 686 687 pLog.Debug("setup-bridge-starting") 688 if err := p.setupBridge(pLog, id, resources); err != nil { 689 p.rootFSProvider.Destroy(pLog, id) 690 return "", nil, err 691 } 692 pLog.Debug("setup-bridge-ended") 693 694 return rootFSPath, rootFSEnvVars, nil 695 } 696 697 func (p *LinuxResourcePool) setupBridge(pLog lager.Logger, id string, resources *linux_backend.Resources) error { 698 var err error 699 if resources.Bridge, err = p.bridges.Reserve(resources.Network.Subnet, id); err != nil { 700 pLog.Error("reserve-bridge-failed", err, lager.Data{ 701 "Id": id, 702 "Subnet": resources.Network.Subnet, 703 "Bridge": resources.Bridge, 704 }) 705 706 return err 707 } 708 709 if err = p.saveBridgeName(id, resources.Bridge); err != nil { 710 pLog.Error("save-bridge-name-failed", err, lager.Data{ 711 "Id": id, 712 "Bridge": resources.Bridge, 713 }) 714 715 return err 716 } 717 718 return nil 719 } 720 721 func (p *LinuxResourcePool) tryReleaseSystemResources(logger lager.Logger, id string) { 722 err := p.releaseSystemResources(logger, id) 723 if err != nil { 724 logger.Error("failed-to-undo-failed-create", err) 725 } 726 } 727 728 func (p *LinuxResourcePool) releaseSystemResources(logger lager.Logger, id string) error { 729 pRunner := logging.Runner{ 730 CommandRunner: p.runner, 731 Logger: logger, 732 } 733 734 bridgeName, err := ioutil.ReadFile(path.Join(p.depotPath, id, "bridge-name")) 735 if err == nil { 736 if err := p.bridges.Release(string(bridgeName), id); err != nil { 737 return fmt.Errorf("containerpool: release bridge %s: %v", bridgeName, err) 738 } 739 } 740 741 rootFSProvider, err := ioutil.ReadFile(path.Join(p.depotPath, id, "rootfs-provider")) 742 if err != nil { 743 rootFSProvider = []byte("invalid-rootfs-provider") 744 } 745 746 if err = p.iptablesMgr.ContainerTeardown(id); err != nil { 747 return err 748 } 749 750 destroy := exec.Command(path.Join(p.binPath, "destroy.sh"), path.Join(p.depotPath, id)) 751 err = pRunner.Run(destroy) 752 if err != nil { 753 return err 754 } 755 756 if shouldCleanRootfs(string(rootFSProvider)) { 757 if err = p.rootFSProvider.Destroy(logger, id); err != nil { 758 return err 759 } 760 } 761 762 p.filterProvider.ProvideFilter(id).TearDown() 763 return nil 764 } 765 766 func shouldCleanRootfs(rootFSProvider string) bool { 767 // invalid-rootfs-provider indicates that this is probably a recent container that failed on create. 768 // we should try to clean it up 769 770 providers := []string{ 771 "docker-local-aufs", 772 "docker-local-vfs", 773 "docker-remote-aufs", 774 "docker-remote-vfs", 775 "docker-composite", 776 "invalid-rootfs-provider", 777 } 778 779 for _, provider := range providers { 780 if provider == rootFSProvider { 781 return true 782 } 783 } 784 785 return false 786 } 787 788 func getHandle(handle, id string) string { 789 if handle != "" { 790 return handle 791 } 792 return id 793 } 794 795 func cleanup(err *error, undo func()) { 796 if *err != nil { 797 undo() 798 } 799 } 800 801 func parseNetworkSpec(spec string) (subnets.SubnetSelector, subnets.IPSelector, error) { 802 var ipSelector subnets.IPSelector = subnets.DynamicIPSelector 803 var subnetSelector subnets.SubnetSelector = subnets.DynamicSubnetSelector 804 805 if spec != "" { 806 specifiedIP, ipn, err := net.ParseCIDR(suffixIfNeeded(spec)) 807 if err != nil { 808 return nil, nil, err 809 } 810 811 subnetSelector = subnets.StaticSubnetSelector{ipn} 812 813 if !specifiedIP.Equal(subnets.NetworkIP(ipn)) { 814 ipSelector = subnets.StaticIPSelector{specifiedIP} 815 } 816 } 817 818 return subnetSelector, ipSelector, nil 819 } 820 821 func suffixIfNeeded(spec string) string { 822 if !strings.Contains(spec, "/") { 823 spec = spec + "/30" 824 } 825 826 return spec 827 }