pcf_blue_green 0.4 → 0.22

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d915116064ddab4b0e3396a21efd70914a00bf98
4
- data.tar.gz: 050772c4e8252090b800ec435aba7e15e5e0d38f
3
+ metadata.gz: b40c3523b6e881e7a74b3e64dadecb349bb87acc
4
+ data.tar.gz: c2cf368391ca87ba2a63abf39dd49e14310e216a
5
5
  SHA512:
6
- metadata.gz: 2cbde7c30fee961af01a255d5dfd4f5351243d7f0bfe305fa2fb42dc751b0b52e9d09cd5500720da255e931d75a1f40022537bcc9a29075295220a034ac64c0d
7
- data.tar.gz: fa577b92629e52c89817bf0f0928348c24c50a1e578f206c5372d4a8de135686e7aa556427ff1374b0aaf3b0a927455f60e44d063dd86fdf2e35a534a47763c5
6
+ metadata.gz: 270933ab310cbd7c6e7f449db6d04499c8e0981dac4389478fc5f1110a042ad9fd42a7ea296bf9497a27fa9fe82e80f337caee0b075b6fefb9aef91829766e02
7
+ data.tar.gz: ab1420b3f69e87d7d5d239329e40cf547601b0d3d316f74d08c8a36337a813cf4231720b721e49c3c8316eeaeb51e3709044a0f5f1371ec8a4dc908b899dcaef
@@ -4,148 +4,8 @@ require 'shellwords'
4
4
  require "json"
5
5
 
6
6
  module PcfBlueGreen
7
- class BlueGreenDeploymentFailed < StandardError
7
+ class BlueGreenDeployFailed < StandardError
8
8
  end
9
-
10
- class Routing
11
-
12
- def initialize(url, user, pass, org, space, call_login = false, opts = {})
13
- @options = {:login => '--skip-ssl-validation'}.merge(opts)
14
- @url = url
15
- @user = user
16
- @pass = pass
17
- @org = org
18
- @space = space
19
- @login_options = @options[:login]
20
- login if call_login
21
- cmd = "echo $(gem which pcf_blue_green)"
22
- shell = Mixlib::ShellOut.new(cmd)
23
- shell.run_command
24
- gem_path = shell.stdout
25
- sdf_path = gem_path.gsub("pcf_blue_green.rb\n", "shared-deploy-functions.sh")
26
- @source_cmd = "source \'#{sdf_path}\'"
27
- end
28
-
29
- def cleanup_blue
30
- remap_routes
31
- status = bash("#{@source_cmd} && remove-blue")
32
- raise PcfBlueGreen::BlueGreenDeploymentFailed unless status
33
- remove_routes
34
- bash("#{@source_cmd} && announce-success")
35
- end
36
-
37
- def remove_routes
38
- removed_routes = []
39
- if entity = entity_for(ENV['green'])
40
- routes_stdout(entity)['resources'].each do |resource|
41
- domain_url = resource['entity']['domain_url']
42
- domain = domain_for(domain_url)
43
- if resource['entity']['path'].empty?
44
- cmd = "#{delete_route_command} #{domain} -n #{resource['entity']['host']} -f"
45
- else
46
- cmd = "#{delete_route_command} #{domain} -n #{resource['entity']['host']} --path #{resource['entity']['path']} -f"
47
- end
48
- puts cmd
49
- shell = Mixlib::ShellOut.new(cmd)
50
- shell.run_command
51
- removed_routes += shell.stdout.split("\n")
52
-
53
- unless shell.stderr.empty?
54
- puts shell.stderr
55
- raise PcfBlueGreen::BlueGreenDeploymentFailed
56
- end
57
- end
58
- end
59
- puts "Removed routes: #{removed_routes.uniq.join(', ')} "
60
- removed_routes.uniq.join(",\n")
61
- end
62
-
63
- def remap_routes
64
- puts "Remapping routes..."
65
- puts "cf_app is #{ENV['cf_app']}"
66
- puts "green is #{ENV['green']}"
67
- mapped_routes = []
68
- if entity = entity_for(ENV['cf_app'])
69
- routes_stdout(entity)['resources'].each do |resource|
70
- domain_url = resource['entity']['domain_url']
71
- domain = domain_for(domain_url)
72
- if resource['entity']['path'].empty?
73
- cmd = "#{map_route_command} #{ENV['green']} #{domain} -n #{resource['entity']['host']}"
74
- else
75
- cmd = "#{map_route_command} #{ENV['green']} #{domain} -n #{resource['entity']['host']} --path #{resource['entity']['path']}"
76
- end
77
- puts cmd
78
- shell = Mixlib::ShellOut.new(cmd)
79
- shell.run_command
80
- mapped_routes += shell.stdout.split("\n")
81
- unless shell.stderr.empty?
82
- puts shell.stderr
83
- raise PcfBlueGreen::BlueGreenDeploymentFailed
84
- end
85
- end
86
- end
87
- puts "Mappped routes: #{mapped_routes.uniq.join(', ')} "
88
- mapped_routes.uniq.join(",\n")
89
- end
90
-
91
- private
92
-
93
- def entity_for(app_name)
94
- app_data_cmd = "cf curl \"/v2/apps\" -X GET -H \"Content-Type: application/x-www-form-urlencoded\" -d 'q=name:#{app_name}'"
95
- app_data_shell = Mixlib::ShellOut.new(app_data_cmd)
96
- app_data_shell.run_command
97
- app_stdout = JSON.parse(app_data_shell.stdout)
98
- entity = app_stdout['resources'].first['entity']
99
- unless app_data_shell.stderr.empty?
100
- puts app_data_shell.stderr
101
- raise PcfBlueGreen::BlueGreenDeploymentFailed
102
- end
103
- entity
104
- end
105
-
106
- def routes_stdout(entity)
107
- routes_url = entity['routes_url']
108
- app_routes_cmd = "cf curl #{routes_url}"
109
- app_routes_shell = Mixlib::ShellOut.new(app_routes_cmd)
110
- app_routes_shell.run_command
111
- JSON.parse(app_routes_shell.stdout)
112
- end
113
-
114
- def domain_for(domain_url)
115
- app_domain_cmd = "cf curl #{domain_url}"
116
- app_domain_shell = Mixlib::ShellOut.new(app_domain_cmd)
117
- app_domain_shell.run_command
118
- domain_stdout = JSON.parse(app_domain_shell.stdout)
119
- domain_stdout['entity']['name']
120
- end
121
-
122
- def login
123
- puts "Logging in to #{@url}"
124
- cf_login = "cf login -a #{@url} -u #{@user} -p '#{@pass}' -o #{@org} -s #{@space} #{@login_options} > /dev/null"
125
- cmd = "#{cf_login}"
126
- shell = Mixlib::ShellOut.new(cmd)
127
- shell.run_command
128
- end
129
-
130
- def delete_route_command
131
- "cf delete-route"
132
- # cmd = "cf routes"
133
- # shell = Mixlib::ShellOut.new(cmd)
134
- # shell.run_command
135
- # shell.stdout
136
- end
137
-
138
- def map_route_command
139
- "cf map-route"
140
- end
141
-
142
-
143
- def bash(command)
144
- escaped_command = Shellwords.escape(command)
145
- system "bash -c #{escaped_command}"
146
- end
147
- end
148
-
149
9
  class SharedDeployFunctions
150
10
 
151
11
  def initialize
@@ -159,7 +19,7 @@ module PcfBlueGreen
159
19
 
160
20
  def blue_green_deploy
161
21
  status = bash("#{@source_cmd} && blue-green")
162
- raise PcfBlueGreen::BlueGreenDeploymentFailed unless status
22
+ raise PcfBlueGreen::BlueGreenDeployFailed unless status
163
23
  end
164
24
 
165
25
  def env_var_manifest_update
@@ -190,6 +50,7 @@ module PcfBlueGreen
190
50
  def bash(command)
191
51
  escaped_command = Shellwords.escape(command)
192
52
  system "bash -c #{escaped_command}"
193
- end
53
+ end
54
+
194
55
  end
195
56
  end
@@ -1,3 +1,3 @@
1
1
  module PcfBlueGreen
2
- VERSION = "0.4"
2
+ VERSION = "0.22"
3
3
  end
@@ -69,19 +69,33 @@ function cf-push {
69
69
  function check-health {
70
70
  local app_url=$1
71
71
  announce-task "Checking health"
72
- maitenance_enabled=$(echo $(cf curl '/v2/apps' -X GET -H 'Content-Type: application/x-www-form-urlencoded' -d "q=name:$cf_app" | jq '.resources[0].entity.environment_json.MAINTENANCE_ENABLED'))
73
- maitenance_enabled=$(echo ${maitenance_enabled//\"} | awk '{ print tolower($1) }')
74
- if [ $maitenance_enabled == "true" ] ; then
75
- status=$(curl -sSIL --header "x-digital-auth: ${JWT}" -X GET -o /dev/null -w "%{http_code}" ${app_url} --insecure)
76
- if [ $status == 503 ] ; then
77
- echo -e "${BGre}curl of ${app_url} 503 OK"
72
+ cf_output=$(echo $(cf env ${cf_app})| grep -o 'User-Provided:.*')
73
+ echo "Executing curl of ${app_url}\n\n"
74
+ if echo $cf_output| egrep -o 'MAINTENANCE_ENABLED: \S*'
75
+ then
76
+ kv=$(echo $cf_output| egrep -o 'MAINTENANCE_ENABLED: \S*')
77
+ arrkv=(${kv// / })
78
+ if [ ${arrkv[1]} == true ] ; then
79
+ status=$(curl -sSIL -X GET -o /dev/null -w "%{http_code}" ${app_url} --insecure)
80
+ if [ $status == 503 ] ; then
81
+ echo -e "${BGre}curl of ${app_url} 503 OK"
82
+ else
83
+ echo ERROR: Expecting 503 status
84
+ exit 1
85
+ fi
78
86
  else
79
- echo ERROR: Expecting 503 status
87
+ curl -fsSIL --user "${BASIC_AUTH_USERNAME}:${BASIC_AUTH_PASSWORD}" ${app_url} --insecure
88
+ status=$(curl -sSIL -X GET -o /dev/null -w "%{http_code}" ${app_url} --insecure)
89
+ if [ $status == 200 ] ; then
90
+ echo -e "${BGre}curl of ${app_url} 200 OK"
91
+ else
92
+ echo -e "${BGre}curl of ${app_url} ${status} ERR"
80
93
  exit 1
94
+ fi
81
95
  fi
82
96
  else
83
- curl -fsSIL --header "x-digital-auth: ${JWT}" --user "${BASIC_AUTH_USERNAME}:${BASIC_AUTH_PASSWORD}" ${app_url} --insecure
84
- status=$(curl -sSIL --header "x-digital-auth: ${JWT}" -X GET -o /dev/null -w "%{http_code}" ${app_url} --insecure)
97
+ curl -fsSIL -X GET --user "${BASIC_AUTH_USERNAME}:${BASIC_AUTH_PASSWORD}" ${app_url} --insecure
98
+ status=$(curl -sSIL -X GET -o /dev/null -w "%{http_code}" ${app_url} --insecure)
85
99
  if [ $status == 200 ] ; then
86
100
  echo -e "${BGre}curl of ${app_url} 200 OK"
87
101
  else
@@ -111,9 +125,37 @@ function generate-manifest {
111
125
  fi
112
126
  }
113
127
 
128
+ function remap-routes {
129
+ announce-task "Remapping routes..."
130
+ echo "cf_app is ${cf_app}"
131
+ echo "green is ${green}"
132
+ set -x
133
+ run-cmd cf routes
134
+
135
+ if [ -z ${sso_hostname+x} ]; then
136
+ run-cmd cf routes | tail -n +4 | grep ${cf_app} | awk '{print $3" -n "$2}'
137
+ cf routes | tail -n +4 | grep ${cf_app} | awk '{print $3" -n "$2}' | xargs -n 3 cf map-route ${green}
138
+ else
139
+ if [ -n "$map_main_route" ]; then
140
+ run-cmd cf routes | tail -n +4 | awk '($3==ENVIRON["domain"]) && ($2==ENVIRON["cf_app"]) {print $3" -n " $2 }' | xargs -n 3 cf map-route ${green}
141
+ fi
142
+ run-cmd cf routes | tail -n +4 | awk '($2==ENVIRON["sso_hostname"]) && ($5==ENVIRON["cf_app"]) {print $3" -n "$2 " --path "substr($4,2) }'
143
+ cf routes | tail -n +4 | awk '($2==ENVIRON["sso_hostname"]) && ($5==ENVIRON["cf_app"]) {print $3" -n "$2 " --path "substr($4,2) }' | xargs -n 5 cf map-route ${green}
144
+ fi
145
+
146
+ set +x
147
+ }
148
+
114
149
  function remove-blue {
115
150
  run-cmd cf delete ${blue} -f
116
151
  run-cmd cf rename $green $blue
152
+ run-cmd cf delete-route $domain -n $green -f
153
+ if [ -n "$sso_hostname" ] || [ -n "$remove_all_green_paths"] ; then
154
+ set -x
155
+ run-cmd cf routes | tail -n +4 | awk '($2==ENVIRON["green"]) && $4 {print $3" -n "$2 " --path "substr($4,2)" -f"}' | xargs -n 6
156
+ cf routes | tail -n +4 | awk '($2==ENVIRON["green"]) && $4 {print $3" -n "$2 " --path "substr($4,2)" -f"}' | xargs -n 6 cf delete-route
157
+ set +x
158
+ fi
117
159
  }
118
160
 
119
161
  function get-domain {
@@ -123,7 +165,7 @@ function get-domain {
123
165
 
124
166
  function prep-green {
125
167
  blue=${cf_app}
126
- export green="${cf_app}-${cf_space}-g"
168
+ export green="${cf_app}-${cf_space}-green"
127
169
  if [ -z ${cf_manifest+x }]; then
128
170
  generate-manifest
129
171
  else
@@ -161,6 +203,10 @@ function blue-green {
161
203
  else
162
204
  check-health "https://${green}.${health_check_domain}/${app_path}"
163
205
  fi
206
+
207
+ remap-routes
208
+ remove-blue
209
+ announce-success
164
210
  }
165
211
 
166
212
  function simple-push {
@@ -32,5 +32,4 @@ Gem::Specification.new do |spec|
32
32
  spec.add_development_dependency "bundler", "~> 1.13"
33
33
  spec.add_development_dependency "rake", "~> 10.0"
34
34
  spec.add_development_dependency "rspec", "~> 3.0"
35
- spec.add_dependency "pry"
36
35
  end
@@ -5,66 +5,7 @@ describe PcfBlueGreen do
5
5
  expect(PcfBlueGreen::VERSION).not_to be nil
6
6
  end
7
7
 
8
- describe "blue green deploy remap_routes" do
9
- it 're-maps routes correctly for an application' do
10
- expected_routes = %{simpleapp-app-space-green app.domain.com -n app-prod,
11
- simpleapp-app-space-green app.domain.com -n app-prod --path /some_path,
12
- simpleapp-app-space-green app.domain.com -n app-prod --path /other_domain_path}
13
-
14
- ENV['APP_SPACE'] = 'app-space'
15
- ENV['APP_NAME'] = 'simpleapp'
16
- ENV['cf_app'] = 'simpleapp'
17
- ENV['cf_api'] = 'https://api.sys.sandbox.test.com'
18
- ENV['cf_user'] = 'testuser'
19
- ENV['cf_password'] = 'password'
20
- ENV['cf_org'] = 'ds'
21
- ENV['green'] = 'simpleapp-app-space-green'
22
-
23
- space = ENV['APP_SPACE']
24
- api_url = ENV['cf_api']
25
- username = ENV['cf_user']
26
- password = ENV['cf_password']
27
- org = ENV['cf_org']
28
-
29
-
30
- @pcf_blue_green = PcfBlueGreen::Routing.new(api_url, username, password, org, space, login = false)
31
- @pcf_blue_green.stub(:entity_for).and_return(JSON.parse(File.read('spec/mocks/simple_entity.json')))
32
- @pcf_blue_green.stub(:routes_stdout).and_return(JSON.parse(File.read('spec/mocks/routes_stdout.json')))
33
- @pcf_blue_green.stub(:domain_for).and_return("app.domain.com")
34
- @pcf_blue_green.stub(:map_route_command).and_return('echo')
35
-
36
- expect(expected_routes).to eq(@pcf_blue_green.remap_routes)
37
- end
8
+ it "does something useful" do
9
+ expect(false).to eq(true)
38
10
  end
39
- describe "blue green deploy remove_routes" do
40
- it 'removes routes correctly for an application' do
41
- expected_routes = %{app.domain.com -n app-prod -f,
42
- app.domain.com -n app-prod --path /some_path -f,
43
- app.domain.com -n app-prod --path /other_domain_path -f}
44
-
45
- ENV['APP_SPACE'] = 'app-space'
46
- ENV['APP_NAME'] = 'simpleapp'
47
- ENV['cf_app'] = 'simpleapp'
48
- ENV['cf_api'] = 'https://api.sys.sandbox.test.com'
49
- ENV['cf_user'] = 'testuser'
50
- ENV['cf_password'] = 'password'
51
- ENV['cf_org'] = 'ds'
52
- ENV['green'] = 'simpleapp-app-space-green'
53
-
54
- space = ENV['APP_SPACE']
55
- api_url = ENV['cf_api']
56
- username = ENV['cf_user']
57
- password = ENV['cf_password']
58
- org = ENV['cf_org']
59
-
60
-
61
- @pcf_blue_green = PcfBlueGreen::Routing.new(api_url, username, password, org, space, login = false)
62
- @pcf_blue_green.stub(:entity_for).and_return(JSON.parse(File.read('spec/mocks/simple_entity.json')))
63
- @pcf_blue_green.stub(:routes_stdout).and_return(JSON.parse(File.read('spec/mocks/routes_stdout.json')))
64
- @pcf_blue_green.stub(:domain_for).and_return("app.domain.com")
65
- @pcf_blue_green.stub(:delete_route_command).and_return('echo')
66
-
67
- expect(expected_routes).to eq(@pcf_blue_green.remove_routes)
68
- end
69
- end
70
- end
11
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pcf_blue_green
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.4'
4
+ version: '0.22'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pawel Bardzinski
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-07-10 00:00:00.000000000 Z
11
+ date: 2017-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mixlib-shellout
@@ -72,20 +72,6 @@ dependencies:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
74
  version: '3.0'
75
- - !ruby/object:Gem::Dependency
76
- name: pry
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - ">="
80
- - !ruby/object:Gem::Version
81
- version: '0'
82
- type: :runtime
83
- prerelease: false
84
- version_requirements: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - ">="
87
- - !ruby/object:Gem::Version
88
- version: '0'
89
75
  description: This is a gem that encapsulates the blue-green bash script. This allows
90
76
  to run blue-green deployments easily
91
77
  email:
@@ -106,10 +92,6 @@ files:
106
92
  - lib/pcf_blue_green/version.rb
107
93
  - lib/shared-deploy-functions.sh
108
94
  - pcf_blue_green.gemspec
109
- - spec/mocks/custom_routes.txt
110
- - spec/mocks/custom_routes_domain_host.txt
111
- - spec/mocks/routes_stdout.json
112
- - spec/mocks/simple_entity.json
113
95
  - spec/pcf_blue_green_spec.rb
114
96
  - spec/spec_helper.rb
115
97
  homepage: https://github.com/aaa-ncnu-ie/pcf_blue_green
@@ -1,14 +0,0 @@
1
-
2
-
3
- space host domain port path type apps service
4
- app-space onemoreapp apps.prod.domain.com onemoreapp
5
- app-space otherapp apps.prod.domain.com otherapp
6
- app-space customapp apps.prod.domain.com customapp
7
- app-space otherapp apps.prod.domain.com customapp
8
- app-space customapp apps.prod.otherdomain.com otherapp
9
- app-space customapp apps.prod.domain.com /somepath1 customapp
10
- app-space otherapp apps.prod.domain.com /somepath2 customapp
11
- app-space otherapp apps.prod.domain.com /somepath3 customapp
12
- app-space otherapp apps.prod.domain.com /somepath4 otherapp
13
- app-space otherapp apps.prod.domain.com /somepath5 otherapp
14
- app-space otherapp apps.prod.domain.com /somepath6 otherapp
@@ -1,16 +0,0 @@
1
-
2
-
3
- space host domain port path type apps service
4
- app-space first-app apps.prod.domain.com.com first-app
5
- app-space second-app apps.prod.domain.com.com second-app
6
- app-space third-app apps.prod.domain.com.com third-app
7
- app-space third-app apps.prod.otherdomain.com third-app
8
- app-space third-app apps.prod.domain.com.com /firstapp-path1 second-app
9
- app-space third-app apps.prod.domain.com.com /firstapp-path2 second-app
10
- app-space third-app apps.prod.domain.com.com /firstapp-path3 second-app
11
- app-space third-app apps.prod.domain.com.com /firstapp-path4 second-app
12
- app-space third-app apps.prod.domain.com.com /firstapp-path5 second-app
13
- app-space third-app apps.prod.otherdomain.com /otherdomainpath first-app
14
- app-space third-app apps.prod.domain.com.com /secondapp-path second-app
15
- app-space third-app apps.prod.domain.com.com /secondapp-path second-app
16
- app-space third-app-other-host apps.prod.otherdomain.com third-app
@@ -1,43 +0,0 @@
1
- {
2
- "resources": [
3
- {
4
- "metadata": {
5
- "guid": "ddbc3214-eg68-44ed-836a-a2defae75547",
6
- "url": "/v2/routes/XXXXXXXXXXXXXXXXXXXX",
7
- "created_at": "2017-02-08T16:28:08Z",
8
- "updated_at": null
9
- },
10
- "entity": {
11
- "host": "app-prod",
12
- "path": "",
13
- "domain_url": "/v2/shared_domains/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
14
- }
15
- },
16
- {
17
- "metadata": {
18
- "guid": "ddsc3214-eg38-44ed-836a-a2defae75547",
19
- "url": "/v2/routes/XXXXXXXXXXXXXXXXXXXX",
20
- "created_at": "2017-02-08T16:28:08Z",
21
- "updated_at": null
22
- },
23
- "entity": {
24
- "host": "app-prod",
25
- "path": "/some_path",
26
- "domain_url": "/v2/shared_domains/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
27
- }
28
- },
29
- {
30
- "metadata": {
31
- "guid": "dd3c3214-eg68-44ed-836a-a2defae74547",
32
- "url": "/v2/routes/XXXXXXXXXXXXXXXXXXXX",
33
- "created_at": "2017-02-08T16:28:08Z",
34
- "updated_at": null
35
- },
36
- "entity": {
37
- "host": "app-prod",
38
- "path": "/other_domain_path",
39
- "domain_url": "/v2/shared_domains/YYYYYYYYYYYYYYYYYYYYYYYYYYYY"
40
- }
41
- }
42
- ]
43
- }
@@ -1,16 +0,0 @@
1
- {
2
- "name": "app-prod",
3
- "production": false,
4
- "detected_buildpack_guid": null,
5
- "environment_json": {
6
- },
7
- "ports": [
8
- 8080
9
- ],
10
- "space_url": "/v2/spaces/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
11
- "stack_url": "/v2/stacks/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
12
- "routes_url": "/v2/apps/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/routes",
13
- "events_url": "/v2/apps/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/events",
14
- "service_bindings_url": "/v2/apps/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/service_bindings",
15
- "route_mappings_url": "/v2/apps/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/route_mappings"
16
- }