1package awsat 2 3import ( 4 "errors" 5 "testing" 6 7 "github.com/aws/aws-sdk-go/aws/awserr" 8 "github.com/aws/aws-sdk-go/service/ecs" 9) 10 11func TestContainerTask(t *testing.T) { 12 t.Run("start", func(t *testing.T) { 13 t.Run("service", func(t *testing.T) { 14 Template("start containertask name=my-new-service cluster=my-cluster-name desired-count=3 type=service "+ 15 "role=arn:of:container:role deployment-name=prod loadbalancer.container-name=redis loadbalancer.container-port=6379 "+ 16 "loadbalancer.targetgroup=arn:of:my:targetgroup"). 17 Mock(&ecsMock{ 18 CreateServiceFunc: func(param0 *ecs.CreateServiceInput) (*ecs.CreateServiceOutput, error) { 19 return &ecs.CreateServiceOutput{ 20 Service: &ecs.Service{ServiceArn: String("arn:of:my:new:service")}, 21 }, nil 22 }, 23 }).ExpectInput("CreateService", &ecs.CreateServiceInput{ 24 TaskDefinition: String("my-new-service"), 25 Cluster: String("my-cluster-name"), 26 DesiredCount: Int64(3), 27 Role: String("arn:of:container:role"), 28 ServiceName: String("prod"), 29 LoadBalancers: []*ecs.LoadBalancer{ 30 { 31 ContainerName: String("redis"), 32 ContainerPort: Int64(6379), 33 TargetGroupArn: String("arn:of:my:targetgroup"), 34 }, 35 }, 36 }). 37 ExpectCommandResult("arn:of:my:new:service").ExpectCalls("CreateService").Run(t) 38 }) 39 t.Run("task", func(t *testing.T) { 40 Template("start containertask name=my-new-task cluster=my-cluster-name desired-count=3 type=task"). 41 Mock(&ecsMock{ 42 RunTaskFunc: func(param0 *ecs.RunTaskInput) (*ecs.RunTaskOutput, error) { 43 return &ecs.RunTaskOutput{ 44 Tasks: []*ecs.Task{{TaskArn: String("arn:of:new:task")}}, 45 }, nil 46 }, 47 }).ExpectInput("RunTask", &ecs.RunTaskInput{ 48 TaskDefinition: String("my-new-task"), 49 Cluster: String("my-cluster-name"), 50 Count: Int64(3), 51 }). 52 ExpectCommandResult("arn:of:new:task").ExpectCalls("RunTask").Run(t) 53 }) 54 }) 55 56 t.Run("stop", func(t *testing.T) { 57 t.Run("service", func(t *testing.T) { 58 Template("stop containertask cluster=my-cluster-name type=service deployment-name=prod"). 59 Mock(&ecsMock{ 60 DeleteServiceFunc: func(param0 *ecs.DeleteServiceInput) (*ecs.DeleteServiceOutput, error) { 61 return nil, nil 62 }, 63 }).ExpectInput("DeleteService", &ecs.DeleteServiceInput{ 64 Cluster: String("my-cluster-name"), 65 Service: String("prod"), 66 }).ExpectCalls("DeleteService").Run(t) 67 }) 68 69 t.Run("task", func(t *testing.T) { 70 Template("stop containertask cluster=my-cluster-name type=task run-arn=arn:task:to:stop"). 71 Mock(&ecsMock{ 72 StopTaskFunc: func(param0 *ecs.StopTaskInput) (*ecs.StopTaskOutput, error) { 73 return nil, nil 74 }, 75 }).ExpectInput("StopTask", &ecs.StopTaskInput{ 76 Cluster: String("my-cluster-name"), 77 Task: String("arn:task:to:stop"), 78 }).ExpectCalls("StopTask").Run(t) 79 }) 80 }) 81 82 t.Run("update", func(t *testing.T) { 83 Template("update containertask name=my-service cluster=my-cluster-name deployment-name=prod desired-count=5"). 84 Mock(&ecsMock{ 85 UpdateServiceFunc: func(param0 *ecs.UpdateServiceInput) (*ecs.UpdateServiceOutput, error) { 86 return nil, nil 87 }, 88 }).ExpectInput("UpdateService", &ecs.UpdateServiceInput{ 89 TaskDefinition: String("my-service"), 90 Cluster: String("my-cluster-name"), 91 Service: String("prod"), 92 DesiredCount: Int64(5), 93 }).ExpectCalls("UpdateService").Run(t) 94 }) 95 96 t.Run("attach", func(t *testing.T) { 97 t.Run("first container in task", func(t *testing.T) { 98 Template("attach containertask name=my-task container-name=redis image=redis/redis memory-hard-limit=128 command='redis --start --fake-param' env=User:Jdoe,DbPasswd:VERYSECRET privileged=true workdir=/home ports=6379,8080:80"). 99 Mock(&ecsMock{ 100 DescribeTaskDefinitionFunc: func(param0 *ecs.DescribeTaskDefinitionInput) (*ecs.DescribeTaskDefinitionOutput, error) { 101 return nil, awserr.New("ClientException", "unable to describe task definition", errors.New("task does not exist")) 102 }, 103 RegisterTaskDefinitionFunc: func(param0 *ecs.RegisterTaskDefinitionInput) (*ecs.RegisterTaskDefinitionOutput, error) { 104 return &ecs.RegisterTaskDefinitionOutput{TaskDefinition: &ecs.TaskDefinition{TaskDefinitionArn: String("arn:of:my:new:definition")}}, nil 105 }, 106 }).ExpectInput("DescribeTaskDefinition", &ecs.DescribeTaskDefinitionInput{ 107 TaskDefinition: String("my-task"), 108 }).ExpectInput("RegisterTaskDefinition", &ecs.RegisterTaskDefinitionInput{ 109 Family: String("my-task"), 110 ContainerDefinitions: []*ecs.ContainerDefinition{ 111 { 112 Name: String("redis"), 113 Image: String("redis/redis"), 114 Memory: Int64(128), 115 Command: []*string{String("redis"), String("--start"), String("--fake-param")}, 116 Environment: []*ecs.KeyValuePair{{Name: String("User"), Value: String("Jdoe")}, {Name: String("DbPasswd"), Value: String("VERYSECRET")}}, 117 Privileged: Bool(true), 118 WorkingDirectory: String("/home"), 119 PortMappings: []*ecs.PortMapping{{ContainerPort: Int64(6379)}, {ContainerPort: Int64(80), HostPort: Int64(8080)}}, 120 }, 121 }, 122 }).ExpectCommandResult("arn:of:my:new:definition").ExpectCalls("DescribeTaskDefinition", "RegisterTaskDefinition").Run(t) 123 }) 124 125 t.Run("more containers in existing task", func(t *testing.T) { 126 existingContainer := &ecs.ContainerDefinition{ 127 Name: String("redis"), 128 Image: String("redis/redis"), 129 Memory: Int64(128), 130 Command: []*string{String("redis"), String("--start"), String("--fake-param")}, 131 Environment: []*ecs.KeyValuePair{{Name: String("User"), Value: String("Jdoe")}, {Name: String("DbPasswd"), Value: String("VERYSECRET")}}, 132 Privileged: Bool(true), 133 WorkingDirectory: String("/home"), 134 PortMappings: []*ecs.PortMapping{{ContainerPort: Int64(6379)}, {ContainerPort: Int64(80), HostPort: Int64(8080)}}, 135 } 136 137 Template("attach containertask name=my-task container-name=postgresql image=postgresql memory-hard-limit=64 command=postgresql,--port,3306 ports=3306:3306/tcp"). 138 Mock(&ecsMock{ 139 DescribeTaskDefinitionFunc: func(param0 *ecs.DescribeTaskDefinitionInput) (*ecs.DescribeTaskDefinitionOutput, error) { 140 return &ecs.DescribeTaskDefinitionOutput{ 141 TaskDefinition: &ecs.TaskDefinition{ 142 ContainerDefinitions: []*ecs.ContainerDefinition{existingContainer}, 143 Family: String("my-task"), 144 NetworkMode: String("bridge"), 145 }, 146 }, nil 147 }, 148 RegisterTaskDefinitionFunc: func(param0 *ecs.RegisterTaskDefinitionInput) (*ecs.RegisterTaskDefinitionOutput, error) { 149 return &ecs.RegisterTaskDefinitionOutput{TaskDefinition: &ecs.TaskDefinition{TaskDefinitionArn: String("arn:of:my:updated:definition")}}, nil 150 }, 151 }).ExpectInput("DescribeTaskDefinition", &ecs.DescribeTaskDefinitionInput{ 152 TaskDefinition: String("my-task"), 153 }).ExpectInput("RegisterTaskDefinition", &ecs.RegisterTaskDefinitionInput{ 154 ContainerDefinitions: []*ecs.ContainerDefinition{ 155 existingContainer, 156 { 157 Name: String("postgresql"), 158 Image: String("postgresql"), 159 Memory: Int64(64), 160 Command: []*string{String("postgresql"), String("--port"), String("3306")}, 161 PortMappings: []*ecs.PortMapping{{ContainerPort: Int64(3306), HostPort: Int64(3306), Protocol: String("tcp")}}, 162 }, 163 }, 164 Family: String("my-task"), 165 NetworkMode: String("bridge"), 166 }).ExpectCommandResult("arn:of:my:updated:definition").ExpectCalls("DescribeTaskDefinition", "RegisterTaskDefinition").Run(t) 167 }) 168 }) 169 170 t.Run("detach", func(t *testing.T) { 171 container1Def := &ecs.ContainerDefinition{ 172 Name: String("redis"), 173 Image: String("redis/redis"), 174 } 175 container2Def := &ecs.ContainerDefinition{ 176 Name: String("posgresql"), 177 } 178 t.Run("at least 2 containers in task", func(t *testing.T) { 179 Template("detach containertask name=my-task container-name=posgresql"). 180 Mock(&ecsMock{ 181 DescribeTaskDefinitionFunc: func(param0 *ecs.DescribeTaskDefinitionInput) (*ecs.DescribeTaskDefinitionOutput, error) { 182 return &ecs.DescribeTaskDefinitionOutput{ 183 TaskDefinition: &ecs.TaskDefinition{ 184 ContainerDefinitions: []*ecs.ContainerDefinition{container1Def, container2Def}, 185 Family: String("my-task"), 186 }, 187 }, nil 188 }, 189 RegisterTaskDefinitionFunc: func(param0 *ecs.RegisterTaskDefinitionInput) (*ecs.RegisterTaskDefinitionOutput, error) { 190 return nil, nil 191 }, 192 }).ExpectInput("DescribeTaskDefinition", &ecs.DescribeTaskDefinitionInput{ 193 TaskDefinition: String("my-task"), 194 }).ExpectInput("RegisterTaskDefinition", &ecs.RegisterTaskDefinitionInput{ 195 ContainerDefinitions: []*ecs.ContainerDefinition{container1Def}, 196 Family: String("my-task"), 197 }).ExpectCalls("DescribeTaskDefinition", "RegisterTaskDefinition").Run(t) 198 }) 199 200 t.Run("last container in task", func(t *testing.T) { 201 Template("detach containertask name=my-task container-name=redis/redis"). 202 Mock(&ecsMock{ 203 DescribeTaskDefinitionFunc: func(param0 *ecs.DescribeTaskDefinitionInput) (*ecs.DescribeTaskDefinitionOutput, error) { 204 return &ecs.DescribeTaskDefinitionOutput{ 205 TaskDefinition: &ecs.TaskDefinition{ 206 ContainerDefinitions: []*ecs.ContainerDefinition{container1Def}, 207 Family: String("my-task"), 208 TaskDefinitionArn: String("arn:my-task-to-deregister"), 209 }, 210 }, nil 211 }, 212 DeregisterTaskDefinitionFunc: func(param0 *ecs.DeregisterTaskDefinitionInput) (*ecs.DeregisterTaskDefinitionOutput, error) { 213 return nil, nil 214 }, 215 }).ExpectInput("DescribeTaskDefinition", &ecs.DescribeTaskDefinitionInput{ 216 TaskDefinition: String("my-task"), 217 }).ExpectInput("DeregisterTaskDefinition", &ecs.DeregisterTaskDefinitionInput{ 218 TaskDefinition: String("arn:my-task-to-deregister"), 219 }).ExpectCalls("DescribeTaskDefinition", "DeregisterTaskDefinition").Run(t) 220 }) 221 }) 222 223 t.Run("delete", func(t *testing.T) { 224 Template("delete containertask name=my-task-to-delete all-versions=true"). 225 Mock(&ecsMock{ 226 ListTaskDefinitionsFunc: func(param0 *ecs.ListTaskDefinitionsInput) (*ecs.ListTaskDefinitionsOutput, error) { 227 return &ecs.ListTaskDefinitionsOutput{TaskDefinitionArns: []*string{String("arn:of:task:to:delete")}}, nil 228 }, 229 DeregisterTaskDefinitionFunc: func(param0 *ecs.DeregisterTaskDefinitionInput) (*ecs.DeregisterTaskDefinitionOutput, error) { 230 return nil, nil 231 }, 232 }).ExpectInput("ListTaskDefinitions", &ecs.ListTaskDefinitionsInput{ 233 FamilyPrefix: String("my-task-to-delete"), 234 }).ExpectInput("DeregisterTaskDefinition", &ecs.DeregisterTaskDefinitionInput{ 235 TaskDefinition: String("arn:of:task:to:delete"), 236 }).ExpectCalls("ListTaskDefinitions", "DeregisterTaskDefinition").Run(t) 237 238 }) 239 240} 241