github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/integration/e2e/scenario.go (about)

     1  package e2e
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strings"
     7  
     8  	"github.com/pkg/errors"
     9  )
    10  
    11  const (
    12  	ContainerSharedDir = "/shared"
    13  )
    14  
    15  type Service interface {
    16  	Name() string
    17  	Start(networkName, dir string) error
    18  	WaitReady() error
    19  
    20  	// It should be ok to Stop and Kill more than once, with next invokes being noop.
    21  	Kill() error
    22  	Stop() error
    23  }
    24  
    25  type Scenario struct {
    26  	services []Service
    27  
    28  	networkName string
    29  	sharedDir   string
    30  }
    31  
    32  func NewScenario(networkName string) (*Scenario, error) {
    33  	s := &Scenario{networkName: networkName}
    34  
    35  	var err error
    36  	s.sharedDir, err = GetTempDirectory()
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	// Force a shutdown in order to cleanup from a spurious situation in case
    42  	// the previous tests run didn't cleanup correctly.
    43  	s.shutdown()
    44  
    45  	// Setup the docker network.
    46  	if out, err := RunCommandAndGetOutput("docker", "network", "create", networkName); err != nil {
    47  		logger.Log(string(out))
    48  		s.clean()
    49  		return nil, errors.Wrapf(err, "create docker network '%s'", networkName)
    50  	}
    51  
    52  	return s, nil
    53  }
    54  
    55  // SharedDir returns the absolute path of the directory on the host that is shared with all services in docker.
    56  func (s *Scenario) SharedDir() string {
    57  	return s.sharedDir
    58  }
    59  
    60  // NetworkName returns the network name that scenario is responsible for.
    61  func (s *Scenario) NetworkName() string {
    62  	return s.networkName
    63  }
    64  
    65  func (s *Scenario) isRegistered(name string) bool {
    66  	for _, service := range s.services {
    67  		if service.Name() == name {
    68  			return true
    69  		}
    70  	}
    71  	return false
    72  }
    73  
    74  func (s *Scenario) StartAndWaitReady(services ...Service) error {
    75  	if err := s.Start(services...); err != nil {
    76  		return err
    77  	}
    78  	return s.WaitReady(services...)
    79  }
    80  
    81  func (s *Scenario) Start(services ...Service) error {
    82  	for _, service := range services {
    83  		logger.Log("Starting", service.Name())
    84  
    85  		// Ensure another service with the same name doesn't exist.
    86  		if s.isRegistered(service.Name()) {
    87  			return fmt.Errorf("another service with the same name '%s' has already been started", service.Name())
    88  		}
    89  
    90  		// Start the service.
    91  		if err := service.Start(s.networkName, s.SharedDir()); err != nil {
    92  			return err
    93  		}
    94  
    95  		// Add to the list of services.
    96  		s.services = append(s.services, service)
    97  	}
    98  
    99  	return nil
   100  }
   101  
   102  func (s *Scenario) Stop(services ...Service) error {
   103  	for _, service := range services {
   104  		if !s.isRegistered(service.Name()) {
   105  			return fmt.Errorf("unable to stop service %s because it does not exist", service.Name())
   106  		}
   107  		if err := service.Stop(); err != nil {
   108  			return err
   109  		}
   110  
   111  		// Remove the service from the list of services.
   112  		for i, entry := range s.services {
   113  			if entry.Name() == service.Name() {
   114  				s.services = append(s.services[:i], s.services[i+1:]...)
   115  				break
   116  			}
   117  		}
   118  	}
   119  	return nil
   120  }
   121  
   122  func (s *Scenario) WaitReady(services ...Service) error {
   123  	for _, service := range services {
   124  		if !s.isRegistered(service.Name()) {
   125  			return fmt.Errorf("unable to wait for service %s because it does not exist", service.Name())
   126  		}
   127  		if err := service.WaitReady(); err != nil {
   128  			return err
   129  		}
   130  	}
   131  	return nil
   132  }
   133  
   134  func (s *Scenario) Close() {
   135  	if s == nil {
   136  		return
   137  	}
   138  	s.shutdown()
   139  	s.clean()
   140  }
   141  
   142  // TODO(bwplotka): Add comments.
   143  func (s *Scenario) clean() {
   144  	if err := os.RemoveAll(s.sharedDir); err != nil {
   145  		logger.Log("error while removing sharedDir", s.sharedDir, "err:", err)
   146  	}
   147  }
   148  
   149  func (s *Scenario) shutdown() {
   150  	// Kill the services in the opposite order.
   151  	for i := len(s.services) - 1; i >= 0; i-- {
   152  		if err := s.services[i].Kill(); err != nil {
   153  			logger.Log("Unable to kill service", s.services[i].Name(), ":", err.Error())
   154  		}
   155  	}
   156  
   157  	// Ensure there are no leftover containers.
   158  	if out, err := RunCommandAndGetOutput(
   159  		"docker",
   160  		"ps",
   161  		"-a",
   162  		"--quiet",
   163  		"--filter",
   164  		fmt.Sprintf("network=%s", s.networkName),
   165  	); err == nil {
   166  		for _, containerID := range strings.Split(string(out), "\n") {
   167  			containerID = strings.TrimSpace(containerID)
   168  			if containerID == "" {
   169  				continue
   170  			}
   171  
   172  			if out, err = RunCommandAndGetOutput("docker", "rm", "--force", containerID); err != nil {
   173  				logger.Log(string(out))
   174  				logger.Log("Unable to cleanup leftover container", containerID, ":", err.Error())
   175  			}
   176  		}
   177  	} else {
   178  		logger.Log(string(out))
   179  		logger.Log("Unable to cleanup leftover containers:", err.Error())
   180  	}
   181  
   182  	// Teardown the docker network. In case the network does not exists (ie. this function
   183  	// is called during the setup of the scenario) we skip the removal in order to not log
   184  	// an error which may be misleading.
   185  	if ok, err := existDockerNetwork(s.networkName); ok || err != nil {
   186  		if out, err := RunCommandAndGetOutput("docker", "network", "rm", s.networkName); err != nil {
   187  			logger.Log(string(out))
   188  			logger.Log("Unable to remove docker network", s.networkName, ":", err.Error())
   189  		}
   190  	}
   191  }
   192  
   193  func existDockerNetwork(networkName string) (bool, error) {
   194  	out, err := RunCommandAndGetOutput("docker", "network", "ls", "--quiet", "--filter", fmt.Sprintf("name=%s", networkName))
   195  	if err != nil {
   196  		logger.Log(string(out))
   197  		logger.Log("Unable to check if docker network", networkName, "exists:", err.Error())
   198  	}
   199  
   200  	return strings.TrimSpace(string(out)) != "", nil
   201  }