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  }