github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/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  	"syscall"
    25  
    26  	"github.com/coreos/gexpect"
    27  	"github.com/rkt/rkt/tests/testutils/logger"
    28  )
    29  
    30  // dirDesc structure manages one directory and provides an option for
    31  // rkt invocations
    32  type dirDesc struct {
    33  	dir    string // directory path
    34  	desc   string // directory description, mostly for failure cases
    35  	prefix string // temporary directory prefix
    36  	option string // rkt option for given directory
    37  }
    38  
    39  // newDirDesc creates dirDesc instance managing a temporary directory.
    40  func newDirDesc(prefix, desc, option string) *dirDesc {
    41  	dir := &dirDesc{
    42  		dir:    "",
    43  		desc:   desc,
    44  		prefix: prefix,
    45  		option: option,
    46  	}
    47  	dir.reset()
    48  	return dir
    49  }
    50  
    51  // reset removes the managed directory and recreates it
    52  func (d *dirDesc) reset() {
    53  	d.cleanup()
    54  	dir, err := ioutil.TempDir("", d.prefix)
    55  	if err != nil {
    56  		panic(fmt.Sprintf("Failed to create temporary %s directory: %v", d.desc, err))
    57  	}
    58  	d.dir = dir
    59  }
    60  
    61  // cleanup removes the managed directory. After cleanup this instance
    62  // cannot be used for anything, until it is reset.
    63  func (d *dirDesc) cleanup() {
    64  	if d.dir == "" {
    65  		return
    66  	}
    67  	if err := os.RemoveAll(d.dir); err != nil && !os.IsNotExist(err) {
    68  		panic(fmt.Sprintf("Failed to remove temporary %s directory %q: %s", d.desc, d.dir, err))
    69  	}
    70  	d.dir = ""
    71  }
    72  
    73  // rktOption returns option for rkt invocation
    74  func (d *dirDesc) rktOption() string {
    75  	d.ensureValid()
    76  	return fmt.Sprintf("--%s=%s", d.option, d.dir)
    77  }
    78  
    79  func (d *dirDesc) ensureValid() {
    80  	if d.dir == "" {
    81  		panic(fmt.Sprintf("A temporary %s directory is not set up", d.desc))
    82  	}
    83  }
    84  
    85  // TODO(emil): Document exported.
    86  
    87  type RktRunCtx struct {
    88  	directories []*dirDesc
    89  	useDefaults bool
    90  	mds         *exec.Cmd
    91  	children    []*gexpect.ExpectSubprocess
    92  }
    93  
    94  func NewRktRunCtx() *RktRunCtx {
    95  	return &RktRunCtx{
    96  		directories: []*dirDesc{
    97  			newDirDesc("datadir-", "data", "dir"),
    98  			newDirDesc("localdir-", "local configuration", "local-config"),
    99  			newDirDesc("systemdir-", "system configuration", "system-config"),
   100  			newDirDesc("userdir-", "user configuration", "user-config"),
   101  		},
   102  	}
   103  }
   104  
   105  func (ctx *RktRunCtx) SetupDataDir() error {
   106  	return setupDataDir(ctx.DataDir())
   107  }
   108  
   109  func (ctx *RktRunCtx) LaunchMDS() error {
   110  	ctx.mds = exec.Command(ctx.rktBin(), "metadata-service")
   111  	return ctx.mds.Start()
   112  }
   113  
   114  func (ctx *RktRunCtx) DataDir() string {
   115  	return ctx.dir(0)
   116  }
   117  
   118  func (ctx *RktRunCtx) LocalDir() string {
   119  	return ctx.dir(1)
   120  }
   121  
   122  func (ctx *RktRunCtx) SystemDir() string {
   123  	return ctx.dir(2)
   124  }
   125  
   126  func (ctx *RktRunCtx) UserDir() string {
   127  	return ctx.dir(3)
   128  }
   129  
   130  func (ctx *RktRunCtx) dir(idx int) string {
   131  	ctx.ensureValid()
   132  	if idx < len(ctx.directories) {
   133  		return ctx.directories[idx].dir
   134  	}
   135  	panic("Directory index out of bounds")
   136  }
   137  
   138  func (ctx *RktRunCtx) Reset() {
   139  	ctx.cleanupChildren()
   140  	ctx.RunGC()
   141  	for _, d := range ctx.directories {
   142  		d.reset()
   143  	}
   144  }
   145  
   146  func (ctx *RktRunCtx) cleanupChildren() error {
   147  	for _, child := range ctx.children {
   148  		if child.Cmd == nil ||
   149  			child.Cmd.Process == nil ||
   150  			child.Cmd.ProcessState == nil {
   151  			continue
   152  		}
   153  
   154  		if child.Cmd.ProcessState.Exited() {
   155  			logger.Logf("Child %q already exited", child.Cmd.Path)
   156  			continue
   157  		}
   158  		logger.Logf("Shutting down child %q", child.Cmd.Path)
   159  		if err := child.Cmd.Process.Kill(); err != nil {
   160  			return err
   161  		}
   162  		if _, err := child.Cmd.Process.Wait(); err != nil {
   163  			return err
   164  		}
   165  	}
   166  	return nil
   167  }
   168  
   169  func (ctx *RktRunCtx) Cleanup() {
   170  	if ctx.mds != nil {
   171  		ctx.mds.Process.Kill()
   172  		ctx.mds.Wait()
   173  		os.Remove("/run/rkt/metadata-svc.sock")
   174  	}
   175  	if err := ctx.cleanupChildren(); err != nil {
   176  		logger.Logf("Error during child cleanup: %v", err)
   177  	}
   178  	ctx.RunGC()
   179  	for _, d := range ctx.directories {
   180  		d.cleanup()
   181  	}
   182  }
   183  
   184  func (ctx *RktRunCtx) RunGC() {
   185  	rktArgs := append(ctx.rktOptions(),
   186  		"gc",
   187  		"--grace-period=0s",
   188  	)
   189  	if err := exec.Command(ctx.rktBin(), rktArgs...).Run(); err != nil {
   190  		panic(fmt.Sprintf("Failed to run gc: %v", err))
   191  	}
   192  }
   193  
   194  func (ctx *RktRunCtx) Cmd() string {
   195  	return fmt.Sprintf("%s %s",
   196  		ctx.rktBin(),
   197  		strings.Join(ctx.rktOptions(), " "),
   198  	)
   199  }
   200  
   201  func (ctx *RktRunCtx) ExecCmd(arg ...string) *exec.Cmd {
   202  	args := ctx.rktOptions()
   203  	args = append(args, arg...)
   204  	return exec.Command(ctx.rktBin(), args...)
   205  }
   206  
   207  // TODO(jonboulle): clean this up
   208  func (ctx *RktRunCtx) CmdNoConfig() string {
   209  	return fmt.Sprintf("%s %s",
   210  		ctx.rktBin(),
   211  		ctx.directories[0].rktOption(),
   212  	)
   213  }
   214  
   215  func (ctx *RktRunCtx) rktBin() string {
   216  	rkt := GetValueFromEnvOrPanic("RKT")
   217  	abs, err := filepath.Abs(rkt)
   218  	if err != nil {
   219  		abs = rkt
   220  	}
   221  	return abs
   222  }
   223  
   224  func (ctx *RktRunCtx) GetUidGidRktBinOwnerNotRoot() (int, int) {
   225  	s, err := os.Stat(ctx.rktBin())
   226  	if err != nil {
   227  		return GetUnprivilegedUidGid()
   228  	}
   229  
   230  	uid := int(s.Sys().(*syscall.Stat_t).Uid)
   231  	gid := int(s.Sys().(*syscall.Stat_t).Gid)
   232  
   233  	// If owner is root, fallback to user "nobody"
   234  	if uid == 0 {
   235  		return GetUnprivilegedUidGid()
   236  	}
   237  
   238  	return uid, gid
   239  }
   240  
   241  func (ctx *RktRunCtx) rktOptions() []string {
   242  	ctx.ensureValid()
   243  	opts := make([]string, 0, len(ctx.directories))
   244  	for _, d := range ctx.directories {
   245  		opts = append(opts, d.rktOption())
   246  	}
   247  	return opts
   248  }
   249  
   250  func (ctx *RktRunCtx) ensureValid() {
   251  	for _, d := range ctx.directories {
   252  		d.ensureValid()
   253  	}
   254  }
   255  
   256  func (ctx *RktRunCtx) RegisterChild(child *gexpect.ExpectSubprocess) {
   257  	ctx.children = append(ctx.children, child)
   258  }