@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.
- package/package.json +1 -1
- package/resources/legacy-ruby-cli/.rspec +1 -0
- package/resources/legacy-ruby-cli/.rubocop.yml +26 -0
- package/resources/legacy-ruby-cli/.ruby-version +1 -0
- package/resources/legacy-ruby-cli/Gemfile +24 -0
- package/resources/legacy-ruby-cli/Gemfile.lock +1431 -0
- package/resources/legacy-ruby-cli/README.md +142 -0
- package/resources/legacy-ruby-cli/bin/pay +31 -0
- package/resources/legacy-ruby-cli/config/generate-secrets.yml +9 -0
- package/resources/legacy-ruby-cli/config/secrets.yml +581 -0
- package/resources/legacy-ruby-cli/config/service_secrets.yml +174 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/aws/document.rb +23 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/aws/services.rb +47 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/aws/tokens.rb +161 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/aws.rb +51 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/browse.rb +31 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/doctor.rb +154 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/app_client.rb +216 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/config.rb +138 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/config.yaml +192 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/docker.rb +36 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/docker-compose.erb +270 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/end-to-end.erb +30 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/localstack/init-aws.sh +70 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/naxsi/readme.md +1 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/postgres/docker-entrypoint-initdb.d/make_payments_databases.sql +26 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/adminusers.env +49 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/cardid.env +2 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/connector.env +70 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/demo-service.env +10 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/frontend.env +12 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/java_app.env +1 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ledger.env +7 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/products-ui.env +14 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/products.env +25 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/publicapi.env +13 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/publicauth.env +13 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/selfservice.env +21 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/certs/frontend-proxy.crt +18 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/certs/products-ui-proxy.crt +20 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/certs/selfservice-proxy.crt +20 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/certs/stubs-proxy.crt +18 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/keys/frontend-proxy.key +28 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/keys/products-ui-proxy.key +28 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/keys/selfservice-proxy.key +28 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/keys/stubs-proxy.key +28 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/ssl/make-selfsigned.sh +2 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/stubs.env +12 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/toolbox.env +5 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/files/services/webhooks.env +9 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local/image_extractor.rb +20 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/local.rb +430 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/schema.rb +36 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/secrets.rb +114 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/ssm.rb +111 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/tf.rb +90 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/commands/tunnel/services.yml +49 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/config.rb +27 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/ec2.rb +38 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/entry_point.rb +52 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/environment.rb +25 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/logger.rb +3 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/logs.rb +248 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/naming.rb +44 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/secrets.rb +276 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/stop_yubico_authenticator.rb +10 -0
- package/resources/legacy-ruby-cli/lib/pay_cli/ykman_oath_credential_config.rb +70 -0
- package/resources/legacy-ruby-cli/lib/zeitwerk_setup.rb +5 -0
- package/resources/legacy-ruby-cli/package-lock.json +6 -0
- package/resources/legacy-ruby-cli/rds_access/connect.sh +149 -0
- package/resources/legacy-ruby-cli/spec/.rubocop.yml +2 -0
- package/resources/legacy-ruby-cli/spec/fixtures/dockerfile_examples/Dockerfile.complex +34 -0
- package/resources/legacy-ruby-cli/spec/fixtures/dockerfile_examples/Dockerfile.complex_differing_froms +33 -0
- package/resources/legacy-ruby-cli/spec/fixtures/dockerfile_examples/Dockerfile.no_from +3 -0
- package/resources/legacy-ruby-cli/spec/fixtures/dockerfile_examples/Dockerfile.simple +5 -0
- package/resources/legacy-ruby-cli/spec/fixtures/dockerfile_examples/Dockerfile.simple_no_tag +5 -0
- package/resources/legacy-ruby-cli/spec/fixtures/dockerfile_examples/Dockerfile.with_sha +5 -0
- package/resources/legacy-ruby-cli/spec/fixtures/dockerfile_examples/Dockerfile.with_sha_no_tag +5 -0
- package/resources/legacy-ruby-cli/spec/lib/pay_cli/commands/local/image_extractor_spec.rb +55 -0
- package/resources/legacy-ruby-cli/spec/naming_spec.rb +83 -0
- package/resources/legacy-ruby-cli/spec/spec_helper.rb +106 -0
- package/resources/legacy-ruby-cli/vulnerability_scan/.nvmrc +1 -0
- package/resources/legacy-ruby-cli/vulnerability_scan/generate_vulnerability_report.js +91 -0
- package/resources/legacy-ruby-cli/vulnerability_scan/reports/.gitkeep +0 -0
- package/resources/legacy-ruby-cli/vulnerability_scan/scan.sh +57 -0
- package/src/commands/browse.js +2 -2
- package/src/commands/legacy.js +3 -2
- package/src/core/constants.js +7 -10
- 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,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
|