cloudformation_test.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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 TestLoadBalancerTypeNetwork(t *testing.T) {
  146. template := convertYaml(t, "test", `
  147. version: "3"
  148. services:
  149. test:
  150. image: nginx
  151. ports:
  152. - 80:80
  153. - 88:88
  154. `)
  155. lb := template.Resources["TestLoadBalancer"].(*elasticloadbalancingv2.LoadBalancer)
  156. assert.Check(t, lb != nil)
  157. assert.Check(t, lb.Type == elbv2.LoadBalancerTypeEnumNetwork)
  158. }
  159. func TestServiceMapping(t *testing.T) {
  160. template := convertYaml(t, "test", `
  161. version: "3"
  162. services:
  163. test:
  164. image: "image"
  165. command: "command"
  166. entrypoint: "entrypoint"
  167. environment:
  168. - "FOO=BAR"
  169. cap_add:
  170. - SYS_PTRACE
  171. cap_drop:
  172. - SYSLOG
  173. init: true
  174. user: "user"
  175. working_dir: "working_dir"
  176. `)
  177. def := template.Resources["TestTaskDefinition"].(*ecs.TaskDefinition)
  178. container := def.ContainerDefinitions[0]
  179. assert.Equal(t, container.Image, "image")
  180. assert.Equal(t, container.Command[0], "command")
  181. assert.Equal(t, container.EntryPoint[0], "entrypoint")
  182. assert.Equal(t, get(container.Environment, "FOO"), "BAR")
  183. assert.Check(t, container.LinuxParameters.InitProcessEnabled)
  184. assert.Equal(t, container.LinuxParameters.Capabilities.Add[0], "SYS_PTRACE")
  185. assert.Equal(t, container.LinuxParameters.Capabilities.Drop[0], "SYSLOG")
  186. assert.Equal(t, container.User, "user")
  187. assert.Equal(t, container.WorkingDirectory, "working_dir")
  188. }
  189. func get(l []ecs.TaskDefinition_KeyValuePair, name string) string {
  190. for _, e := range l {
  191. if e.Name == name {
  192. return e.Value
  193. }
  194. }
  195. return ""
  196. }
  197. func TestResourcesHaveProjectTagSet(t *testing.T) {
  198. template := convertYaml(t, "test", `
  199. version: "3"
  200. services:
  201. test:
  202. image: nginx
  203. ports:
  204. - 80:80
  205. - 88:88
  206. `)
  207. for _, r := range template.Resources {
  208. tags := reflect.Indirect(reflect.ValueOf(r)).FieldByName("Tags")
  209. if !tags.IsValid() {
  210. continue
  211. }
  212. for i := 0; i < tags.Len(); i++ {
  213. k := tags.Index(i).FieldByName("Key").String()
  214. v := tags.Index(i).FieldByName("Value").String()
  215. if k == compose.ProjectTag {
  216. assert.Equal(t, v, "Test")
  217. }
  218. }
  219. }
  220. }
  221. func convertResultAsString(t *testing.T, project *types.Project) string {
  222. backend, err := NewBackend("", "")
  223. assert.NilError(t, err)
  224. result, err := backend.Convert(project)
  225. assert.NilError(t, err)
  226. resultAsJSON, err := result.JSON()
  227. assert.NilError(t, err)
  228. return fmt.Sprintf("%s\n", string(resultAsJSON))
  229. }
  230. func load(t *testing.T, paths ...string) *types.Project {
  231. options := cli.ProjectOptions{
  232. Name: t.Name(),
  233. ConfigPaths: paths,
  234. }
  235. project, err := cli.ProjectFromOptions(&options)
  236. assert.NilError(t, err)
  237. return project
  238. }
  239. func convertYaml(t *testing.T, name string, yaml string) *cloudformation.Template {
  240. dict, err := loader.ParseYAML([]byte(yaml))
  241. assert.NilError(t, err)
  242. model, err := loader.Load(types.ConfigDetails{
  243. ConfigFiles: []types.ConfigFile{
  244. {Config: dict},
  245. },
  246. }, func(options *loader.Options) {
  247. options.Name = "Test"
  248. })
  249. assert.NilError(t, err)
  250. template, err := Backend{}.Convert(model)
  251. assert.NilError(t, err)
  252. return template
  253. }