@govuk-pay/cli 0.0.3 → 0.0.4

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 (89) hide show
  1. package/package.json +1 -1
  2. package/resources/legacy-ruby-cli/.rspec +1 -0
  3. package/resources/legacy-ruby-cli/.rubocop.yml +26 -0
  4. package/resources/legacy-ruby-cli/.ruby-version +1 -0
  5. package/resources/legacy-ruby-cli/Gemfile +24 -0
  6. package/resources/legacy-ruby-cli/Gemfile.lock +1431 -0
  7. package/resources/legacy-ruby-cli/README.md +142 -0
  8. package/resources/legacy-ruby-cli/bin/pay +31 -0
  9. package/resources/legacy-ruby-cli/config/generate-secrets.yml +9 -0
  10. package/resources/legacy-ruby-cli/config/secrets.yml +581 -0
  11. package/resources/legacy-ruby-cli/config/service_secrets.yml +174 -0
  12. package/resources/legacy-ruby-cli/lib/pay_cli/aws/document.rb +23 -0
  13. package/resources/legacy-ruby-cli/lib/pay_cli/aws/services.rb +47 -0
  14. package/resources/legacy-ruby-cli/lib/pay_cli/aws/tokens.rb +161 -0
  15. package/resources/legacy-ruby-cli/lib/pay_cli/commands/aws.rb +51 -0
  16. package/resources/legacy-ruby-cli/lib/pay_cli/commands/browse.rb +31 -0
  17. package/resources/legacy-ruby-cli/lib/pay_cli/commands/doctor.rb +154 -0
  18. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/app_client.rb +216 -0
  19. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/config.rb +138 -0
  20. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/config.yaml +192 -0
  21. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/docker.rb +36 -0
  22. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/docker-compose.erb +270 -0
  23. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/end-to-end.erb +30 -0
  24. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/localstack/init-aws.sh +70 -0
  25. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/naxsi/readme.md +1 -0
  26. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/postgres/docker-entrypoint-initdb.d/make_payments_databases.sql +26 -0
  27. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/adminusers.env +49 -0
  28. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/cardid.env +2 -0
  29. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/connector.env +70 -0
  30. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/demo-service.env +10 -0
  31. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/frontend.env +12 -0
  32. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/java_app.env +1 -0
  33. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ledger.env +7 -0
  34. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/products-ui.env +14 -0
  35. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/products.env +25 -0
  36. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/publicapi.env +13 -0
  37. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/publicauth.env +13 -0
  38. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/selfservice.env +21 -0
  39. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/certs/frontend-proxy.crt +18 -0
  40. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/certs/products-ui-proxy.crt +20 -0
  41. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/certs/selfservice-proxy.crt +20 -0
  42. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/certs/stubs-proxy.crt +18 -0
  43. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/keys/frontend-proxy.key +28 -0
  44. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/keys/products-ui-proxy.key +28 -0
  45. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/keys/selfservice-proxy.key +28 -0
  46. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/keys/stubs-proxy.key +28 -0
  47. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/make-selfsigned.sh +2 -0
  48. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/stubs.env +12 -0
  49. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/toolbox.env +5 -0
  50. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/webhooks.env +9 -0
  51. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/image_extractor.rb +20 -0
  52. package/resources/legacy-ruby-cli/lib/pay_cli/commands/local.rb +430 -0
  53. package/resources/legacy-ruby-cli/lib/pay_cli/commands/schema.rb +36 -0
  54. package/resources/legacy-ruby-cli/lib/pay_cli/commands/secrets.rb +114 -0
  55. package/resources/legacy-ruby-cli/lib/pay_cli/commands/ssm.rb +111 -0
  56. package/resources/legacy-ruby-cli/lib/pay_cli/commands/tf.rb +90 -0
  57. package/resources/legacy-ruby-cli/lib/pay_cli/commands/tunnel/services.yml +49 -0
  58. package/resources/legacy-ruby-cli/lib/pay_cli/config.rb +27 -0
  59. package/resources/legacy-ruby-cli/lib/pay_cli/ec2.rb +38 -0
  60. package/resources/legacy-ruby-cli/lib/pay_cli/entry_point.rb +52 -0
  61. package/resources/legacy-ruby-cli/lib/pay_cli/environment.rb +25 -0
  62. package/resources/legacy-ruby-cli/lib/pay_cli/logger.rb +3 -0
  63. package/resources/legacy-ruby-cli/lib/pay_cli/logs.rb +248 -0
  64. package/resources/legacy-ruby-cli/lib/pay_cli/naming.rb +44 -0
  65. package/resources/legacy-ruby-cli/lib/pay_cli/secrets.rb +276 -0
  66. package/resources/legacy-ruby-cli/lib/pay_cli/stop_yubico_authenticator.rb +10 -0
  67. package/resources/legacy-ruby-cli/lib/pay_cli/ykman_oath_credential_config.rb +70 -0
  68. package/resources/legacy-ruby-cli/lib/zeitwerk_setup.rb +5 -0
  69. package/resources/legacy-ruby-cli/package-lock.json +6 -0
  70. package/resources/legacy-ruby-cli/rds_access/connect.sh +149 -0
  71. package/resources/legacy-ruby-cli/spec/.rubocop.yml +2 -0
  72. package/resources/legacy-ruby-cli/spec/fixtures/dockerfile_examples/Dockerfile.complex +34 -0
  73. package/resources/legacy-ruby-cli/spec/fixtures/dockerfile_examples/Dockerfile.complex_differing_froms +33 -0
  74. package/resources/legacy-ruby-cli/spec/fixtures/dockerfile_examples/Dockerfile.no_from +3 -0
  75. package/resources/legacy-ruby-cli/spec/fixtures/dockerfile_examples/Dockerfile.simple +5 -0
  76. package/resources/legacy-ruby-cli/spec/fixtures/dockerfile_examples/Dockerfile.simple_no_tag +5 -0
  77. package/resources/legacy-ruby-cli/spec/fixtures/dockerfile_examples/Dockerfile.with_sha +5 -0
  78. package/resources/legacy-ruby-cli/spec/fixtures/dockerfile_examples/Dockerfile.with_sha_no_tag +5 -0
  79. package/resources/legacy-ruby-cli/spec/lib/pay_cli/commands/local/image_extractor_spec.rb +55 -0
  80. package/resources/legacy-ruby-cli/spec/naming_spec.rb +83 -0
  81. package/resources/legacy-ruby-cli/spec/spec_helper.rb +106 -0
  82. package/resources/legacy-ruby-cli/vulnerability_scan/.nvmrc +1 -0
  83. package/resources/legacy-ruby-cli/vulnerability_scan/generate_vulnerability_report.js +91 -0
  84. package/resources/legacy-ruby-cli/vulnerability_scan/reports/.gitkeep +0 -0
  85. package/resources/legacy-ruby-cli/vulnerability_scan/scan.sh +57 -0
  86. package/src/commands/browse.js +2 -2
  87. package/src/commands/legacy.js +3 -2
  88. package/src/core/constants.js +7 -10
  89. package/src/util/payCliExec.js +18 -1
@@ -0,0 +1,90 @@
1
+ require 'yaml'
2
+ require 'fileutils'
3
+
4
+ module PayCLI::Commands::Tf
5
+ def self.usage!
6
+ STDERR.puts <<~USAGE
7
+ pay terraform ...
8
+
9
+ # Run newer Terraform from `provisioning/terraform/deployments` (note: For terraform commands which are
10
+ # multi-part (such as `terraform state list`), or to supply additional arguments to terraform you can
11
+ # append all the extra commands and arguments at the end of pay command)
12
+ - `pay tf apply account staging`
13
+ - `pay tf plan microservices_v2 test-12 frontend`
14
+ - `pay tf plan management staging-2 scheduled_http_v2`
15
+ - `pay tf apply microservices_v2 test-12 frontend -var "myvariable=variable-value"`
16
+ - `pay tf state microservices_v2 test-perf-1 selfservice list`
17
+
18
+ # Terraform versions
19
+ - All other terraform should not need the version specified
20
+
21
+ USAGE
22
+ exit
23
+ end
24
+
25
+ def self.start!
26
+ args = ARGV[1..-1] || []
27
+
28
+ command, *args = args
29
+ if command == 'copy'
30
+ return copy!(args)
31
+ end
32
+
33
+ if command == 'force-unlock'
34
+ lock_id = args.shift
35
+ command = command + ' ' + lock_id
36
+ end
37
+
38
+ usage! if args.length < 2
39
+ resource, environment, *args = args
40
+
41
+ PayCLI::Environment.setup! environment
42
+
43
+ STDERR.puts "\n"
44
+
45
+ item!(command, environment, resource, args)
46
+ end
47
+
48
+ def self.item!(command, environment, deployment_type, args)
49
+ account = environment.split('-').first
50
+ deployment_path = [PayCLI::Config::PROJECT_PATH, 'provisioning', 'terraform', 'deployments']
51
+ if account == environment
52
+ deployment_path += [account, deployment_type]
53
+ else
54
+ deployment_name, *args = *args
55
+ deployment_path += [account, environment, deployment_type, deployment_name]
56
+ end
57
+
58
+ directory = File.expand_path(File.join(deployment_path))
59
+ unless File.exist? directory
60
+ STDERR.puts "#{directory} does not exist"
61
+ exit 1
62
+ end
63
+
64
+ puts "ℹ️ Running new-style Terraform from #{directory} ..."
65
+ init_flags = %w(production staging).include?(account) ? '' : '-upgrade'
66
+
67
+ # Only run init if .terraform doesn't already exist
68
+ tf_command = "cd #{directory}; "
69
+ if !File.directory?(File.join(directory, ".terraform"))
70
+ tf_command += "terraform init #{init_flags}; "
71
+ end
72
+ tf_command += "terraform #{command} #{args.join(' ')}"
73
+
74
+ *pid = spawn(
75
+ tf_command,
76
+ :in => $stdin,
77
+ :out => $stdout,
78
+ :err => $stderr,
79
+ )
80
+ Process.wait pid[0]
81
+ report_status! $?
82
+ end
83
+
84
+ def self.report_status!(child_status)
85
+ emoji = child_status.success? ? '✅' : '🔴'
86
+ status = child_status.success? ? 'success' : 'failure'
87
+
88
+ puts "#{emoji} Terraform run was a #{status}"
89
+ end
90
+ end
@@ -0,0 +1,49 @@
1
+ ---
2
+ - service_name: adminusers
3
+ local_port: 9001
4
+ remote_port: 50603
5
+
6
+ # There are no longer live servers for cardid so it's
7
+ # not possible to tunnel to it
8
+ # - service_name: cardid
9
+ # local_port: 9002
10
+ # remote_port: 50503
11
+
12
+ - service_name: connector
13
+ local_port: 9003
14
+ remote_port: 50103
15
+
16
+ - service_name: products
17
+ local_port: 9005
18
+ remote_port: 51003
19
+
20
+ - service_name: publicauth
21
+ local_port: 9006
22
+ remote_port: 50303
23
+
24
+ - service_name: ledger
25
+ local_port: 9007
26
+ remote_port: 50104
27
+
28
+ - service_name: adminusers_db
29
+ local_port: 5432
30
+ remote_port: 5432
31
+
32
+ - service_name: connector_db
33
+ local_port: 5432
34
+ remote_port: 5432
35
+
36
+ - service_name: products_db
37
+ local_port: 5432
38
+ remote_port: 5432
39
+
40
+ - service_name: publicauth_db
41
+ local_port: 5432
42
+ remote_port: 5432
43
+
44
+ - service_name: ledger_db
45
+ local_port: 5432
46
+ remote_port: 5432
47
+ instance_version:
48
+ production-2: 1
49
+ ...
@@ -0,0 +1,27 @@
1
+ module PayCLI::Config
2
+ COMMANDS = %w(
3
+ aws
4
+ browse
5
+ build
6
+ deploy
7
+ doctor
8
+ dns
9
+ healthcheck
10
+ logs
11
+ purgatory
12
+ scale
13
+ secrets
14
+ tf
15
+ tunnel
16
+ validate
17
+ performance
18
+ ).freeze
19
+
20
+ PROJECT_PATH = File.expand_path("../../..", __dir__)
21
+
22
+ CLI_PATH = File.join PROJECT_PATH, 'cli'
23
+ CONFIG_PATH = File.join CLI_PATH, 'config'
24
+ LOCAL_SECRETS_PATH = File.expand_path(
25
+ File.join('~', '.govuk-pay', 'secrets.yml')
26
+ )
27
+ end
@@ -0,0 +1,38 @@
1
+ require 'aws-sdk-ec2'
2
+
3
+ class PayCLI::Ec2
4
+
5
+ attr_reader :ec2, :logger
6
+
7
+ def initialize(region: default_region, logger: Logger.new(STDERR))
8
+ @ec2 = ::Aws::EC2::Client.new(region: region)
9
+ @logger = logger
10
+ end
11
+
12
+ def all_instances
13
+ @instances ||= fetch_all_instances
14
+ end
15
+
16
+ private
17
+ def default_region
18
+ ENV['AWS_REGION'] || ENV['AWS_DEFAULT_REGION'] || 'eu-west-1'
19
+ end
20
+
21
+ def fetch_all_instances
22
+ next_token = nil
23
+ instances = []
24
+
25
+ begin
26
+ response = ec2.describe_instances(next_token: next_token)
27
+
28
+ if response
29
+ instances += response.reservations.flat_map { |r| r.instances }
30
+ next_token = response.next_token
31
+ else
32
+ break
33
+ end
34
+ end while next_token
35
+
36
+ return instances
37
+ end
38
+ end
@@ -0,0 +1,52 @@
1
+ require 'thor'
2
+
3
+ $LOAD_PATH << File.join(File.dirname(__FILE__))
4
+
5
+ class PayCLI::EntryPoint < Thor
6
+ desc 'aws', 'Interactions with AWS, e.g. fetching STS tokens'
7
+ subcommand 'aws', PayCLI::Commands::Aws
8
+
9
+ desc 'browse', 'Opens web browser link to useful links'
10
+ subcommand 'browse', PayCLI::Commands::Browse
11
+
12
+ desc 'doctor', 'Attempts to initialise or fix the Pay CLI'
13
+
14
+ def doctor(*_args)
15
+ PayCLI::Commands::Doctor.new.start!
16
+ end
17
+
18
+ desc 'deployment_status <env>', 'Describe whats deployed'
19
+ def deployment_status(env, regex = '')
20
+ PayCLI::Commands::Healthcheck.deployment_status!(env)
21
+ end
22
+
23
+ desc 'scale', 'Quick way to scale microservices (dangerous)'
24
+
25
+ desc 'secrets', 'Manage secrets in and between environments'
26
+ subcommand 'secrets', PayCLI::Commands::Secrets
27
+
28
+ desc 'ssh', 'DEPRECATED: SSH to boxes in environments'
29
+
30
+ desc 'ssm', 'Start an SSM session to boxes in environments'
31
+
32
+ def ssm(*_args)
33
+ PayCLI::Commands::Ssm.start!
34
+ end
35
+
36
+ desc 'tf', 'Runs Terraform'
37
+
38
+ def tf(*_args)
39
+ PayCLI::Commands::Tf.start!
40
+ end
41
+
42
+ desc 'tunnel', 'DEPRECATED: Opens tunnels to microservice(s).'
43
+
44
+ desc 'local', 'Sets up local Pay development environment'
45
+ subcommand 'local', PayCLI::Commands::Local
46
+
47
+ desc 'schema', 'Generates web based database diagrams and metadata locally'
48
+ def schema(*_args)
49
+ PayCLI::Commands::Schema.start!
50
+ end
51
+
52
+ end
@@ -0,0 +1,25 @@
1
+ module PayCLI::Environment
2
+ def self.setup!(environment)
3
+ account = environment.split('-').first
4
+ STDERR.puts "Using the #{account} AWS account"
5
+
6
+ PayCLI::Aws::Tokens.setup!(account)
7
+
8
+ ENV['AWS_PROFILE'] = account
9
+ ENV['AWS_ACCOUNT'] = account
10
+ end
11
+
12
+ def self.setup_each!(env_filter, &block)
13
+ all_envs = %w{dev ci deploy test staging production}
14
+ if env_filter.any?
15
+ envs = env_filter & all_envs
16
+ else
17
+ envs = all_envs
18
+ end
19
+
20
+ envs.each do |env|
21
+ setup!(env)
22
+ yield(env)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ PayCLI::Logger = Logger.new($stderr).tap do |logger|
2
+ logger.level = Logger::INFO
3
+ end
@@ -0,0 +1,248 @@
1
+ require 'pp'
2
+
3
+ require 'tty-table'
4
+ require 'SecureRandom'
5
+
6
+ require 'aws-sdk-cloudwatchlogs'
7
+
8
+
9
+ module PayCLI::Logs
10
+
11
+ def self.port_service_map(port)
12
+ port_service = {
13
+ "50003" => 'frontend',
14
+ '50103' => 'connector',
15
+ '50203' => 'publicapi',
16
+ '50303' => 'publicauth',
17
+ '50403' => 'selfservice',
18
+ '50503' => 'cardid',
19
+ '50603' => 'adminusers',
20
+ '51003' => 'products',
21
+ '50903' => 'products-ui',
22
+ }
23
+ port_service[port.chomp]
24
+
25
+ end
26
+
27
+ def self.host_groups
28
+ {
29
+ 'frontend' => ['connector', 'cardid'],
30
+ 'selfservice' => ['connector','adminusers'],
31
+ 'product-ui' => ['products'] ,
32
+ 'publicapi' => ['publicauth', 'connector']
33
+ }
34
+ end
35
+
36
+ def self.fetch_and_process_logs(process, env, start_time, end_time, log_group, filter)
37
+
38
+
39
+ cloud_watch_logs = Aws::CloudWatchLogs::Client.new
40
+ stats = {}
41
+
42
+ next_token = nil
43
+ loop do
44
+ STDERR.puts "Making request to AWS logs for #{log_group} between #{start_time} and #{end_time}"
45
+
46
+ opts = {log_group_name: "#{env}__#{log_group}", start_time: start_time, end_time:end_time, filter_pattern: filter}
47
+ opts[:next_token] = next_token unless next_token.nil?
48
+ response = cloud_watch_logs.filter_log_events(opts)
49
+
50
+ next_token = response.next_token
51
+ messages= response.events.map{|event| event.message}
52
+ stats = process.call(stats, messages)
53
+
54
+
55
+ break if next_token.nil?
56
+ end
57
+ stats
58
+ end
59
+
60
+
61
+ def self.compare_stats(stats_bundle1, stats_bundle2, label1="the first dataset", label2="the second dataset")
62
+ group_stats1 = stats_bundle1.first
63
+ host_stats1 = stats_bundle1.last
64
+ group_stats2 = stats_bundle2.first
65
+ host_stats2 = stats_bundle2.last
66
+ puts "#{label1} #{label2}"
67
+ STDERR.puts "We are looking at each frontend node and nodes that back them. \nIf the percentage of the duration between them varies more than 10% between the environments make note of it"
68
+ group_stats1.each do |frontend, hosts1|
69
+
70
+ if !group_stats2.include? frontend
71
+ STDERR.puts "No traffic for #{frontend} in the #{label2}"
72
+ break;
73
+ end
74
+ hosts2= group_stats2[frontend]
75
+
76
+ if hosts1.length != hosts2.length
77
+ STDERR.puts "Skipping #{frontend} group as there is a different number of hosts, probably an unused cluster "
78
+ else
79
+
80
+ STDERR.puts "\nDurations for #{frontend} group\n"
81
+ STDERR.puts "host, percentage duration #{label1}, percentage duration #{label2}"
82
+ hosts1.each do |host, count_duration1|
83
+ count_duration2 = hosts2[host]
84
+
85
+ if count_duration1[:percentage_duration] > count_duration2[:percentage_duration] + 10\
86
+ || count_duration1[:percentage_duration] < count_duration2[:percentage_duration] - 10 then
87
+
88
+ #STDERR.puts "The percentage request duration for #{host} in #{frontend} group is greater than 10% different in duration for #{label1} compared to the #{label2}"
89
+ #STDERR.puts "It is #{count_duration1[:percentage_duration]}% of the duration for #{label1} vs #{count_duration2[:percentage_duration]}% for #{label2}"
90
+ STDERR.puts "#{host}, #{count_duration1[:percentage_duration]}, #{count_duration2[:percentage_duration]}"
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+
97
+ STDERR.puts "\n This section shows endpoints that have more than 5% difference in total durations \n \
98
+ and also endpoints where there is greater than 1% duration that don't exist at all in the other set \n"
99
+ host_stats1.each do |host, endpoint_stats1|
100
+
101
+ if !host_stats2.include? host
102
+ STDERR.puts "No traffic for #{host} in the #{label2}"
103
+ else
104
+ endpoint_stats2 = host_stats2[host]
105
+ STDERR.puts "\n Differences in endpoint durations for #{host} "
106
+ STDERR.puts "endpoint, percentage duration #{label1}, percentage duration #{label2}, mean duration #{label1}, mean duration #{label2} \n"
107
+ endpoint_stats1.each do |end_point, count_duration1|
108
+ if count_duration1[:percentage_duration] > 1
109
+ if endpoint_stats2.include? end_point
110
+ count_duration2 = endpoint_stats2[end_point]
111
+ if count_duration1[:percentage_duration] > count_duration2[:percentage_duration] + 5 \
112
+ || count_duration1[:percentage_duration] < count_duration2[:percentage_duration] - 5 then
113
+
114
+ #STDERR.puts "The percentage request duration for #{end_point} on #{host} group is greater than 5% different in duration of #{label1} compared to #{label2}"
115
+ #STDERR.puts "It is #{count_duration1[:percentage_duration]}% of the total duration for #{label1} vs #{count_duration2[:percentage_duration]}% for #{label2}"
116
+ STDERR.puts "#{end_point}, #{count_duration1[:percentage_duration]}, #{count_duration2[:percentage_duration]}, #{count_duration1[:mean_duration]} , #{count_duration2[:mean_duration]} "
117
+ end
118
+ else
119
+ #STDERR.puts "The second dataset doesn't have #{end_point} and it has been found to have significant amount of traffic >1% total duration for #{host} in the first dataset, but not in the second"
120
+ STDERR.puts "#{end_point}, #{count_duration1[:percentage_duration]}, Not found, #{count_duration1[:mean_duration]} , Not found"
121
+ end
122
+ elsif endpoint_stats2.include? end_point
123
+ if endpoint_stats2[end_point][:percentage_duration] > 1
124
+ #STDERR.puts "#{label1} doesn't have significant amount of traffic to #{end_point} and it has been found to have significant amount of traffic >1% total duration for #{host} for #{label2}"
125
+ #STDERR.puts "It is #{count_duration1[:percentage_duration]}% of the total duration spent for #{label1} vs #{endpoint_stats2[end_point][:percentage_duration]}% for #{label2}"
126
+
127
+ count_duration2 = endpoint_stats2[end_point]
128
+ STDERR.puts "#{end_point}, #{count_duration1[:percentage_duration]}, #{endpoint_stats2[end_point][:percentage_duration]}, #{count_duration1[:mean_duration]} , #{count_duration2[:mean_duration]}"
129
+ end
130
+ end
131
+ end
132
+ endpoint_stats2.each do |end_point, count_duration2|
133
+ if count_duration2[:percentage_duration] > 1 && !(endpoint_stats1.include? end_point)
134
+
135
+ #STDERR.puts "#{label1} doesn't have any traffic to #{end_point} and it has been found to have significant amount of traffic >1% total duration for #{host} for #{label1}"
136
+ #STDERR.puts "It has #{count_duration2[:percentage_duration]}% of the total duration spent for #{label2}"
137
+ STDERR.puts "#{end_point}, Not found, #{count_duration2[:percentage_duration]}, Not found, #{count_duration2[:mean_duration]}"
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ end
144
+
145
+ def self.percent_stats(all_stats)
146
+ averaged_stats = {}
147
+ grand_total = 0.0
148
+ grand_duration = 0.0
149
+ all_stats.each do |host, host_stats|
150
+ averaged_stats[host] ||= {}
151
+ #puts host_stats
152
+ total_duration = 0.0
153
+ total_count = 0.0
154
+ host_stats[:requests].each do |end_point, count_duration|
155
+ count_percent = (count_duration[:count]/host_stats[:total_count]) * 100
156
+ total_count += count_percent
157
+
158
+ duration_percent = (count_duration[:duration]/host_stats[:total_duration]) * 100
159
+ total_duration += duration_percent
160
+ averaged_stats[host][end_point] = {:percentage_request => count_percent, :percentage_duration => duration_percent, :mean_duration => count_duration[:duration]/count_duration[:count] }
161
+ end
162
+ puts "Host total duration #{total_duration}, total count #{total_count}"
163
+ grand_total += host_stats[:total_count]
164
+ grand_duration += host_stats[:total_duration]
165
+ end
166
+ #average_host_stats = {}
167
+ #all_stats.each do |host, stats|
168
+ # count_percent = (stats[:total_count]/grand_total) * 100
169
+ # duration_percent = (stats[:total_duration]/grand_duration) * 100
170
+ # average_host_stats[host] ||= {percentage_duration: duration_percent, percentage_request:count_percent }
171
+ #end
172
+ average_group_stats = {}
173
+ self.host_groups.each do |frontend, backends |
174
+ # Get the stats for the frontend and the backends
175
+ group_stats = all_stats.select { |host| backends.include?(host) || frontend == host }
176
+
177
+ group_duration = 0.0
178
+ group_count = 0.0
179
+ group_stats.each do |host, count_duration|
180
+ group_duration+= count_duration[:total_duration]
181
+ group_count+= count_duration[:total_count]
182
+ end
183
+ processed_group_stats = {}
184
+ group_stats.each do |host, count_duration|
185
+
186
+ count_percent = (count_duration[:total_count]/group_count) * 100
187
+
188
+ duration_percent = (count_duration[:total_duration]/group_duration) * 100
189
+ processed_group_stats[host] ||={ percantage_request: count_percent, percentage_duration: duration_percent }
190
+ end
191
+ average_group_stats[frontend] ||= processed_group_stats
192
+ end
193
+ [average_group_stats, averaged_stats] #average_host_stats
194
+ end
195
+
196
+ def self.process_nginx_logs(stats, logs)
197
+ #Split logs -> end_point, duration
198
+
199
+ #Transform endpoint names
200
+ #Collect stats
201
+ end_point_durations = logs.map{|line| line.split(' ').values_at(3, 10, 11, 13, 15)}
202
+ end_point_durations.each do | end_point_duration |
203
+ host, verb,end_point, return_code ,duration = end_point_duration
204
+ # Some heuristics to make sure that we have reasonable information in the log-line.
205
+ #It has a valid verb, a / in the path and is not a 404 or 302 (as these just
206
+ #add lots of noise to the logs, 302 redirect to 404s a lot)
207
+ if /GET|POST|PUT|DELETE|PATCH/.match(verb) && end_point.include?('/') \
208
+ && ! (return_code.include?('404') || return_code.include?('302') ) then
209
+ # The host name is not always defined nicely so use the port to get a nice name. Will break with ecs proper...
210
+ host = port_service_map(host.split(':')[-1])
211
+ #The verb has a " at the beginning so get rid of it
212
+ verb[0] = ''
213
+ stats[host] ||= {total_duration: 0.0, total_count: 0.0, requests: {} }
214
+ end_point=self.process_end_point_name(end_point,verb)
215
+ stats[host][:total_duration] += duration.to_f
216
+ stats[host][:total_count] += 1
217
+ stats[host][:requests][end_point] ||= { count: 0 , duration: 0.0 }
218
+ stats[host][:requests][end_point][:count] +=1
219
+ stats[host][:requests][end_point][:duration] += duration.to_f
220
+ end
221
+
222
+
223
+
224
+ end
225
+ stats
226
+ end
227
+
228
+
229
+
230
+ def self.process_end_point_name(end_point_name, verb)
231
+ #If part of a path is greater than 16 characters long or just a number, we probably don't care about it.
232
+
233
+
234
+ end_point = end_point_name.split('?').first
235
+ new_end_point = end_point.split('/').reject{ |part| /^[0-9]+$/.match(part) || part.length > 16 }.join('/')
236
+ #We don't care about parameters after a ?.
237
+ # Requests to / get filtered out to nil so add them back
238
+ new_end_point ||= '/'
239
+ #puts even_newer
240
+ verb+new_end_point
241
+ end
242
+
243
+ def self.fetch_and_process_nginx_logs(env, start_date, end_date)
244
+ self.fetch_and_process_logs(method(:process_nginx_logs),env, start_date, end_date, "nginx_access", "-healthcheck")
245
+ end
246
+
247
+
248
+ end
@@ -0,0 +1,44 @@
1
+ module PayCLI::Naming
2
+ def self.asg_name(env, microservice)
3
+ microservice = 'www' if microservice == 'frontend'
4
+ "#{env}-#{microservice}-ecs-appserver"
5
+ end
6
+
7
+ def self.elb_names(env, services)
8
+ services
9
+ .map { |s| s.gsub(/notifications/, 'notices') }
10
+ .map { |s| "#{env}-#{s}-ecs-elb" }
11
+ .zip(services)
12
+ .to_h
13
+ end
14
+
15
+ def self.domain_name(env)
16
+ acc = env.split('-').first
17
+
18
+ case acc
19
+ when 'deploy', 'staging', 'production'
20
+ 'payments.service.gov.uk'
21
+ else
22
+ 'pymnt.uk'
23
+ end
24
+ end
25
+
26
+ def self.bucket_name(env)
27
+ acc = env.split('-').first
28
+
29
+ case acc
30
+ when 'dev', 'test'
31
+ 'pay-govuk-terraform-state-ci'
32
+ when 'staging', 'production'
33
+ 'pay-govuk-terraform-state-deploy'
34
+ else
35
+ "pay-govuk-terraform-state-#{acc}"
36
+ end
37
+ end
38
+
39
+ def self.db_name(env, service)
40
+ microservice = service['service_name'].gsub(/_db$/, '')
41
+ version = service.dig('instance_version', env) || '0'
42
+ "#{env}-#{microservice}-rds-#{version}"
43
+ end
44
+ end