cloudformation_test.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. package backend
  2. import (
  3. "fmt"
  4. "reflect"
  5. "testing"
  6. "github.com/aws/aws-sdk-go/service/elbv2"
  7. "github.com/awslabs/goformation/v4/cloudformation"
  8. "github.com/awslabs/goformation/v4/cloudformation/ec2"
  9. "github.com/awslabs/goformation/v4/cloudformation/ecs"
  10. "github.com/awslabs/goformation/v4/cloudformation/elasticloadbalancingv2"
  11. "github.com/awslabs/goformation/v4/cloudformation/iam"
  12. "github.com/compose-spec/compose-go/cli"
  13. "github.com/compose-spec/compose-go/loader"
  14. "github.com/compose-spec/compose-go/types"
  15. "github.com/docker/ecs-plugin/pkg/compose"
  16. "gotest.tools/v3/assert"
  17. "gotest.tools/v3/golden"
  18. )
  19. func TestSimpleConvert(t *testing.T) {
  20. project := load(t, "testdata/input/simple-single-service.yaml")
  21. result := convertResultAsString(t, project)
  22. expected := "simple/simple-cloudformation-conversion.golden"
  23. golden.Assert(t, result, expected)
  24. }
  25. func TestRolePolicy(t *testing.T) {
  26. template := convertYaml(t, "test", `
  27. version: "3"
  28. services:
  29. foo:
  30. image: hello_world
  31. x-aws-pull_credentials: "secret"
  32. `)
  33. role := template.Resources["FooTaskExecutionRole"].(*iam.Role)
  34. assert.Check(t, role != nil)
  35. assert.Check(t, role.ManagedPolicyArns[0] == ECSTaskExecutionPolicy)
  36. assert.Check(t, role.ManagedPolicyArns[1] == ECRReadOnlyPolicy)
  37. // We expect an extra policy has been created for x-aws-pull_credentials
  38. assert.Check(t, len(role.Policies) == 1)
  39. policy := role.Policies[0].PolicyDocument.(*PolicyDocument)
  40. expected := []string{"secretsmanager:GetSecretValue", "ssm:GetParameters", "kms:Decrypt"}
  41. assert.DeepEqual(t, expected, policy.Statement[0].Action)
  42. assert.DeepEqual(t, []string{"secret"}, policy.Statement[0].Resource)
  43. }
  44. func TestMapNetworksToSecurityGroups(t *testing.T) {
  45. template := convertYaml(t, "test", `
  46. version: "3"
  47. services:
  48. test:
  49. image: hello_world
  50. networks:
  51. - front-tier
  52. - back-tier
  53. networks:
  54. front-tier:
  55. name: public
  56. back-tier:
  57. internal: true
  58. `)
  59. assert.Check(t, template.Resources["TestPublicNetwork"] != nil)
  60. assert.Check(t, template.Resources["TestBacktierNetwork"] != nil)
  61. assert.Check(t, template.Resources["TestBacktierNetworkIngress"] != nil)
  62. ingress := template.Resources["TestPublicNetworkIngress"].(*ec2.SecurityGroupIngress)
  63. assert.Check(t, ingress != nil)
  64. assert.Check(t, ingress.SourceSecurityGroupId == cloudformation.Ref("TestPublicNetwork"))
  65. }
  66. func TestLoadBalancerTypeApplication(t *testing.T) {
  67. template := convertYaml(t, "test123456789009876543211234567890", `
  68. version: "3"
  69. services:
  70. test:
  71. image: nginx
  72. ports:
  73. - 80:80
  74. `)
  75. lb := template.Resources["TestLoadBalancer"].(*elasticloadbalancingv2.LoadBalancer)
  76. assert.Check(t, lb != nil)
  77. assert.Check(t, len(lb.Name) <= 32)
  78. assert.Check(t, lb.Type == elbv2.LoadBalancerTypeEnumApplication)
  79. assert.Check(t, len(lb.SecurityGroups) > 0)
  80. }
  81. func TestNoLoadBalancerIfNoPortExposed(t *testing.T) {
  82. template := convertYaml(t, "test", `
  83. version: "3"
  84. services:
  85. test:
  86. image: nginx
  87. foo:
  88. image: bar
  89. `)
  90. for _, r := range template.Resources {
  91. assert.Check(t, r.AWSCloudFormationType() != "AWS::ElasticLoadBalancingV2::TargetGroup")
  92. assert.Check(t, r.AWSCloudFormationType() != "AWS::ElasticLoadBalancingV2::Listener")
  93. assert.Check(t, r.AWSCloudFormationType() != "AWS::ElasticLoadBalancingV2::LoadBalancer")
  94. }
  95. }
  96. func TestServiceReplicas(t *testing.T) {
  97. template := convertYaml(t, "test", `
  98. version: "3"
  99. services:
  100. test:
  101. image: nginx
  102. deploy:
  103. replicas: 10
  104. `)
  105. s := template.Resources["TestService"].(*ecs.Service)
  106. assert.Check(t, s != nil)
  107. assert.Check(t, s.DesiredCount == 10)
  108. }
  109. func TestTaskSizeConvert(t *testing.T) {
  110. template := convertYaml(t, "test", `
  111. version: "3"
  112. services:
  113. test:
  114. image: nginx
  115. deploy:
  116. resources:
  117. limits:
  118. cpus: '0.5'
  119. memory: 2048M
  120. reservations:
  121. cpus: '0.5'
  122. memory: 2048M
  123. `)
  124. def := template.Resources["TestTaskDefinition"].(*ecs.TaskDefinition)
  125. assert.Equal(t, def.Cpu, "512")
  126. assert.Equal(t, def.Memory, "2048")
  127. template = convertYaml(t, "test", `
  128. version: "3"
  129. services:
  130. test:
  131. image: nginx
  132. deploy:
  133. resources:
  134. limits:
  135. cpus: '4'
  136. memory: 8192M
  137. reservations:
  138. cpus: '4'
  139. memory: 8192M
  140. `)
  141. def = template.Resources["TestTaskDefinition"].(*ecs.TaskDefinition)
  142. assert.Equal(t, def.Cpu, "4096")
  143. assert.Equal(t, def.Memory, "8192")
  144. }
  145. func TestTaskSizeConvertFailure(t *testing.T) {
  146. model := loadConfig(t, "test", `
  147. version: "3"
  148. services:
  149. test:
  150. image: nginx
  151. deploy:
  152. resources:
  153. limits:
  154. cpus: '0.5'
  155. memory: 2043248M
  156. `)
  157. _, err := Backend{}.Convert(model)
  158. assert.ErrorContains(t, err, "unable to find cpu/mem for the required resources")
  159. }
  160. func TestLoadBalancerTypeNetwork(t *testing.T) {
  161. template := convertYaml(t, "test", `
  162. version: "3"
  163. services:
  164. test:
  165. image: nginx
  166. ports:
  167. - 80:80
  168. - 88:88
  169. `)
  170. lb := template.Resources["TestLoadBalancer"].(*elasticloadbalancingv2.LoadBalancer)
  171. assert.Check(t, lb != nil)
  172. assert.Check(t, lb.Type == elbv2.LoadBalancerTypeEnumNetwork)
  173. }
  174. func TestServiceMapping(t *testing.T) {
  175. template := convertYaml(t, "test", `
  176. version: "3"
  177. services:
  178. test:
  179. image: "image"
  180. command: "command"
  181. entrypoint: "entrypoint"
  182. environment:
  183. - "FOO=BAR"
  184. cap_add:
  185. - SYS_PTRACE
  186. cap_drop:
  187. - SYSLOG
  188. init: true
  189. user: "user"
  190. working_dir: "working_dir"
  191. `)
  192. def := template.Resources["TestTaskDefinition"].(*ecs.TaskDefinition)
  193. container := def.ContainerDefinitions[0]
  194. assert.Equal(t, container.Image, "image")
  195. assert.Equal(t, container.Command[0], "command")
  196. assert.Equal(t, container.EntryPoint[0], "entrypoint")
  197. assert.Equal(t, get(container.Environment, "FOO"), "BAR")
  198. assert.Check(t, container.LinuxParameters.InitProcessEnabled)
  199. assert.Equal(t, container.LinuxParameters.Capabilities.Add[0], "SYS_PTRACE")
  200. assert.Equal(t, container.LinuxParameters.Capabilities.Drop[0], "SYSLOG")
  201. assert.Equal(t, container.User, "user")
  202. assert.Equal(t, container.WorkingDirectory, "working_dir")
  203. }
  204. func get(l []ecs.TaskDefinition_KeyValuePair, name string) string {
  205. for _, e := range l {
  206. if e.Name == name {
  207. return e.Value
  208. }
  209. }
  210. return ""
  211. }
  212. func TestResourcesHaveProjectTagSet(t *testing.T) {
  213. template := convertYaml(t, "test", `
  214. version: "3"
  215. services:
  216. test:
  217. image: nginx
  218. ports:
  219. - 80:80
  220. - 88:88
  221. `)
  222. for _, r := range template.Resources {
  223. tags := reflect.Indirect(reflect.ValueOf(r)).FieldByName("Tags")
  224. if !tags.IsValid() {
  225. continue
  226. }
  227. for i := 0; i < tags.Len(); i++ {
  228. k := tags.Index(i).FieldByName("Key").String()
  229. v := tags.Index(i).FieldByName("Value").String()
  230. if k == compose.ProjectTag {
  231. assert.Equal(t, v, "Test")
  232. }
  233. }
  234. }
  235. }
  236. func convertResultAsString(t *testing.T, project *types.Project) string {
  237. backend, err := NewBackend("", "")
  238. assert.NilError(t, err)
  239. result, err := backend.Convert(project)
  240. assert.NilError(t, err)
  241. resultAsJSON, err := result.JSON()
  242. assert.NilError(t, err)
  243. return fmt.Sprintf("%s\n", string(resultAsJSON))
  244. }
  245. func load(t *testing.T, paths ...string) *types.Project {
  246. options := cli.ProjectOptions{
  247. Name: t.Name(),
  248. ConfigPaths: paths,
  249. }
  250. project, err := cli.ProjectFromOptions(&options)
  251. assert.NilError(t, err)
  252. return project
  253. }
  254. func convertYaml(t *testing.T, name string, yaml string) *cloudformation.Template {
  255. model := loadConfig(t, name, yaml)
  256. template, err := Backend{}.Convert(model)
  257. assert.NilError(t, err)
  258. return template
  259. }
  260. func loadConfig(t *testing.T, name string, yaml string) *types.Project {
  261. dict, err := loader.ParseYAML([]byte(yaml))
  262. assert.NilError(t, err)
  263. model, err := loader.Load(types.ConfigDetails{
  264. ConfigFiles: []types.ConfigFile{
  265. {Config: dict},
  266. },
  267. }, func(options *loader.Options) {
  268. options.Name = "Test"
  269. })
  270. assert.NilError(t, err)
  271. return model
  272. }