rails_template_18f 1.3.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +64 -61
  5. data/README.md +11 -16
  6. data/lib/generators/rails_template18f/active_storage/active_storage_generator.rb +7 -6
  7. data/lib/generators/rails_template18f/circleci/circleci_generator.rb +28 -27
  8. data/lib/generators/rails_template18f/circleci/templates/Dockerfile.ci.tt +0 -1
  9. data/lib/generators/rails_template18f/circleci/templates/circleci/config.yml.tt +162 -163
  10. data/lib/generators/rails_template18f/cloud_gov_config/templates/app/models/cloud_gov_config.rb +9 -1
  11. data/lib/generators/rails_template18f/github_actions/github_actions_generator.rb +31 -30
  12. data/lib/generators/rails_template18f/github_actions/templates/github/actions/compile-assets/action.yml +50 -0
  13. data/lib/generators/rails_template18f/github_actions/templates/github/actions/setup-project/action.yml.tt +4 -8
  14. data/lib/generators/rails_template18f/github_actions/templates/github/dependabot.yml.tt +2 -4
  15. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-production.yml +74 -0
  16. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-staging.yml +74 -0
  17. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/owasp-daily-scan.yml.tt +10 -1
  18. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/owasp-scan.yml.tt +1 -1
  19. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/pa11y.yml.tt +2 -2
  20. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/terraform-production.yml +49 -4
  21. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/terraform-staging.yml +49 -4
  22. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/validate-ssp.yml +2 -2
  23. data/lib/generators/rails_template18f/gitlab_ci/gitlab_ci_generator.rb +147 -0
  24. data/lib/generators/rails_template18f/gitlab_ci/templates/gitlab/node.yml.tt +11 -0
  25. data/lib/generators/rails_template18f/gitlab_ci/templates/gitlab/rails.yml +75 -0
  26. data/lib/generators/rails_template18f/gitlab_ci/templates/gitlab/ruby.yml +7 -0
  27. data/lib/generators/rails_template18f/gitlab_ci/templates/gitlab/terraform.yml +28 -0
  28. data/lib/generators/rails_template18f/gitlab_ci/templates/gitlab-ci.yml.tt +212 -0
  29. data/lib/generators/rails_template18f/i18n_js/i18n_js_generator.rb +2 -2
  30. data/lib/generators/rails_template18f/i18n_js/templates/app/javascript/{i18n.js → i18n/index.js} +1 -1
  31. data/lib/generators/rails_template18f/i18n_js/templates/config/i18n-js.yml +1 -1
  32. data/lib/generators/rails_template18f/newrelic/newrelic_generator.rb +4 -2
  33. data/lib/generators/rails_template18f/public_egress/public_egress_generator.rb +71 -55
  34. data/lib/generators/rails_template18f/sidekiq/sidekiq_generator.rb +8 -17
  35. data/lib/generators/rails_template18f/terraform/templates/full_bootstrap/imports.tf.tftpl +25 -0
  36. data/lib/generators/rails_template18f/terraform/templates/full_bootstrap/main.tf.tt +159 -0
  37. data/lib/generators/rails_template18f/terraform/templates/sandbox_bootstrap/imports.tf.tftpl +10 -0
  38. data/lib/generators/rails_template18f/terraform/templates/sandbox_bootstrap/main.tf.tt +117 -0
  39. data/lib/generators/rails_template18f/terraform/templates/terraform/README.md.tt +69 -95
  40. data/lib/generators/rails_template18f/terraform/templates/terraform/app.tf.tt +57 -0
  41. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/apply.sh +15 -0
  42. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/templates/backend_config.tftpl +8 -0
  43. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/templates/bot_secrets.tftpl +5 -0
  44. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/users.auto.tfvars +5 -0
  45. data/lib/generators/rails_template18f/terraform/templates/terraform/main.tf.tt +117 -0
  46. data/lib/generators/rails_template18f/terraform/templates/terraform/production.tfvars.tt +13 -0
  47. data/lib/generators/rails_template18f/terraform/templates/terraform/providers.tf.tt +18 -0
  48. data/lib/generators/rails_template18f/terraform/templates/terraform/staging.tfvars.tt +8 -0
  49. data/lib/generators/rails_template18f/terraform/templates/terraform/terraform.sh.tt +95 -0
  50. data/lib/generators/rails_template18f/terraform/templates/terraform/variables.tf.tt +98 -0
  51. data/lib/generators/rails_template18f/terraform/terraform_generator.rb +12 -4
  52. data/lib/rails_template18f/generators/cloud_gov_options.rb +9 -6
  53. data/lib/rails_template18f/generators/cloud_gov_parsing.rb +7 -7
  54. data/lib/rails_template18f/generators.rb +0 -1
  55. data/lib/rails_template18f/version.rb +1 -1
  56. data/rails-template-18f.gemspec +4 -4
  57. data/railsrc +4 -2
  58. data/railsrc-hotwire +4 -2
  59. data/template.rb +80 -82
  60. data/templates/README.md.tt +21 -8
  61. data/templates/app/assets/stylesheets/uswds-settings.scss +3 -2
  62. data/templates/app/views/application/_header.html.erb +1 -1
  63. data/templates/app/views/application/_usa_banner.html.erb +3 -3
  64. data/templates/bin/ops/create_service_account.sh.tt +20 -11
  65. data/templates/bin/ops/destroy_service_account.sh.tt +3 -3
  66. data/templates/browserslistrc +1 -2
  67. data/templates/doc/compliance/TODO.md +1 -4
  68. data/templates/{pa11yci.js → pa11yci.js.tt} +5 -0
  69. metadata +36 -35
  70. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-production.yml.tt +0 -50
  71. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-staging.yml.tt +0 -50
  72. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/import.sh +0 -13
  73. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/main.tf.tt +0 -22
  74. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/providers.tf +0 -16
  75. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/run.sh.tt +0 -40
  76. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/teardown_creds.sh.tt +0 -5
  77. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/variables.tf +0 -2
  78. data/lib/generators/rails_template18f/terraform/templates/terraform/production/main.tf.tt +0 -90
  79. data/lib/generators/rails_template18f/terraform/templates/terraform/production/providers.tf +0 -23
  80. data/lib/generators/rails_template18f/terraform/templates/terraform/production/variables.tf +0 -2
  81. data/lib/generators/rails_template18f/terraform/templates/terraform/staging/main.tf.tt +0 -70
  82. data/lib/generators/rails_template18f/terraform/templates/terraform/staging/providers.tf +0 -23
  83. data/lib/generators/rails_template18f/terraform/templates/terraform/staging/variables.tf +0 -2
  84. data/lib/rails_template18f/generators/pipeline_options.rb +0 -18
  85. data/templates/app/assets/images/uswds.js +0 -6
  86. data/templates/config/deployment/production.yml +0 -3
  87. data/templates/config/deployment/staging.yml +0 -3
  88. data/templates/manifest.yml.tt +0 -17
@@ -0,0 +1,212 @@
1
+ # Note that environment variables can be set in several places
2
+ # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
3
+
4
+ workflow:
5
+ rules:
6
+ - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
7
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
8
+ - if: $CI_COMMIT_BRANCH == "production"
9
+
10
+ stages:
11
+ - build
12
+ - test
13
+ - deploy
14
+
15
+ variables:
16
+ POSTGRES_DB: <%= app_name %>_test
17
+ POSTGRES_PASSWORD: not-actually-secret
18
+ POSTGRES_VERSION: <%= postgres_version %>
19
+ RUBY_VERSION: <%= RUBY_VERSION %>
20
+
21
+ include:
22
+ - local: ".gitlab/ruby.yml"
23
+ - local: ".gitlab/node.yml"
24
+ - local: ".gitlab/rails.yml"
25
+ - local: ".gitlab/terraform.yml"
26
+
27
+ default:
28
+ image: "ruby:${RUBY_VERSION}"
29
+ before_script:
30
+ - !reference [.setup-ruby]
31
+ cache:
32
+ - !reference [.cache-dependencies, cache]
33
+
34
+ build-project:
35
+ stage: build
36
+ extends: [.cache-dependencies, .setup-languages]
37
+ cache:
38
+ policy: pull-push
39
+ script:
40
+ - !reference [.bundle-install]
41
+ - !reference [.yarn-install]
42
+ - bin/rake assets:precompile
43
+ artifacts:
44
+ expire_in: 1 hour
45
+ paths:
46
+ - app/assets/builds
47
+ - public/assets
48
+ rules:
49
+ - if: $CI_PIPELINE_SOURCE != "schedule"
50
+
51
+ brakeman-scan:
52
+ stage: test
53
+ script:
54
+ - bin/brakeman --no-pager --ensure-ignore-notes -f sarif -o output.sarif.json
55
+ artifacts:
56
+ when: always
57
+ expose_as: "Brakeman results"
58
+ paths:
59
+ - output.sarif.json
60
+
61
+ dependency_scanning:
62
+ stage: test
63
+ extends: .setup-languages
64
+ script:
65
+ - bin/rake bundler:audit
66
+ - bin/rake yarn:audit
67
+ - gem install cyclonedx-ruby
68
+ - cyclonedx-ruby -p . -o ruby_bom.xml
69
+ artifacts:
70
+ expose_as: "Ruby SBOM"
71
+ paths:
72
+ - ruby_bom.xml
73
+
74
+ rspec:
75
+ stage: test
76
+ extends: .setup-project
77
+ script:
78
+ - bundle exec rspec
79
+ rules:
80
+ - if: $CI_PIPELINE_SOURCE != "schedule"
81
+
82
+ pa11y_scan:
83
+ stage: test
84
+ extends: .run-server
85
+ script:
86
+ - !reference [.install-puppet-deps]
87
+ - yarn run pa11y-ci -c pa11yci.js
88
+ rules:
89
+ - if: $CI_PIPELINE_SOURCE != "schedule"
90
+
91
+ owasp_scan:
92
+ extends: .owasp:setup
93
+ script:
94
+ - /zap/zap-baseline.py -t http://localhost:3000 -c zap.conf -I -r zap_report.html
95
+ rules:
96
+ - if: $CI_PIPELINE_SOURCE != "schedule"
97
+
98
+ owasp_daily_scan:
99
+ extends: .owasp:setup
100
+ script:
101
+ - /zap/zap-full-scan.py -t http://localhost:3000 -c zap.conf -I -r zap_report.html
102
+ rules:
103
+ - if: $CI_PIPELINE_SOURCE == "schedule"
104
+
105
+ terraform:fmt:
106
+ stage: test
107
+ extends: .terraform:setup
108
+ script:
109
+ - terraform fmt -check -recursive .
110
+
111
+ terraform:validate:
112
+ stage: test
113
+ extends: .terraform:setup
114
+ script:
115
+ - terraform validate
116
+
117
+ terraform:assets:staging:
118
+ extends: .assets:builder
119
+ cache:
120
+ - !reference [.cache-dependencies, cache]
121
+ - key: staging-assets
122
+ unprotect: true
123
+ paths:
124
+ - public/assets
125
+ - app/assets/builds
126
+ policy: $CACHE_POLICY
127
+ variables:
128
+ RAILS_ENV: staging
129
+ rules:
130
+ - if: $CI_PIPELINE_SOURCE == "schedule"
131
+ when: never
132
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
133
+ variables:
134
+ CACHE_POLICY: pull-push
135
+ - variables:
136
+ CACHE_POLICY: pull
137
+ <% if terraform_manage_spaces? %>
138
+ terraform:assets:production:
139
+ extends: .assets:builder
140
+ cache:
141
+ - !reference [.cache-dependencies, cache]
142
+ - key: production-assets
143
+ paths:
144
+ - public/assets
145
+ - app/assets/builds
146
+ policy: $CACHE_POLICY
147
+ variables:
148
+ RAILS_ENV: production
149
+ rules:
150
+ - if: $CI_COMMIT_BRANCH == "production"
151
+ variables:
152
+ CACHE_POLICY: pull-push
153
+ - if: $CI_PIPELINE_SOURCE != "schedule"
154
+ variables:
155
+ CACHE_POLICY: pull
156
+ <% end %>
157
+ terraform:plan:staging:
158
+ extends:
159
+ - .terraform:setup
160
+ - .terraform:variables:staging
161
+ needs: ["terraform:assets:staging"]
162
+ script:
163
+ - apk add zip
164
+ - terraform plan -out=staging_plan.out -var-file=staging.tfvars -var rails_master_key=$RAILS_MASTER_KEY -var cf_user=$CF_USERNAME
165
+ artifacts:
166
+ paths:
167
+ - terraform/staging_plan.out
168
+ - terraform/dist
169
+
170
+ terraform:apply:staging:
171
+ extends:
172
+ - .terraform:setup
173
+ - .terraform:variables:staging
174
+ needs:
175
+ - terraform:plan:staging
176
+ - terraform:assets:staging
177
+ script:
178
+ - apk add zip
179
+ - terraform apply -var-file=staging.tfvars -var rails_master_key=$RAILS_MASTER_KEY -var cf_user=$CF_USERNAME staging_plan.out
180
+ rules:
181
+ - if: $CI_PIPELINE_SOURCE == "schedule"
182
+ when: never
183
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
184
+ <% if terraform_manage_spaces? %>
185
+ terraform:plan:production:
186
+ extends:
187
+ - .terraform:setup
188
+ - .terraform:variables:production
189
+ needs: ["terraform:assets:production"]
190
+ script:
191
+ - apk add zip
192
+ - terraform plan -out=production_plan.out -var-file=production.tfvars -var rails_master_key=$PRODUCTION_RAILS_MASTER_KEY -var cf_user=$CF_USERNAME
193
+ artifacts:
194
+ paths:
195
+ - terraform/production_plan.out
196
+ - terraform/dist
197
+
198
+ terraform:apply:production:
199
+ extends:
200
+ - .terraform:setup
201
+ - .terraform:variables:production
202
+ needs:
203
+ - terraform:plan:production
204
+ - terraform:assets:production
205
+ script:
206
+ - apk add zip
207
+ - terraform apply -var-file=production.tfvars -var rails_master_key=$PRODUCTION_RAILS_MASTER_KEY -var cf_user=$CF_USERNAME production_plan.out
208
+ rules:
209
+ - if: $CI_PIPELINE_SOURCE == "schedule"
210
+ when: never
211
+ - if: $CI_COMMIT_BRANCH == "production"
212
+ when: manual<% end %>
@@ -34,7 +34,7 @@ module RailsTemplate18f
34
34
  def configure_asset_pipeline
35
35
  copy_file "lib/tasks/i18n.rake"
36
36
  copy_file "config/initializers/i18n_js.rb"
37
- copy_file "app/javascript/i18n.js"
37
+ copy_file "app/javascript/i18n/index.js"
38
38
  end
39
39
 
40
40
  def ignore_generated_file
@@ -42,7 +42,7 @@ module RailsTemplate18f
42
42
  append_to_file ".gitignore", <<~EOM
43
43
 
44
44
  # Generated by i18n-js
45
- /app/javascript/generated
45
+ /app/javascript/i18n/translations.json
46
46
  EOM
47
47
  end
48
48
  end
@@ -1,5 +1,5 @@
1
1
  import { I18n } from 'i18n-js';
2
- import translations from './generated/translations.json';
2
+ import translations from './translations.json';
3
3
 
4
4
  const userLocale = document.documentElement.lang;
5
5
 
@@ -1,4 +1,4 @@
1
1
  translations:
2
- - file: "app/javascript/generated/translations.json"
2
+ - file: "app/javascript/i18n/translations.json"
3
3
  patterns:
4
4
  - "*.js.*"
@@ -24,7 +24,7 @@ module RailsTemplate18f
24
24
 
25
25
  def install_gem
26
26
  return if gem_installed?("newrelic_rpm")
27
- gem "newrelic_rpm", "~> 9.12"
27
+ gem "newrelic_rpm", "~> 9.16"
28
28
  bundle_install
29
29
  end
30
30
 
@@ -33,7 +33,9 @@ module RailsTemplate18f
33
33
  end
34
34
 
35
35
  def update_cloud_gov_manifest
36
- insert_into_file "manifest.yml", " NEW_RELIC_LOG: stdout\n", before: /^\s+processes:/
36
+ insert_into_file file_path("terraform/app.tf"), <<EOT, after: "environment = {\n"
37
+ NEW_RELIC_LOG = "stdout"
38
+ EOT
37
39
  end
38
40
 
39
41
  def update_readme
@@ -22,44 +22,38 @@ module RailsTemplate18f
22
22
  end
23
23
 
24
24
  def use_terraform_module
25
- append_to_file file_path("terraform/staging/main.tf"), terraform_module
26
- append_to_file file_path("terraform/production/main.tf"), terraform_module
25
+ append_to_file file_path("terraform/main.tf"), terraform_module
26
+ append_to_file file_path("terraform/variables.tf"), <<~EOT
27
+ variable "egress_allowlist" {
28
+ type = set(string)
29
+ default = []
30
+ description = "The set of hostnames that the application is allowed to connect to"
31
+ }
32
+ EOT
33
+ insert_into_file file_path("terraform/app.tf"), <<EOT, after: "environment = {\n"
34
+ no_proxy = "apps.internal,s3-fips.us-gov-west-1.amazonaws.com"
35
+ EOT
36
+ insert_into_file file_path("terraform/app.tf"), <<EOT, after: "service_bindings = [\n"
37
+ { service_instance = "egress-proxy-${var.env}-credentials" },
38
+ EOT
39
+ insert_into_file file_path("terraform/app.tf"), <<EOT, after: "depends_on = [\n"
40
+ cloudfoundry_service_instance.egress_proxy_credentials,
41
+ EOT
27
42
  end
28
43
 
29
- def add_to_deploy_steps
30
- if file_exists?(".github/workflows/deploy-staging.yml")
31
- insert_into_file ".github/workflows/deploy-staging.yml", <<EOD, before: " - name: Deploy app"
32
- - name: Set public egress
33
- uses: cloud-gov/cg-cli-tools@main
34
- with:
35
- cf_username: ${{ secrets.CF_USERNAME }}
36
- cf_password: ${{ secrets.CF_PASSWORD }}
37
- cf_org: #{cloud_gov_organization}
38
- cf_space: #{cloud_gov_staging_space}-egress
39
- cf_command: bind-security-group public_networks_egress $INPUT_CF_ORG --space $INPUT_CF_SPACE
40
- EOD
41
- end
42
- if file_exists?(".github/workflows/deploy-production.yml")
43
- insert_into_file ".github/workflows/deploy-production.yml", <<EOD, before: " - name: Deploy app"
44
- - name: Set public egress
45
- uses: cloud-gov/cg-cli-tools@main
46
- with:
47
- cf_username: ${{ secrets.CF_USERNAME }}
48
- cf_password: ${{ secrets.CF_PASSWORD }}
49
- cf_org: #{cloud_gov_organization}
50
- cf_space: #{cloud_gov_production_space}-egress
51
- cf_command: bind-security-group public_networks_egress $INPUT_CF_ORG --space $INPUT_CF_SPACE
52
- EOD
53
- end
54
- if file_exists?(".circleci/config.yml")
55
- insert_into_file ".circleci/config.yml", <<EOD, before: " name: Push application with deployment vars"
56
- name: Set public egress
57
- command: |
58
- cf bind-security-group public_networks_egress << parameters.cloudgov_org >> \
59
- --space << parameters.cloudgov_space >>-egress
60
- - run:
61
- EOD
62
- end
44
+ def setup_proxy_vars
45
+ create_file ".profile", <<~EOP unless file_exists?(".profile")
46
+ ##
47
+ # Cloud Foundry app initialization script
48
+ # https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#profile
49
+ ##
50
+
51
+ EOP
52
+ insert_into_file ".profile", <<~EOP
53
+ proxy_creds=$(echo "$VCAP_SERVICES" | jq --arg service_name "egress-proxy-$RAILS_ENV-credentials" '.[][] | select(.name == $service_name) | .credentials')
54
+ export http_proxy=$(echo "$proxy_creds" | jq --raw-output ".http_uri")
55
+ export https_proxy=$(echo "$proxy_creds" | jq --raw-output ".https_uri")
56
+ EOP
63
57
  end
64
58
 
65
59
  def update_readme
@@ -94,9 +88,10 @@ EOB
94
88
  ### Public Egress Proxy
95
89
 
96
90
  Traffic to be delivered to the public internet must be proxied through the [cg-egress-proxy](https://github.com/GSA-TTS/cg-egress-proxy) app. Hostnames that the app should be able to
97
- reach should be added to the `allowlist` terraform configuration in `terraform/staging/main.tf` and `terraform/production/main.tf`
91
+ reach should be added to the `egress_allowlist` terraform variable in `terraform/production.tfvars` and `terraform/staging.tfvars`
98
92
 
99
93
  See the [ruby troubleshooting doc](https://github.com/GSA-TTS/cg-egress-proxy/blob/main/docs/ruby.md) first if you have any problems making outbound connections through the proxy.
94
+
100
95
  README
101
96
  end
102
97
 
@@ -104,30 +99,51 @@ EOB
104
99
  <<~EOT
105
100
 
106
101
  module "egress_space" {
107
- source = "github.com/gsa-tts/terraform-cloudgov//cg_space?ref=v1.1.0"
108
-
109
- cf_org_name = local.cf_org_name
110
- cf_space_name = "${local.cf_space_name}-egress"
111
- # deployers should include any user or service account ID that will deploy the egress proxy
112
- deployers = [
113
- var.cf_user
114
- ]
102
+ source = "github.com/gsa-tts/terraform-cloudgov//cg_space?ref=v2.3.0"
103
+
104
+ cf_org_name = local.cf_org_name
105
+ cf_space_name = "${var.cf_space_name}-egress"
106
+ allow_ssh = var.allow_space_ssh
107
+ deployers = local.space_deployers
108
+ developers = var.space_developers
109
+ auditors = var.space_auditors
110
+ security_group_names = ["public_networks_egress"]
115
111
  }
116
112
 
117
113
  module "egress_proxy" {
118
- source = "github.com/gsa-tts/terraform-cloudgov//egress_proxy?ref=v1.1.0"
119
-
120
- cf_org_name = local.cf_org_name
121
- cf_space_name = module.egress_space.space_name
122
- client_space = local.cf_space_name
123
- name = "egress-proxy-${local.env}"
124
- # comment out allowlist if this module is being deployed before the app has ever been deployed
125
- allowlist = {
126
- "${local.app_name}-${local.env}" = []
127
- }
114
+ source = "github.com/gsa-tts/terraform-cloudgov//egress_proxy?ref=v2.3.0"
115
+
116
+ cf_org_name = local.cf_org_name
117
+ cf_egress_space = module.egress_space.space
118
+ name = "egress-proxy-${var.env}"
119
+ allowlist = var.egress_allowlist
128
120
  # depends_on line is needed only for initial creation and destruction. It should be commented out for updates to prevent unwanted cascading effects
129
121
  depends_on = [module.app_space, module.egress_space]
130
122
  }
123
+
124
+ resource "cloudfoundry_network_policy" "egress_routing" {
125
+ policies = [
126
+ {
127
+ source_app = cloudfoundry_app.app.id
128
+ destination_app = module.egress_proxy.app_id
129
+ port = module.egress_proxy.https_port
130
+ },
131
+ {
132
+ source_app = cloudfoundry_app.app.id
133
+ destination_app = module.egress_proxy.app_id
134
+ port = module.egress_proxy.http_port
135
+ }
136
+ ]
137
+ }
138
+
139
+ resource "cloudfoundry_service_instance" "egress_proxy_credentials" {
140
+ name = "egress-proxy-${var.env}-credentials"
141
+ space = module.app_space.space_id
142
+ type = "user-provided"
143
+ credentials = module.egress_proxy.json_credentials
144
+ # depends_on line is needed only for initial creation and destruction. It should be commented out for updates to prevent unwanted cascading effects
145
+ depends_on = [module.app_space]
146
+ }
131
147
  EOT
132
148
  end
133
149
  end
@@ -29,23 +29,14 @@ module RailsTemplate18f
29
29
 
30
30
  def configure_server_runner
31
31
  append_to_file "Procfile.dev", "worker: bundle exec sidekiq\n"
32
- insert_into_file "manifest.yml", indent(<<~EOYAML), after: /processes:$\n/
33
- - type: worker
34
- instances: ((worker_instances))
35
- memory: ((worker_memory))
36
- command: bundle exec sidekiq
37
- EOYAML
38
- insert_into_file "manifest.yml", "\n - #{app_name}-redis-((env))", after: "services:"
39
- inside "config/deployment" do
40
- append_to_file "staging.yml", <<~EOYAML
41
- worker_instances: 1
42
- worker_memory: 256M
43
- EOYAML
44
- append_to_file "production.yml", <<~EOYAML
45
- worker_instances: 1
46
- worker_memory: 512M
47
- EOYAML
48
- end
32
+ insert_into_file file_path("terraform/app.tf"), <<EOT, after: "processes = [\n"
33
+ {
34
+ type = "worker"
35
+ instances = var.worker_instances
36
+ memory = var.worker_memory
37
+ command = "bundle exec sidekiq"
38
+ },
39
+ EOT
49
40
  end
50
41
 
51
42
  def configure_active_job
@@ -0,0 +1,25 @@
1
+ # This file takes care of importing bootstrap
2
+ # resources onto a new developer's machine if needed
3
+ # import happens automatically on a normal ./apply.sh run
4
+
5
+ %{ for resource_name, id in import_map ~}
6
+ import {
7
+ to = ${resource_name}
8
+ id = "${id}"
9
+ }
10
+ %{ endfor ~}
11
+
12
+ locals {
13
+ developer_import_map = "${replace(jsonencode(developer_map), "\"", "\\\"")}"
14
+ manager_import_map = "${replace(jsonencode(manager_map), "\"", "\\\"")}"
15
+ }
16
+ import {
17
+ for_each = jsondecode(local.developer_import_map)
18
+ to = module.mgmt_space.cloudfoundry_space_role.developers[each.key]
19
+ id = each.value
20
+ }
21
+ import {
22
+ for_each = jsondecode(local.manager_import_map)
23
+ to = module.mgmt_space.cloudfoundry_space_role.managers[each.key]
24
+ id = each.value
25
+ }
@@ -0,0 +1,159 @@
1
+ terraform {
2
+ required_version = "~> 1.10"
3
+ required_providers {
4
+ cloudfoundry = {
5
+ source = "cloudfoundry/cloudfoundry"
6
+ version = "1.2.0"
7
+ }
8
+ }
9
+ }
10
+ # empty config will let terraform borrow cf-cli's auth
11
+ provider "cloudfoundry" {}
12
+
13
+ variable "terraform_users" {
14
+ type = set(string)
15
+ description = "The list of developer emails and service account usernames who should be granted access to retrieve state bucket credentials"
16
+
17
+ validation {
18
+ condition = length(var.terraform_users) > 0
19
+ error_message = "terraform_users must include at least the current user calling apply.sh"
20
+ }
21
+ }
22
+ variable "mgmt_space_name" {
23
+ type = string
24
+ default = "<%= cloud_gov_production_space %>-mgmt"
25
+ description = "The name of the mgmt space"
26
+ }
27
+ variable "create_bot_secrets_file" {
28
+ type = bool
29
+ default = false
30
+ description = "Flag whether to create secrets.cicd.tfvars file"
31
+ }
32
+
33
+ locals {
34
+ org_name = "<%= cloud_gov_organization %>"
35
+ # s3_plan_name should be basic when holding production data, though basic-sandbox will make early iterations easier
36
+ s3_plan_name = "basic"
37
+ }
38
+ module "mgmt_space" {
39
+ source = "github.com/gsa-tts/terraform-cloudgov//cg_space?ref=v2.3.0"
40
+
41
+ cf_org_name = local.org_name
42
+ cf_space_name = var.mgmt_space_name
43
+ developers = var.terraform_users
44
+ }
45
+
46
+ module "s3" {
47
+ source = "github.com/gsa-tts/terraform-cloudgov//s3?ref=v2.3.0"
48
+
49
+ cf_space_id = module.mgmt_space.space_id
50
+ name = "<%= app_name %>-terraform-state"
51
+ s3_plan_name = local.s3_plan_name
52
+ depends_on = [module.mgmt_space]
53
+ }
54
+
55
+ data "cloudfoundry_service_plans" "cg_service_account" {
56
+ name = "space-deployer"
57
+ service_offering_name = "cloud-gov-service-account"
58
+ }
59
+ locals {
60
+ sa_service_name = "<%= app_name %>-cicd-deployer"
61
+ sa_key_name = "cicd-deployer-access-key"
62
+ sa_bot_credentials = jsondecode(data.cloudfoundry_service_credential_binding.runner_sa_key.credential_bindings.0.credential_binding).credentials
63
+ sa_cf_username = nonsensitive(local.sa_bot_credentials.username)
64
+ sa_cf_password = local.sa_bot_credentials.password
65
+ }
66
+ resource "cloudfoundry_service_instance" "runner_service_account" {
67
+ name = local.sa_service_name
68
+ type = "managed"
69
+ space = module.mgmt_space.space_id
70
+ service_plan = data.cloudfoundry_service_plans.cg_service_account.service_plans.0.id
71
+ depends_on = [module.mgmt_space]
72
+ }
73
+ resource "cloudfoundry_service_credential_binding" "runner_sa_key" {
74
+ name = local.sa_key_name
75
+ service_instance = cloudfoundry_service_instance.runner_service_account.id
76
+ type = "key"
77
+ }
78
+ data "cloudfoundry_service_credential_binding" "runner_sa_key" {
79
+ name = local.sa_key_name
80
+ service_instance = cloudfoundry_service_instance.runner_service_account.id
81
+ depends_on = [cloudfoundry_service_credential_binding.runner_sa_key]
82
+ }
83
+ data "cloudfoundry_org" "org" {
84
+ name = local.org_name
85
+ }
86
+ data "cloudfoundry_user" "sa_user" {
87
+ name = local.sa_cf_username
88
+ }
89
+ resource "cloudfoundry_org_role" "sa_org_manager" {
90
+ user = data.cloudfoundry_user.sa_user.users.0.id
91
+ type = "organization_manager"
92
+ org = data.cloudfoundry_org.org.id
93
+ }
94
+
95
+ locals {
96
+ bucket_creds_key_name = "backend-state-bucket-creds"
97
+ }
98
+ resource "cloudfoundry_service_credential_binding" "bucket_creds" {
99
+ name = local.bucket_creds_key_name
100
+ service_instance = module.s3.bucket_id
101
+ type = "key"
102
+ }
103
+ data "cloudfoundry_service_credential_binding" "bucket_creds" {
104
+ name = local.bucket_creds_key_name
105
+ service_instance = module.s3.bucket_id
106
+ depends_on = [cloudfoundry_service_credential_binding.bucket_creds]
107
+ }
108
+
109
+ locals {
110
+ import_map = {
111
+ "module.mgmt_space.cloudfoundry_space.space" = module.mgmt_space.space_id
112
+ "module.s3.cloudfoundry_service_instance.bucket" = module.s3.bucket_id
113
+ "cloudfoundry_service_credential_binding.bucket_creds" = cloudfoundry_service_credential_binding.bucket_creds.id
114
+ "cloudfoundry_service_instance.runner_service_account" = cloudfoundry_service_instance.runner_service_account.id
115
+ "cloudfoundry_service_credential_binding.runner_sa_key" = cloudfoundry_service_credential_binding.runner_sa_key.id
116
+ "cloudfoundry_org_role.sa_org_manager" = cloudfoundry_org_role.sa_org_manager.id
117
+ }
118
+
119
+ recreate_state_template = templatefile("${path.module}/templates/imports.tf.tftpl", {
120
+ import_map = local.import_map,
121
+ developer_map = { for username, id in module.mgmt_space.developer_role_ids : username => id },
122
+ manager_map = { for username, id in module.mgmt_space.manager_role_ids : username => id }
123
+ })
124
+ }
125
+ resource "local_file" "recreate_script" {
126
+ content = local.recreate_state_template
127
+ filename = "${path.module}/imports.tf"
128
+ file_permission = "0644"
129
+ }
130
+
131
+ locals {
132
+ bucket_creds = jsondecode(data.cloudfoundry_service_credential_binding.bucket_creds.credential_bindings.0.credential_binding).credentials
133
+ backend_config = templatefile("${path.module}/templates/backend_config.tftpl", { creds = local.bucket_creds })
134
+ }
135
+ resource "local_sensitive_file" "bucket_creds" {
136
+ content = local.backend_config
137
+ filename = "${path.module}/../secrets.backend.tfvars"
138
+ file_permission = "0600"
139
+ }
140
+
141
+ resource "local_sensitive_file" "bot_secrets_file" {
142
+ count = (var.create_bot_secrets_file ? 1 : 0)
143
+ filename = "${path.module}/../secrets.cicd.tfvars"
144
+ file_permission = "0600"
145
+
146
+ content = templatefile("${path.module}/templates/bot_secrets.tftpl", {
147
+ service_name = local.sa_service_name,
148
+ key_name = local.sa_key_name,
149
+ username = local.sa_cf_username,
150
+ password = local.sa_cf_password
151
+ })
152
+ }
153
+
154
+ output "mgmt_space_id" {
155
+ value = module.mgmt_space.space_id
156
+ }
157
+ output "mgmt_org_id" {
158
+ value = data.cloudfoundry_org.org.id
159
+ }
@@ -0,0 +1,10 @@
1
+ # This file takes care of importing bootstrap
2
+ # resources onto a new developer's machine if needed
3
+ # import happens automatically on a normal ./apply.sh run
4
+
5
+ %{ for resource_name, id in import_map ~}
6
+ import {
7
+ to = ${resource_name}
8
+ id = "${id}"
9
+ }
10
+ %{ endfor ~}