github.com/cloudfoundry-attic/garden-linux@v0.333.2-candidate/linux_backend/linux_backend.go (about) 1 package linux_backend 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "os" 8 "path" 9 "time" 10 11 "github.com/cloudfoundry-incubator/garden" 12 "github.com/cloudfoundry-incubator/garden-linux/sysinfo" 13 "github.com/pivotal-golang/lager" 14 ) 15 16 //go:generate counterfeiter . Container 17 18 type Container interface { 19 ID() string 20 HasProperties(garden.Properties) bool 21 GraceTime() time.Duration 22 23 Start() error 24 25 Snapshot(io.Writer) error 26 ResourceSpec() LinuxContainerSpec 27 Restore(LinuxContainerSpec) error 28 Cleanup() error 29 30 LimitDisk(limits garden.DiskLimits) error 31 32 garden.Container 33 } 34 35 //go:generate counterfeiter . ResourcePool 36 37 type ResourcePool interface { 38 Setup() error 39 Acquire(garden.ContainerSpec) (LinuxContainerSpec, error) 40 Restore(io.Reader) (LinuxContainerSpec, error) 41 Release(LinuxContainerSpec) error 42 Prune(keep map[string]bool) error 43 MaxContainers() int 44 } 45 46 //go:generate counterfeiter . ContainerProvider 47 48 type ContainerProvider interface { 49 ProvideContainer(LinuxContainerSpec) Container 50 } 51 52 type ContainerRepository interface { 53 All() []Container 54 Add(Container) 55 FindByHandle(string) (Container, error) 56 Query(filter func(Container) bool, logger lager.Logger) []Container 57 Delete(Container) 58 } 59 60 //go:generate counterfeiter . HealthChecker 61 62 type HealthChecker interface { 63 HealthCheck() error 64 } 65 66 type LinuxBackend struct { 67 logger lager.Logger 68 69 resourcePool ResourcePool 70 systemInfo sysinfo.Provider 71 healthCheck HealthChecker 72 73 snapshotsPath string 74 maxContainers int 75 76 containerRepo ContainerRepository 77 containerProvider ContainerProvider 78 } 79 80 type HandleExistsError struct { 81 Handle string 82 } 83 84 func (e HandleExistsError) Error() string { 85 return fmt.Sprintf("handle already exists: %s", e.Handle) 86 } 87 88 type FailedToSnapshotError struct { 89 OriginalError error 90 } 91 92 func (e FailedToSnapshotError) Error() string { 93 return fmt.Sprintf("failed to save snapshot: %s", e.OriginalError) 94 } 95 96 type MaxContainersReachedError struct { 97 MaxContainers int 98 } 99 100 func (e MaxContainersReachedError) Error() string { 101 return fmt.Sprintf("cannot create more than %d containers", e.MaxContainers) 102 } 103 104 func New( 105 logger lager.Logger, 106 resourcePool ResourcePool, 107 containerRepo ContainerRepository, 108 containerProvider ContainerProvider, 109 systemInfo sysinfo.Provider, 110 healthCheck HealthChecker, 111 snapshotsPath string, 112 maxContainers int, 113 ) *LinuxBackend { 114 return &LinuxBackend{ 115 logger: logger.Session("backend"), 116 117 resourcePool: resourcePool, 118 systemInfo: systemInfo, 119 healthCheck: healthCheck, 120 snapshotsPath: snapshotsPath, 121 maxContainers: maxContainers, 122 123 containerRepo: containerRepo, 124 containerProvider: containerProvider, 125 } 126 } 127 128 func (b *LinuxBackend) Setup() error { 129 return b.resourcePool.Setup() 130 } 131 132 func (b *LinuxBackend) Start() error { 133 if b.snapshotsPath != "" { 134 _, err := os.Stat(b.snapshotsPath) 135 if err == nil { 136 b.restoreSnapshots() 137 os.RemoveAll(b.snapshotsPath) 138 } 139 140 err = os.MkdirAll(b.snapshotsPath, 0755) 141 if err != nil { 142 return err 143 } 144 } 145 146 keep := map[string]bool{} 147 148 containers := b.containerRepo.All() 149 150 for _, container := range containers { 151 keep[container.ID()] = true 152 } 153 154 if err := mountSysFs(); err != nil { 155 return err 156 } 157 158 return b.resourcePool.Prune(keep) 159 } 160 161 func (b *LinuxBackend) Ping() error { 162 if err := b.healthCheck.HealthCheck(); err != nil { 163 return garden.UnrecoverableError{err.Error()} 164 } 165 166 return nil 167 } 168 169 func (b *LinuxBackend) Capacity() (garden.Capacity, error) { 170 totalMemory, err := b.systemInfo.TotalMemory() 171 if err != nil { 172 return garden.Capacity{}, err 173 } 174 175 totalDisk, err := b.systemInfo.TotalDisk() 176 if err != nil { 177 return garden.Capacity{}, err 178 } 179 180 maxContainers := b.resourcePool.MaxContainers() 181 if b.maxContainers > 0 && maxContainers > b.maxContainers { 182 maxContainers = b.maxContainers 183 } 184 185 return garden.Capacity{ 186 MemoryInBytes: totalMemory, 187 DiskInBytes: totalDisk, 188 MaxContainers: uint64(maxContainers), 189 }, nil 190 } 191 192 func (b *LinuxBackend) Create(spec garden.ContainerSpec) (garden.Container, error) { 193 if _, err := b.containerRepo.FindByHandle(spec.Handle); spec.Handle != "" && err == nil { 194 return nil, HandleExistsError{Handle: spec.Handle} 195 } 196 197 if b.maxContainers > 0 { 198 containers := b.containerRepo.All() 199 if len(containers) >= b.maxContainers { 200 return nil, MaxContainersReachedError{ 201 MaxContainers: b.maxContainers, 202 } 203 } 204 } 205 206 containerSpec, err := b.resourcePool.Acquire(spec) 207 if err != nil { 208 return nil, err 209 } 210 211 container := b.containerProvider.ProvideContainer(containerSpec) 212 213 if err := container.Start(); err != nil { 214 b.resourcePool.Release(containerSpec) 215 return nil, err 216 } 217 218 if err := b.ApplyLimits(container, spec.Limits); err != nil { 219 b.resourcePool.Release(containerSpec) 220 return nil, err 221 } 222 223 b.containerRepo.Add(container) 224 225 return container, nil 226 } 227 228 func (b *LinuxBackend) ApplyLimits(container Container, limits garden.Limits) error { 229 if limits.CPU != (garden.CPULimits{}) { 230 if err := container.LimitCPU(limits.CPU); err != nil { 231 return err 232 } 233 } 234 235 if limits.Disk != (garden.DiskLimits{}) { 236 if err := container.LimitDisk(limits.Disk); err != nil { 237 return err 238 } 239 } 240 241 if limits.Bandwidth != (garden.BandwidthLimits{}) { 242 if err := container.LimitBandwidth(limits.Bandwidth); err != nil { 243 return err 244 } 245 } 246 247 if limits.Memory != (garden.MemoryLimits{}) { 248 if err := container.LimitMemory(limits.Memory); err != nil { 249 return err 250 } 251 } 252 253 return nil 254 } 255 256 func (b *LinuxBackend) Destroy(handle string) error { 257 container, err := b.containerRepo.FindByHandle(handle) 258 if err != nil { 259 return err 260 } 261 262 err = container.Cleanup() 263 if err != nil { 264 return err 265 } 266 267 err = b.resourcePool.Release(container.ResourceSpec()) 268 if err != nil { 269 return err 270 } 271 272 b.containerRepo.Delete(container) 273 274 return nil 275 } 276 277 func (b *LinuxBackend) Containers(props garden.Properties) ([]garden.Container, error) { 278 logger := b.logger.Session("containers") 279 logger.Debug("started") 280 containers := toGardenContainers(b.containerRepo.Query(withProperties(props), logger)) 281 logger.Debug("ending", lager.Data{"handles": handles(containers)}) 282 return containers, nil 283 } 284 285 func handles(containers []garden.Container) []string { 286 handles := []string{} 287 for _, container := range containers { 288 handles = append(handles, container.Handle()) 289 } 290 return handles 291 } 292 293 func (b *LinuxBackend) Lookup(handle string) (garden.Container, error) { 294 return b.containerRepo.FindByHandle(handle) 295 } 296 297 func (b *LinuxBackend) BulkInfo(handles []string) (map[string]garden.ContainerInfoEntry, error) { 298 containers := b.containerRepo.Query(withHandles(handles), nil) 299 300 infos := make(map[string]garden.ContainerInfoEntry) 301 for _, container := range containers { 302 info, err := container.Info() 303 if err != nil { 304 infos[container.Handle()] = garden.ContainerInfoEntry{ 305 Err: garden.NewError(err.Error()), 306 } 307 } else { 308 infos[container.Handle()] = garden.ContainerInfoEntry{ 309 Info: info, 310 } 311 } 312 } 313 314 return infos, nil 315 } 316 317 func (b *LinuxBackend) BulkMetrics(handles []string) (map[string]garden.ContainerMetricsEntry, error) { 318 containers := b.containerRepo.Query(withHandles(handles), nil) 319 320 metrics := make(map[string]garden.ContainerMetricsEntry) 321 for _, container := range containers { 322 metric, err := container.Metrics() 323 if err != nil { 324 metrics[container.Handle()] = garden.ContainerMetricsEntry{ 325 Err: garden.NewError(err.Error()), 326 } 327 } else { 328 metrics[container.Handle()] = garden.ContainerMetricsEntry{ 329 Metrics: metric, 330 } 331 } 332 } 333 334 return metrics, nil 335 } 336 337 func (b *LinuxBackend) GraceTime(container garden.Container) time.Duration { 338 return container.(Container).GraceTime() 339 } 340 341 func (b *LinuxBackend) Stop() { 342 for _, container := range b.containerRepo.All() { 343 container.Cleanup() 344 err := b.saveSnapshot(container) 345 if err != nil { 346 b.logger.Error("failed-to-save-snapshot", err, lager.Data{ 347 "container": container.ID(), 348 }) 349 } 350 } 351 } 352 353 func (b *LinuxBackend) restoreSnapshots() { 354 sLog := b.logger.Session("restore") 355 356 entries, err := ioutil.ReadDir(b.snapshotsPath) 357 if err != nil { 358 b.logger.Error("failed-to-read-snapshots", err, lager.Data{ 359 "from": b.snapshotsPath, 360 }) 361 } 362 363 for _, entry := range entries { 364 snapshot := path.Join(b.snapshotsPath, entry.Name()) 365 366 lLog := sLog.Session("load", lager.Data{ 367 "snapshot": entry.Name(), 368 }) 369 370 lLog.Debug("loading") 371 372 file, err := os.Open(snapshot) 373 if err != nil { 374 lLog.Error("failed-to-open", err) 375 } 376 377 _, err = b.restore(file) 378 if err != nil { 379 lLog.Error("failed-to-restore", err) 380 } 381 } 382 } 383 384 func (b *LinuxBackend) saveSnapshot(container Container) error { 385 if b.snapshotsPath == "" { 386 return nil 387 } 388 389 b.logger.Info("save-snapshot", lager.Data{ 390 "container": container.ID(), 391 }) 392 393 snapshotPath := path.Join(b.snapshotsPath, container.ID()) 394 snapshot, err := os.Create(snapshotPath) 395 if err != nil { 396 return &FailedToSnapshotError{err} 397 } 398 399 err = container.Snapshot(snapshot) 400 if err != nil { 401 return &FailedToSnapshotError{err} 402 } 403 404 return snapshot.Close() 405 } 406 407 func (b *LinuxBackend) restore(snapshot io.Reader) (garden.Container, error) { 408 containerSpec, err := b.resourcePool.Restore(snapshot) 409 if err != nil { 410 return nil, err 411 } 412 413 container := b.containerProvider.ProvideContainer(containerSpec) 414 container.Restore(containerSpec) 415 416 b.containerRepo.Add(container) 417 return container, nil 418 } 419 420 func withHandles(handles []string) func(Container) bool { 421 return func(c Container) bool { 422 for _, e := range handles { 423 if e == c.Handle() { 424 return true 425 } 426 } 427 return false 428 } 429 } 430 431 func withProperties(props garden.Properties) func(Container) bool { 432 return func(c Container) bool { 433 return c.HasProperties(props) 434 } 435 } 436 437 func toGardenContainers(cs []Container) []garden.Container { 438 var result []garden.Container 439 for _, c := range cs { 440 result = append(result, c) 441 } 442 443 return result 444 } 445 446 func mountSysFs() error { 447 // mntpoint, err := os.Stat("/sys") 448 // if err != nil { 449 // return err 450 // } 451 452 // parent, err := os.Stat("/") 453 // if err != nil { 454 // return err 455 // } 456 457 //mount sysfs if not mounted already 458 //if mntpoint.Sys().(*syscall.Stat_t).Dev == parent.Sys().(*syscall.Stat_t).Dev { 459 // err = syscall.Mount("sysfs", "/sys", "sysfs", uintptr(0), "") 460 // if err != nil { 461 // return fmt.Errorf("Mounting sysfs failed: %s", err) 462 // } 463 //} 464 465 return nil 466 }