github.com/in4it/ecs-deploy@v0.0.42-0.20240508120354-ed77ff16df25/test/integration_test.go (about)

     1  package test
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/signal"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/in4it/ecs-deploy/api"
    11  	"github.com/in4it/ecs-deploy/provider/ecs"
    12  	"github.com/in4it/ecs-deploy/service"
    13  	"github.com/in4it/ecs-deploy/util"
    14  )
    15  
    16  var runIntegrationTest = util.GetEnv("TEST_RUN_INTEGRATION", "no")
    17  var bootstrapFlags = &api.Flags{
    18  	Region:                util.GetEnv("AWS_REGION", ""),
    19  	ClusterName:           util.GetEnv("TEST_CLUSTERNAME", "integrationtest"),
    20  	Environment:           util.GetEnv("TEST_ENVIRONMENT", ""),
    21  	AlbSecurityGroups:     util.GetEnv("TEST_ALB_SG", ""),
    22  	EcsSubnets:            util.GetEnv("TEST_ECS_SUBNETS", ""),
    23  	CloudwatchLogsPrefix:  util.GetEnv("TEST_CLOUDWATCH_LOGS_PREFIX", ""),
    24  	CloudwatchLogsEnabled: util.YesNoToBool(util.GetEnv("TEST_CLOUDWATCH_LOGS_ENABLED", "no")),
    25  	KeyName:               util.GetEnv("TEST_KEYNAME", util.GetEnv("TEST_CLUSTERNAME", "integrationtest")),
    26  	InstanceType:          util.GetEnv("TEST_INSTANCETYPE", "t2.micro"),
    27  	EcsSecurityGroups:     util.GetEnv("TEST_ECS_SG", ""),
    28  	EcsMinSize:            util.GetEnv("TEST_ECS_MINSIZE", "1"),
    29  	EcsMaxSize:            util.GetEnv("TEST_ECS_MAXSIZE", "1"),
    30  	EcsDesiredSize:        util.GetEnv("TEST_ECS_DESIREDSIZE", "1"),
    31  	ParamstoreEnabled:     util.YesNoToBool(util.GetEnv("TEST_PARAMSTORE_ENABLED", "no")),
    32  	DisableEcsDeploy:      true,
    33  	LoadBalancers: []service.LoadBalancer{
    34  		{
    35  			Name:          util.GetEnv("TEST_CLUSTERNAME", "integrationtest"),
    36  			IPAddressType: "ipv4",
    37  			Scheme:        "internet-facing",
    38  			Type:          "application",
    39  		},
    40  		{
    41  			Name:          util.GetEnv("TEST_CLUSTERNAME", "integrationtest") + "-2",
    42  			IPAddressType: "ipv4",
    43  			Scheme:        "internet-facing",
    44  			Type:          "application",
    45  		},
    46  	},
    47  }
    48  
    49  var ecsDefault = service.Deploy{
    50  	Cluster:               bootstrapFlags.ClusterName,
    51  	ServiceName:           "integrationtest-default",
    52  	ServicePort:           80,
    53  	ServiceProtocol:       "HTTP",
    54  	DesiredCount:          1,
    55  	MinimumHealthyPercent: 100,
    56  	MaximumPercent:        200,
    57  	Containers: []*service.DeployContainer{
    58  		{
    59  			ContainerName:     "integrationtest-default",
    60  			ContainerPort:     80,
    61  			ContainerImage:    "nginx",
    62  			ContainerURI:      "index.docker.io/nginx:alpine",
    63  			Essential:         true,
    64  			MemoryReservation: 128,
    65  			CPUReservation:    64,
    66  			DockerLabels:      map[string]string{"mykey": "myvalue"},
    67  		},
    68  	},
    69  }
    70  var ecsDefaultConcurrentDeploy = service.Deploy{
    71  	Cluster:               bootstrapFlags.ClusterName,
    72  	ServiceName:           "integrationtest-concurrency",
    73  	ServicePort:           80,
    74  	ServiceProtocol:       "HTTP",
    75  	DesiredCount:          1,
    76  	MinimumHealthyPercent: 100,
    77  	MaximumPercent:        200,
    78  	DeregistrationDelay:   5,
    79  	Containers: []*service.DeployContainer{
    80  		{
    81  			ContainerName:     "integrationtest-default",
    82  			ContainerPort:     80,
    83  			ContainerImage:    "nginx",
    84  			ContainerURI:      "index.docker.io/nginx:alpine",
    85  			Essential:         true,
    86  			MemoryReservation: 128,
    87  			CPUReservation:    64,
    88  		},
    89  	},
    90  }
    91  var ecsMultiDeploy = service.DeployServices{
    92  	Services: []service.Deploy{ecsDefault, ecsDefaultConcurrentDeploy},
    93  }
    94  var ecsDefaultWithChanges = service.Deploy{
    95  	Cluster:               bootstrapFlags.ClusterName,
    96  	LoadBalancer:          bootstrapFlags.ClusterName + "-2",
    97  	ServicePort:           80,
    98  	ServiceProtocol:       "HTTP",
    99  	DesiredCount:          1,
   100  	MinimumHealthyPercent: 100,
   101  	MaximumPercent:        200,
   102  	DeregistrationDelay:   0,
   103  	Stickiness: service.DeployStickiness{
   104  		Enabled:  true,
   105  		Duration: 10000,
   106  	},
   107  	Containers: []*service.DeployContainer{
   108  		{
   109  			ContainerName:     "integrationtest-default",
   110  			ContainerPort:     80,
   111  			ContainerImage:    "nginx",
   112  			ContainerURI:      "index.docker.io/nginx:alpine",
   113  			Essential:         true,
   114  			MemoryReservation: 128,
   115  			CPUReservation:    64,
   116  		},
   117  	},
   118  }
   119  var ecsDefaultFailingHealthCheck = service.Deploy{
   120  	Cluster:               bootstrapFlags.ClusterName,
   121  	ServicePort:           80,
   122  	ServiceProtocol:       "HTTP",
   123  	DesiredCount:          1,
   124  	MinimumHealthyPercent: 100,
   125  	MaximumPercent:        200,
   126  	DeregistrationDelay:   5,
   127  	Containers: []*service.DeployContainer{
   128  		{
   129  			ContainerName:     "integrationtest-default",
   130  			ContainerPort:     80,
   131  			ContainerImage:    "nginx",
   132  			ContainerURI:      "index.docker.io/redis:latest",
   133  			Essential:         true,
   134  			MemoryReservation: 128,
   135  			CPUReservation:    64,
   136  		},
   137  	},
   138  }
   139  var ecsDeploy = service.Deploy{
   140  	Cluster:               bootstrapFlags.ClusterName,
   141  	ServicePort:           8080,
   142  	ServiceProtocol:       "HTTP",
   143  	DesiredCount:          1,
   144  	MinimumHealthyPercent: 100,
   145  	MaximumPercent:        200,
   146  	DeregistrationDelay:   5,
   147  	Containers: []*service.DeployContainer{
   148  		{
   149  			ContainerName:     "integrationtest-ecs-deploy",
   150  			ContainerTag:      "latest",
   151  			ContainerPort:     8080,
   152  			ContainerURI:      "index.docker.io/in4it/ecs-deploy:latest",
   153  			Essential:         true,
   154  			MemoryReservation: 256,
   155  			CPUReservation:    64,
   156  		},
   157  	},
   158  }
   159  
   160  func TestClusterIntegration(t *testing.T) {
   161  	if testing.Short() {
   162  		t.Skip("skipping integration test")
   163  	}
   164  	if runIntegrationTest != "yes" {
   165  		fmt.Println("Skipping integrationtest (env var TEST_RUN_INTEGRATION != yes)")
   166  		t.Skip("skipping integration test")
   167  	}
   168  	// Do you want to run integration test?
   169  	fmt.Println("Going to run integration test in 5s... (You can hit ctrl+c now to abort)")
   170  	time.Sleep(5 * time.Second)
   171  	// setup teardown capture (ctrl+c)
   172  	c := make(chan os.Signal, 1)
   173  	signal.Notify(c, os.Interrupt)
   174  	go func() {
   175  		<-c
   176  		fmt.Println("Caught SIGINT: running teardown")
   177  		teardown(t)
   178  		os.Exit(1)
   179  	}()
   180  	// integration test for cluster
   181  	if accountId == nil {
   182  		t.Skip(noAWSMsg)
   183  	}
   184  	teardownFunc := setupTestCluster(t)
   185  	defer teardownFunc(t)
   186  }
   187  func setupTestCluster(t *testing.T) func(t *testing.T) {
   188  	// vars
   189  	var err error
   190  	e := ecs.ECS{}
   191  	s := service.NewService()
   192  	controller := api.Controller{}
   193  	clusterName := bootstrapFlags.ClusterName
   194  
   195  	// change cur dir
   196  	err = os.Chdir("..")
   197  	if err != nil {
   198  		t.Errorf("Couldn't change directory")
   199  		return shutdown
   200  	}
   201  
   202  	err = controller.Bootstrap(bootstrapFlags)
   203  	if err != nil {
   204  		t.Errorf("Couldn't spin up cluster: %v", err.Error())
   205  		return shutdown
   206  	}
   207  
   208  	// deploy (3 times: one time to create, one to update and one with different layout)
   209  	var deployRes, deployRes2 *service.DeployResult
   210  	for y := 0; y < 3; y++ {
   211  		s.ServiceName = "integrationtest-default"
   212  		if y == 0 || y == 2 {
   213  			fmt.Println("==> Deploying first ecs service <==")
   214  			deployRes, err = controller.Deploy(s.ServiceName, ecsDefault)
   215  		} else {
   216  			fmt.Println("==> Deploying ecs service with changes <==")
   217  			deployRes, err = controller.Deploy(s.ServiceName, ecsDefaultWithChanges)
   218  		}
   219  		if err != nil {
   220  			t.Errorf("Error: %v\n", err)
   221  			// can't recover from this
   222  			return teardown
   223  		}
   224  		fmt.Printf("Deployed %v with task definition %v\n", deployRes.ServiceName, deployRes.TaskDefinitionArn)
   225  
   226  		var deployed bool
   227  		for i := 0; i < 30 && !deployed; i++ {
   228  			dd, err := s.GetDeployment(s.ServiceName, deployRes.DeploymentTime.Format("2006-01-02T15:04:05.999999999Z"))
   229  			if err != nil {
   230  				t.Errorf("Error: %v\n", err)
   231  			}
   232  			if dd != nil && dd.Status == "success" {
   233  				deployed = true
   234  			} else {
   235  				fmt.Printf("Waiting for deploy %v to have status success (latest status: %v)\n", s.ServiceName, dd.Status)
   236  				time.Sleep(30 * time.Second)
   237  			}
   238  		}
   239  		if !deployed {
   240  			fmt.Println("Couldn't deploy service")
   241  			return teardown
   242  		}
   243  	}
   244  
   245  	// deploy an update with healthchecks that fail and observe rolling back
   246  	fmt.Println("==> Deploying ecs service that fails <==")
   247  	deployRes2, err = controller.Deploy(s.ServiceName, ecsDefaultFailingHealthCheck)
   248  	var deployed bool
   249  	for i := 0; i < 30 && !deployed; i++ {
   250  		dd, err := s.GetDeployment(s.ServiceName, deployRes2.DeploymentTime.Format("2006-01-02T15:04:05.999999999Z"))
   251  		if err != nil {
   252  			t.Errorf("Error: %v\n", err)
   253  		}
   254  		if dd != nil && dd.Status != "running" {
   255  			deployed = true
   256  		} else {
   257  			fmt.Printf("Waiting for deploy to be rolled back (latest status: %v)\n", dd.Status)
   258  			time.Sleep(30 * time.Second)
   259  		}
   260  	}
   261  	settled := false
   262  	var runningService service.RunningService
   263  	for i := 0; i < 30 && !settled; i++ {
   264  		runningService, err = e.DescribeService(clusterName, s.ServiceName, false, false, false)
   265  		if err != nil {
   266  			t.Errorf("Error: %v\n", err)
   267  		}
   268  		if len(runningService.Deployments) == 1 && runningService.Deployments[0].TaskDefinition == deployRes.TaskDefinitionArn {
   269  			settled = true
   270  		} else {
   271  			fmt.Printf("Waiting for deployments to be 1 (currently %d) and task definition to be %v (currently %v)\n",
   272  				len(runningService.Deployments), deployRes.TaskDefinitionArn, runningService.Deployments[0].TaskDefinition)
   273  			time.Sleep(30 * time.Second)
   274  		}
   275  	}
   276  	if !settled {
   277  		t.Errorf("Error: Rollback didn't happen: wrong task definition (expected: %v): %+v\n", deployRes.TaskDefinitionArn, runningService.Deployments)
   278  	} else {
   279  		fmt.Println("Rolled back")
   280  	}
   281  
   282  	fmt.Println("Waiting before teardown (or ctrl+c)")
   283  	time.Sleep(120 * time.Second)
   284  
   285  	// return teardown
   286  	return teardown
   287  }
   288  func teardown(t *testing.T) {
   289  	controller := api.Controller{}
   290  	err := controller.DeleteCluster(bootstrapFlags)
   291  	if err != nil {
   292  		t.Errorf("Error: %v\n", err)
   293  	}
   294  }
   295  func shutdown(t *testing.T) {
   296  	fmt.Println("Shutting down without teardown")
   297  }