github.com/blixtra/rkt@v0.8.1-0.20160204105720-ab0d1add1a43/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/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) SetupDataDir() error {
   104  	return setupDataDir(ctx.DataDir())
   105  }
   106  
   107  func (ctx *RktRunCtx) LaunchMDS() error {
   108  	ctx.mds = exec.Command(ctx.rktBin(), "metadata-service")
   109  	return ctx.mds.Start()
   110  }
   111  
   112  func (ctx *RktRunCtx) DataDir() string {
   113  	return ctx.dir(0)
   114  }
   115  
   116  func (ctx *RktRunCtx) LocalDir() string {
   117  	return ctx.dir(1)
   118  }
   119  
   120  func (ctx *RktRunCtx) SystemDir() string {
   121  	return ctx.dir(2)
   122  }
   123  
   124  func (ctx *RktRunCtx) dir(idx int) string {
   125  	ctx.ensureValid()
   126  	if idx < len(ctx.directories) {
   127  		return ctx.directories[idx].dir
   128  	}
   129  	panic("Directory index out of bounds")
   130  }
   131  
   132  func (ctx *RktRunCtx) Reset() {
   133  	ctx.cleanupChildren()
   134  	ctx.RunGC()
   135  	for _, d := range ctx.directories {
   136  		d.reset()
   137  	}
   138  }
   139  
   140  func (ctx *RktRunCtx) cleanupChildren() error {
   141  	for _, child := range ctx.children {
   142  		if child.Cmd.ProcessState.Exited() {
   143  			logger.Logf("Child %q already exited", child.Cmd.Path)
   144  			continue
   145  		}
   146  		logger.Logf("Shutting down child %q", child.Cmd.Path)
   147  		if err := child.Cmd.Process.Kill(); err != nil {
   148  			return err
   149  		}
   150  		if _, err := child.Cmd.Process.Wait(); err != nil {
   151  			return err
   152  		}
   153  	}
   154  	return nil
   155  }
   156  
   157  func (ctx *RktRunCtx) Cleanup() {
   158  	if ctx.mds != nil {
   159  		ctx.mds.Process.Kill()
   160  		ctx.mds.Wait()
   161  		os.Remove("/run/rkt/metadata-svc.sock")
   162  	}
   163  	if err := ctx.cleanupChildren(); err != nil {
   164  		logger.Logf("Error during child cleanup: %v", err)
   165  	}
   166  	ctx.RunGC()
   167  	for _, d := range ctx.directories {
   168  		d.cleanup()
   169  	}
   170  }
   171  
   172  func (ctx *RktRunCtx) RunGC() {
   173  	rktArgs := append(ctx.rktOptions(),
   174  		"gc",
   175  		"--grace-period=0s",
   176  	)
   177  	if err := exec.Command(ctx.rktBin(), rktArgs...).Run(); err != nil {
   178  		panic(fmt.Sprintf("Failed to run gc: %v", err))
   179  	}
   180  }
   181  
   182  func (ctx *RktRunCtx) Cmd() string {
   183  	return fmt.Sprintf("%s %s",
   184  		ctx.rktBin(),
   185  		strings.Join(ctx.rktOptions(), " "),
   186  	)
   187  }
   188  
   189  // TODO(jonboulle): clean this up
   190  func (ctx *RktRunCtx) CmdNoConfig() string {
   191  	return fmt.Sprintf("%s %s",
   192  		ctx.rktBin(),
   193  		ctx.directories[0].rktOption(),
   194  	)
   195  }
   196  
   197  func (ctx *RktRunCtx) rktBin() string {
   198  	rkt := GetValueFromEnvOrPanic("RKT")
   199  	abs, err := filepath.Abs(rkt)
   200  	if err != nil {
   201  		abs = rkt
   202  	}
   203  	return abs
   204  }
   205  
   206  func (ctx *RktRunCtx) rktOptions() []string {
   207  	ctx.ensureValid()
   208  	opts := make([]string, 0, len(ctx.directories))
   209  	for _, d := range ctx.directories {
   210  		opts = append(opts, d.rktOption())
   211  	}
   212  	return opts
   213  }
   214  
   215  func (ctx *RktRunCtx) ensureValid() {
   216  	for _, d := range ctx.directories {
   217  		d.ensureValid()
   218  	}
   219  }
   220  
   221  func (ctx *RktRunCtx) RegisterChild(child *gexpect.ExpectSubprocess) {
   222  	ctx.children = append(ctx.children, child)
   223  }