github.com/runcom/containerd@v0.0.0-20160708090337-9bff9f934c0d/integration-test/check_test.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "log" 7 "net" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "strings" 12 "sync" 13 "testing" 14 "time" 15 16 "golang.org/x/net/context" 17 18 "google.golang.org/grpc" 19 "google.golang.org/grpc/grpclog" 20 21 "github.com/docker/containerd/api/grpc/types" 22 utils "github.com/docker/containerd/testutils" 23 "github.com/go-check/check" 24 "github.com/golang/protobuf/ptypes/timestamp" 25 ) 26 27 func Test(t *testing.T) { 28 check.TestingT(t) 29 } 30 31 func init() { 32 check.Suite(&ContainerdSuite{}) 33 } 34 35 type ContainerdSuite struct { 36 cwd string 37 outputDir string 38 stateDir string 39 grpcSocket string 40 logFile *os.File 41 cd *exec.Cmd 42 syncChild chan error 43 grpcClient types.APIClient 44 eventFiltersMutex sync.Mutex 45 eventFilters map[string]func(event *types.Event) 46 lastEventTs *timestamp.Timestamp 47 } 48 49 // getClient returns a connection to the Suite containerd 50 func (cs *ContainerdSuite) getClient(socket string) error { 51 // Parse proto://address form addresses. 52 bindParts := strings.SplitN(socket, "://", 2) 53 if len(bindParts) != 2 { 54 return fmt.Errorf("bad bind address format %s, expected proto://address", socket) 55 } 56 57 // reset the logger for grpc to log to dev/null so that it does not mess with our stdio 58 grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags)) 59 dialOpts := []grpc.DialOption{grpc.WithInsecure()} 60 dialOpts = append(dialOpts, 61 grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { 62 return net.DialTimeout(bindParts[0], bindParts[1], timeout) 63 }, 64 )) 65 conn, err := grpc.Dial(socket, dialOpts...) 66 if err != nil { 67 return err 68 } 69 cs.grpcClient = types.NewAPIClient(conn) 70 71 return nil 72 } 73 74 // ContainerdEventsHandler will process all events coming from 75 // containerd. If a filter as been register for a given container id 76 // via `SetContainerEventFilter()`, it will be invoked every time an 77 // event for that id is received 78 func (cs *ContainerdSuite) ContainerdEventsHandler(events types.API_EventsClient) { 79 for { 80 e, err := events.Recv() 81 if err != nil { 82 // If daemon died or exited, return 83 if strings.Contains(err.Error(), "transport is closing") { 84 break 85 } 86 time.Sleep(1 * time.Second) 87 events, _ = cs.grpcClient.Events(context.Background(), &types.EventsRequest{Timestamp: cs.lastEventTs}) 88 continue 89 } 90 cs.lastEventTs = e.Timestamp 91 cs.eventFiltersMutex.Lock() 92 if f, ok := cs.eventFilters[e.Id]; ok { 93 f(e) 94 if e.Type == "exit" && e.Pid == "init" { 95 delete(cs.eventFilters, e.Id) 96 } 97 } 98 cs.eventFiltersMutex.Unlock() 99 } 100 } 101 102 func (cs *ContainerdSuite) StopDaemon(kill bool) { 103 if cs.cd == nil { 104 return 105 } 106 107 if kill { 108 cs.cd.Process.Kill() 109 <-cs.syncChild 110 cs.cd = nil 111 } else { 112 // Terminate gently if possible 113 cs.cd.Process.Signal(os.Interrupt) 114 115 done := false 116 for done == false { 117 select { 118 case err := <-cs.syncChild: 119 if err != nil { 120 fmt.Printf("master containerd did not exit cleanly: %v\n", err) 121 } 122 done = true 123 case <-time.After(3 * time.Second): 124 fmt.Println("Timeout while waiting for containerd to exit, killing it!") 125 cs.cd.Process.Kill() 126 } 127 } 128 } 129 } 130 131 func (cs *ContainerdSuite) RestartDaemon(kill bool) error { 132 cs.StopDaemon(kill) 133 134 cd := exec.Command("containerd", "--debug", 135 "--state-dir", cs.stateDir, 136 "--listen", cs.grpcSocket, 137 "--metrics-interval", "0m0s", 138 "--runtime-args", fmt.Sprintf("--root=%s", filepath.Join(cs.cwd, cs.outputDir, "runc")), 139 ) 140 cd.Stderr = cs.logFile 141 cd.Stdout = cs.logFile 142 143 if err := cd.Start(); err != nil { 144 return err 145 } 146 cs.cd = cd 147 148 if err := cs.getClient(cs.grpcSocket); err != nil { 149 // Kill the daemon 150 cs.cd.Process.Kill() 151 return err 152 } 153 154 // Monitor events 155 events, err := cs.grpcClient.Events(context.Background(), &types.EventsRequest{Timestamp: cs.lastEventTs}) 156 if err != nil { 157 return err 158 } 159 160 go cs.ContainerdEventsHandler(events) 161 162 go func() { 163 cs.syncChild <- cd.Wait() 164 }() 165 166 return nil 167 } 168 169 func (cs *ContainerdSuite) SetUpSuite(c *check.C) { 170 bundleMap = make(map[string]Bundle) 171 cs.eventFilters = make(map[string]func(event *types.Event)) 172 173 // Get working directory for tests 174 wd := utils.GetTestOutDir() 175 if err := os.Chdir(wd); err != nil { 176 c.Fatalf("Could not change working directory: %v", err) 177 } 178 cs.cwd = wd 179 180 // Clean old bundles 181 os.RemoveAll(utils.BundlesRoot) 182 183 // Ensure the oci bundles directory exists 184 if err := os.MkdirAll(utils.BundlesRoot, 0755); err != nil { 185 c.Fatalf("Failed to create bundles directory: %v", err) 186 } 187 188 // Generate the reference spec 189 if err := utils.GenerateReferenceSpecs(utils.BundlesRoot); err != nil { 190 c.Fatalf("Unable to generate OCI reference spec: %v", err) 191 } 192 193 // Create our output directory 194 cs.outputDir = fmt.Sprintf(utils.OutputDirFormat, time.Now().Format("2006-01-02_150405.000000")) 195 196 cs.stateDir = filepath.Join(cs.outputDir, "containerd-master") 197 if err := os.MkdirAll(cs.stateDir, 0755); err != nil { 198 c.Fatalf("Unable to created output directory '%s': %v", cs.stateDir, err) 199 } 200 201 cs.grpcSocket = "unix://" + filepath.Join(cs.outputDir, "containerd-master", "containerd.sock") 202 cdLogFile := filepath.Join(cs.outputDir, "containerd-master", "containerd.log") 203 204 f, err := os.OpenFile(cdLogFile, os.O_CREATE|os.O_TRUNC|os.O_RDWR|os.O_SYNC, 0777) 205 if err != nil { 206 c.Fatalf("Failed to create master containerd log file: %v", err) 207 } 208 cs.logFile = f 209 210 cs.syncChild = make(chan error) 211 cs.RestartDaemon(false) 212 } 213 214 func (cs *ContainerdSuite) TearDownSuite(c *check.C) { 215 216 // tell containerd to stop 217 if cs.cd != nil { 218 cs.cd.Process.Signal(os.Interrupt) 219 220 done := false 221 for done == false { 222 select { 223 case err := <-cs.syncChild: 224 if err != nil { 225 c.Errorf("master containerd did not exit cleanly: %v", err) 226 } 227 done = true 228 case <-time.After(3 * time.Second): 229 fmt.Println("Timeout while waiting for containerd to exit, killing it!") 230 cs.cd.Process.Kill() 231 } 232 } 233 } 234 235 if cs.logFile != nil { 236 cs.logFile.Close() 237 } 238 } 239 240 func (cs *ContainerdSuite) SetContainerEventFilter(id string, filter func(event *types.Event)) { 241 cs.eventFiltersMutex.Lock() 242 cs.eventFilters[id] = filter 243 cs.eventFiltersMutex.Unlock() 244 } 245 246 func (cs *ContainerdSuite) TearDownTest(c *check.C) { 247 ctrs, err := cs.ListRunningContainers() 248 if err != nil { 249 c.Fatalf("Unable to retrieve running containers: %v", err) 250 } 251 252 // Kill all containers that survived 253 for _, ctr := range ctrs { 254 ch := make(chan interface{}) 255 cs.SetContainerEventFilter(ctr.Id, func(e *types.Event) { 256 if e.Type == "exit" && e.Pid == "init" { 257 ch <- nil 258 } 259 }) 260 261 if err := cs.KillContainer(ctr.Id); err != nil { 262 fmt.Fprintf(os.Stderr, "Failed to cleanup leftover test containers: %v\n", err) 263 } 264 265 select { 266 case <-ch: 267 case <-time.After(3 * time.Second): 268 fmt.Fprintf(os.Stderr, "TearDownTest: Containerd %v didn't die after 3 seconds\n", ctr.Id) 269 } 270 } 271 }