github.com/stackdocker/rkt@v0.10.1-0.20151109095037-1aa827478248/tests/testutils/ctx.go (about)

     1  // Copyright 2015 The rkt Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package testutils
    16  
    17  import (
    18  	"fmt"
    19  	"io/ioutil"
    20  	"os"
    21  	"os/exec"
    22  	"path/filepath"
    23  	"strings"
    24  
    25  	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/steveeJ/gexpect"
    26  	"github.com/coreos/rkt/tests/testutils/logger"
    27  )
    28  
    29  // dirDesc structure manages one directory and provides an option for
    30  // rkt invocations
    31  type dirDesc struct {
    32  	dir    string // directory path
    33  	desc   string // directory description, mostly for failure cases
    34  	prefix string // temporary directory prefix
    35  	option string // rkt option for given directory
    36  }
    37  
    38  // newDirDesc creates dirDesc instance managing a temporary directory.
    39  func newDirDesc(prefix, desc, option string) *dirDesc {
    40  	dir := &dirDesc{
    41  		dir:    "",
    42  		desc:   desc,
    43  		prefix: prefix,
    44  		option: option,
    45  	}
    46  	dir.reset()
    47  	return dir
    48  }
    49  
    50  // reset removes the managed directory and recreates it
    51  func (d *dirDesc) reset() {
    52  	d.cleanup()
    53  	dir, err := ioutil.TempDir("", d.prefix)
    54  	if err != nil {
    55  		panic(fmt.Sprintf("Failed to create temporary %s directory: %v", d.desc, err))
    56  	}
    57  	d.dir = dir
    58  }
    59  
    60  // cleanup removes the managed directory. After cleanup this instance
    61  // cannot be used for anything, until it is reset.
    62  func (d *dirDesc) cleanup() {
    63  	if d.dir == "" {
    64  		return
    65  	}
    66  	if err := os.RemoveAll(d.dir); err != nil && !os.IsNotExist(err) {
    67  		panic(fmt.Sprintf("Failed to remove temporary %s directory %q: %s", d.desc, d.dir, err))
    68  	}
    69  	d.dir = ""
    70  }
    71  
    72  // rktOption returns option for rkt invocation
    73  func (d *dirDesc) rktOption() string {
    74  	d.ensureValid()
    75  	return fmt.Sprintf("--%s=%s", d.option, d.dir)
    76  }
    77  
    78  func (d *dirDesc) ensureValid() {
    79  	if d.dir == "" {
    80  		panic(fmt.Sprintf("A temporary %s directory is not set up", d.desc))
    81  	}
    82  }
    83  
    84  // TODO(emil): Document exported.
    85  
    86  type RktRunCtx struct {
    87  	directories []*dirDesc
    88  	useDefaults bool
    89  	mds         *exec.Cmd
    90  	children    []*gexpect.ExpectSubprocess
    91  }
    92  
    93  func NewRktRunCtx() *RktRunCtx {
    94  	return &RktRunCtx{
    95  		directories: []*dirDesc{
    96  			newDirDesc("datadir-", "data", "dir"),
    97  			newDirDesc("localdir-", "local configuration", "local-config"),
    98  			newDirDesc("systemdir-", "system configuration", "system-config"),
    99  		},
   100  	}
   101  }
   102  
   103  func (ctx *RktRunCtx) LaunchMDS() error {
   104  	ctx.mds = exec.Command(ctx.rktBin(), "metadata-service")
   105  	return ctx.mds.Start()
   106  }
   107  
   108  func (ctx *RktRunCtx) DataDir() string {
   109  	return ctx.dir(0)
   110  }
   111  
   112  func (ctx *RktRunCtx) LocalDir() string {
   113  	return ctx.dir(1)
   114  }
   115  
   116  func (ctx *RktRunCtx) SystemDir() string {
   117  	return ctx.dir(2)
   118  }
   119  
   120  func (ctx *RktRunCtx) dir(idx int) string {
   121  	ctx.ensureValid()
   122  	if idx < len(ctx.directories) {
   123  		return ctx.directories[idx].dir
   124  	}
   125  	panic("Directory index out of bounds")
   126  }
   127  
   128  func (ctx *RktRunCtx) Reset() {
   129  	ctx.cleanupChildren()
   130  	ctx.RunGC()
   131  	for _, d := range ctx.directories {
   132  		d.reset()
   133  	}
   134  }
   135  
   136  func (ctx *RktRunCtx) cleanupChildren() error {
   137  	for _, child := range ctx.children {
   138  		if child.Cmd.ProcessState.Exited() {
   139  			logger.Logf("Child %q already exited", child.Cmd.Path)
   140  			continue
   141  		}
   142  		logger.Logf("Shutting down child %q", child.Cmd.Path)
   143  		if err := child.Cmd.Process.Kill(); err != nil {
   144  			return err
   145  		}
   146  		if _, err := child.Cmd.Process.Wait(); err != nil {
   147  			return err
   148  		}
   149  	}
   150  	return nil
   151  }
   152  
   153  func (ctx *RktRunCtx) Cleanup() {
   154  	if ctx.mds != nil {
   155  		ctx.mds.Process.Kill()
   156  		ctx.mds.Wait()
   157  		os.Remove("/run/rkt/metadata-svc.sock")
   158  	}
   159  	if err := ctx.cleanupChildren(); err != nil {
   160  		logger.Logf("Error during child cleanup: %v", err)
   161  	}
   162  	ctx.RunGC()
   163  	for _, d := range ctx.directories {
   164  		d.cleanup()
   165  	}
   166  }
   167  
   168  func (ctx *RktRunCtx) RunGC() {
   169  	rktArgs := append(ctx.rktOptions(),
   170  		"gc",
   171  		"--grace-period=0s",
   172  	)
   173  	if err := exec.Command(ctx.rktBin(), rktArgs...).Run(); err != nil {
   174  		panic(fmt.Sprintf("Failed to run gc: %v", err))
   175  	}
   176  }
   177  
   178  func (ctx *RktRunCtx) Cmd() string {
   179  	return fmt.Sprintf("%s %s",
   180  		ctx.rktBin(),
   181  		strings.Join(ctx.rktOptions(), " "),
   182  	)
   183  }
   184  
   185  // TODO(jonboulle): clean this up
   186  func (ctx *RktRunCtx) CmdNoConfig() string {
   187  	return fmt.Sprintf("%s %s",
   188  		ctx.rktBin(),
   189  		ctx.directories[0].rktOption(),
   190  	)
   191  }
   192  
   193  func (ctx *RktRunCtx) rktBin() string {
   194  	rkt := GetValueFromEnvOrPanic("RKT")
   195  	abs, err := filepath.Abs(rkt)
   196  	if err != nil {
   197  		abs = rkt
   198  	}
   199  	return abs
   200  }
   201  
   202  func (ctx *RktRunCtx) rktOptions() []string {
   203  	ctx.ensureValid()
   204  	opts := make([]string, 0, len(ctx.directories))
   205  	for _, d := range ctx.directories {
   206  		opts = append(opts, d.rktOption())
   207  	}
   208  	return opts
   209  }
   210  
   211  func (ctx *RktRunCtx) ensureValid() {
   212  	for _, d := range ctx.directories {
   213  		d.ensureValid()
   214  	}
   215  }
   216  
   217  func (ctx *RktRunCtx) RegisterChild(child *gexpect.ExpectSubprocess) {
   218  	ctx.children = append(ctx.children, child)
   219  }