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  }