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 }