github.com/dmaizel/tests@v0.0.0-20210728163746-cae6a2d9cee8/container.go (about)

     1  // Copyright (c) 2018 Intel Corporation
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package tests
     6  
     7  import (
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"path/filepath"
    12  	"strconv"
    13  	"strings"
    14  	"syscall"
    15  )
    16  
    17  // Container represents a kata container
    18  type Container struct {
    19  	// Bundle contains the container information
    20  	// if nil then try to run the container without --bundle option
    21  	Bundle *Bundle
    22  
    23  	// Console pty slave path
    24  	// if nil then try to run the container without --console option
    25  	Console *string
    26  
    27  	// PidFile where process id is written
    28  	// if nil then try to run the container without --pid-file option
    29  	PidFile *string
    30  
    31  	// LogFile where debug information is written
    32  	// if nil then try to run the container without --log option
    33  	LogFile *string
    34  
    35  	// ID of the container
    36  	// if nil then try to run the container without container ID
    37  	ID *string
    38  
    39  	// Detach allows to run the process detached from the shell
    40  	Detach bool
    41  }
    42  
    43  // Process describes a process to be executed on a running container.
    44  type Process struct {
    45  	ContainerID *string
    46  	Console     *string
    47  	Tty         *string
    48  	Workload    []string
    49  	Detach      bool
    50  }
    51  
    52  // NewContainer returns a new Container
    53  func NewContainer(workload []string, detach bool) (*Container, error) {
    54  	b, err := NewBundle(workload)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	console := ""
    60  
    61  	pidFile := filepath.Join(b.Path, "pid")
    62  	logFile := filepath.Join(b.Path, "log")
    63  	id := RandID(20)
    64  
    65  	return &Container{
    66  		Bundle:  b,
    67  		Console: &console,
    68  		PidFile: &pidFile,
    69  		LogFile: &logFile,
    70  		Detach:  detach,
    71  		ID:      &id,
    72  	}, nil
    73  }
    74  
    75  // Run the container
    76  // calls to run command returning its stdout, stderr and exit code
    77  func (c *Container) Run() (string, string, int) {
    78  	args := []string{}
    79  
    80  	if c.LogFile != nil {
    81  		args = append(args, fmt.Sprintf("--log=%s", *c.LogFile))
    82  	}
    83  
    84  	args = append(args, "run")
    85  
    86  	if c.Bundle != nil {
    87  		args = append(args, fmt.Sprintf("--bundle=%s", c.Bundle.Path))
    88  	}
    89  
    90  	if c.Console != nil {
    91  		args = append(args, fmt.Sprintf("--console=%s", *c.Console))
    92  	}
    93  
    94  	if c.PidFile != nil {
    95  		args = append(args, fmt.Sprintf("--pid-file=%s", *c.PidFile))
    96  	}
    97  
    98  	if c.Detach {
    99  		args = append(args, "--detach")
   100  	}
   101  
   102  	if c.ID != nil {
   103  		args = append(args, *c.ID)
   104  	}
   105  
   106  	cmd := NewCommand(Runtime, args...)
   107  
   108  	return cmd.Run()
   109  }
   110  
   111  // Delete the container
   112  // calls to delete command returning its stdout, stderr and exit code
   113  func (c *Container) Delete(force bool) (string, string, int) {
   114  	args := []string{"delete"}
   115  
   116  	if force {
   117  		args = append(args, "--force")
   118  	}
   119  
   120  	if c.ID != nil {
   121  		args = append(args, *c.ID)
   122  	}
   123  
   124  	cmd := NewCommand(Runtime, args...)
   125  
   126  	return cmd.Run()
   127  }
   128  
   129  // Kill the container
   130  // calls to kill command returning its stdout, stderr and exit code
   131  func (c *Container) Kill(all bool, signal interface{}) (string, string, int) {
   132  	args := []string{"kill"}
   133  
   134  	if all {
   135  		args = append(args, "--all")
   136  	}
   137  
   138  	if c.ID != nil {
   139  		args = append(args, *c.ID)
   140  	}
   141  
   142  	switch t := signal.(type) {
   143  	case syscall.Signal:
   144  		args = append(args, strconv.Itoa(int(t)))
   145  	case string:
   146  		args = append(args, t)
   147  	}
   148  
   149  	cmd := NewCommand(Runtime, args...)
   150  
   151  	return cmd.Run()
   152  }
   153  
   154  // Exec the container
   155  // calls into exec command returning its stdout, stderr and exit code
   156  func (c *Container) Exec(process Process) (string, string, int) {
   157  	args := []string{}
   158  
   159  	if c.LogFile != nil {
   160  		args = append(args, fmt.Sprintf("--log=%s", *c.LogFile))
   161  	}
   162  
   163  	args = append(args, "exec")
   164  
   165  	if process.Console != nil {
   166  		args = append(args, fmt.Sprintf("--console=%s", *process.Console))
   167  	}
   168  
   169  	if process.Tty != nil {
   170  		args = append(args, fmt.Sprintf("--tty=%s", *process.Tty))
   171  	}
   172  
   173  	if process.Detach {
   174  		args = append(args, "--detach")
   175  	}
   176  
   177  	if process.ContainerID != nil {
   178  		args = append(args, *process.ContainerID)
   179  	}
   180  
   181  	args = append(args, process.Workload...)
   182  
   183  	cmd := NewCommand(Runtime, args...)
   184  
   185  	return cmd.Run()
   186  }
   187  
   188  // State returns the state of the container
   189  // calls into state command returning its stdout, stderr and exit code
   190  func (c *Container) State() (string, string, int) {
   191  	args := []string{}
   192  
   193  	args = append(args, "state")
   194  
   195  	if c.ID != nil {
   196  		args = append(args, *c.ID)
   197  	}
   198  
   199  	cmd := NewCommand(Runtime, args...)
   200  
   201  	return cmd.Run()
   202  }
   203  
   204  // List the containers
   205  // calls to list command returning its stdout, stderr and exit code
   206  func (c *Container) List(format string, quiet bool, all bool) (string, string, int) {
   207  	args := []string{"list"}
   208  
   209  	if format != "" {
   210  		args = append(args, fmt.Sprintf("--format=%s", format))
   211  	}
   212  
   213  	if quiet {
   214  		args = append(args, "--quiet")
   215  	}
   216  
   217  	if all {
   218  		args = append(args, "--all")
   219  	}
   220  
   221  	cmd := NewCommand(Runtime, args...)
   222  
   223  	return cmd.Run()
   224  }
   225  
   226  // SetWorkload sets a workload for the container
   227  func (c *Container) SetWorkload(workload []string) error {
   228  	c.Bundle.Config.Process.Args = workload
   229  	return c.Bundle.Save()
   230  }
   231  
   232  // RemoveOption removes a specific option
   233  // container will run without the specific option
   234  func (c *Container) RemoveOption(option string) error {
   235  	switch option {
   236  	case "--bundle", "-b":
   237  		defer c.Bundle.Remove()
   238  		c.Bundle = nil
   239  	case "--console":
   240  		c.Console = nil
   241  	case "--pid-file":
   242  		c.PidFile = nil
   243  	default:
   244  		return fmt.Errorf("undefined option '%s'", option)
   245  	}
   246  
   247  	return nil
   248  }
   249  
   250  // Teardown deletes the container if it is running,
   251  // ensures the container is not running and removes any
   252  // file created by the container
   253  func (c *Container) Teardown() error {
   254  	var cid string
   255  
   256  	if c.ID != nil {
   257  		cid = *c.ID
   258  	}
   259  
   260  	// if container exist then delete it
   261  	if c.Exist() {
   262  		_, stderr, exitCode := c.Delete(true)
   263  		if exitCode != 0 {
   264  			return fmt.Errorf("failed to delete container %s %s", cid, stderr)
   265  		}
   266  
   267  		// if container still exist then fail
   268  		if c.Exist() {
   269  			return fmt.Errorf("unable to delete container %s", cid)
   270  		}
   271  	}
   272  
   273  	if c.Bundle != nil {
   274  		return c.Bundle.Remove()
   275  	}
   276  
   277  	return nil
   278  }
   279  
   280  // Exist returns true if any of next cases is true:
   281  // - list command shows the container
   282  // - the process id specified in the pid file is running (cc-shim)
   283  // - the VM is running (qemu)
   284  // - the proxy is running
   285  // - the shim is running
   286  // else false is returned
   287  func (c *Container) Exist() bool {
   288  	return c.isListed() || c.isWorkloadRunning() ||
   289  		HypervisorRunning(*c.ID) || ProxyRunning(*c.ID) ||
   290  		ShimRunning(*c.ID)
   291  }
   292  
   293  func (c *Container) isListed() bool {
   294  	if c.ID == nil {
   295  		return false
   296  	}
   297  
   298  	stdout, _, ret := c.List("", true, false)
   299  	if ret != 0 {
   300  		return false
   301  	}
   302  
   303  	return strings.Contains(stdout, *c.ID)
   304  }
   305  
   306  func (c *Container) isWorkloadRunning() bool {
   307  	if c.PidFile == nil {
   308  		return false
   309  	}
   310  
   311  	content, err := ioutil.ReadFile(*c.PidFile)
   312  	if err != nil {
   313  		return false
   314  	}
   315  
   316  	if _, err := os.Stat(fmt.Sprintf("/proc/%s/stat", string(content))); os.IsNotExist(err) {
   317  		return false
   318  	}
   319  
   320  	return true
   321  }