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