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 }