aptible-cli 0.19.3 → 0.19.6

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
  SHA256:
3
- metadata.gz: f20b71eb3388732ade1e6e53cb1f076761d40a06d460faf08fa3e468f239acc1
4
- data.tar.gz: 3e603d02a1dbe3e8168209f9a0f061d29851a48189d28dc549bff3a7d203a5e1
3
+ metadata.gz: bc1a5d4d43d71792087a31f7994ed5a612dfe93ba90fd2ff3cebe63f5c8888a7
4
+ data.tar.gz: 7ac17167081e9edb3104b372a06622d736ce03051bb07279a690716eb7787ac9
5
5
  SHA512:
6
- metadata.gz: e195096e2854788c488477ca6d42fd1beb15ce2b6044f0be32a774e6b3915c387694e4608c7546a94915dcb32b3f0844f4864544e4a64806dca23379c0383598
7
- data.tar.gz: b2fb1ef99eb836cac0e80419d362277b05877826e8cf4b8f8cb78f937168d4ab658b81f9fea6c7ea0559cd5fe6f926aec896869b7fa8737e4503536d39dfb161
6
+ metadata.gz: b65bc6b0af2a8f88cc0fd72b47948ed01bbb3a32201ccaefd2b2dae3cdbe8042c6133e77862d79c63d4a0fa303460a5d453fa9f9a7ea7543af111a8101388fda
7
+ data.tar.gz: 7f8e3a443093b34b3b7428f249918e35327f92043cf0a35a248f7ef8aa8842f1a0d7ba8d48eeaa29f7d341be049f8f19e6b86d9d9ef91ea7401babaad2091fbf
data/.travis.yml CHANGED
@@ -2,10 +2,12 @@ dist: xenial
2
2
  sudo: false
3
3
 
4
4
  rvm:
5
- - "2.0"
6
5
  - "2.1"
7
6
  - "2.2"
8
7
  - "2.3"
8
+ - "2.4"
9
+ - "2.5"
10
+ - "2.6"
9
11
 
10
12
  script:
11
13
  - bundle exec rake
data/README.md CHANGED
@@ -28,70 +28,76 @@ From `aptible help`:
28
28
  <!-- BEGIN USAGE -->
29
29
  ```
30
30
  Commands:
31
- aptible apps # List all applications
32
- aptible apps:create HANDLE # Create a new application
33
- aptible apps:deprovision # Deprovision an app
34
- aptible apps:scale SERVICE [--container-count COUNT] [--container-size SIZE_MB] # Scale a service
35
- aptible backup:list DB_HANDLE # List backups for a database
36
- aptible backup:orphaned # List backups associated with deprovisioned databases
37
- aptible backup:purge BACKUP_ID # Permanently delete a backup and any copies of it
38
- aptible backup:restore BACKUP_ID [--environment ENVIRONMENT_HANDLE] [--handle HANDLE] [--container-size SIZE_MB] [--disk-size SIZE_GB] [--key-arn KEY_ARN] # Restore a backup
39
- aptible config # Print an app's current configuration
40
- aptible config:add [VAR1=VAL1] [VAR2=VAL2] [...] # Add an ENV variable to an app
41
- aptible config:rm [VAR1] [VAR2] [...] # Remove an ENV variable from an app
42
- aptible config:set [VAR1=VAL1] [VAR2=VAL2] [...] # Add an ENV variable to an app
43
- aptible config:unset [VAR1] [VAR2] [...] # Remove an ENV variable from an app
44
- aptible db:backup HANDLE # Backup a database
45
- aptible db:clone SOURCE DEST # Clone a database to create a new one
46
- aptible db:create HANDLE [--type TYPE] [--version VERSION] [--container-size SIZE_MB] [--disk-size SIZE_GB] [--key-arn KEY_ARN] # Create a new database
47
- aptible db:deprovision HANDLE # Deprovision a database
48
- aptible db:dump HANDLE [pg_dump options] # Dump a remote database to file
49
- aptible db:execute HANDLE SQL_FILE [--on-error-stop] # Executes sql against a database
50
- aptible db:list # List all databases
51
- aptible db:modify HANDLE [--iops IOPS] [--volume-type [gp2, gp3]] # Modify a database disk
52
- aptible db:reload HANDLE # Reload a database
53
- aptible db:replicate HANDLE REPLICA_HANDLE [--container-size SIZE_MB] [--disk-size SIZE_GB] [--logical --version VERSION] [--key-arn KEY_ARN] # Create a replica/follower of a database
54
- aptible db:restart HANDLE [--container-size SIZE_MB] [--disk-size SIZE_GB] [--iops IOPS] [--volume-type [gp2, gp3]] # Restart a database
55
- aptible db:tunnel HANDLE # Create a local tunnel to a database
56
- aptible db:url HANDLE # Display a database URL
57
- aptible db:versions # List available database versions
58
- aptible deploy [OPTIONS] [VAR1=VAL1] [VAR2=VAL2] [...] # Deploy an app
59
- aptible endpoints:database:create DATABASE # Create a Database Endpoint
60
- aptible endpoints:database:modify --database DATABASE ENDPOINT_HOSTNAME # Modify a Database Endpoint
61
- aptible endpoints:deprovision [--app APP | --database DATABASE] ENDPOINT_HOSTNAME # Deprovision an App or Database Endpoint
62
- aptible endpoints:https:create [--app APP] SERVICE # Create an App HTTPS Endpoint
63
- aptible endpoints:https:modify [--app APP] ENDPOINT_HOSTNAME # Modify an App HTTPS Endpoint
64
- aptible endpoints:list [--app APP | --database DATABASE] # List Endpoints for an App or Database
65
- aptible endpoints:renew [--app APP] ENDPOINT_HOSTNAME # Renew an App Managed TLS Endpoint
66
- aptible endpoints:tcp:create [--app APP] SERVICE # Create an App TCP Endpoint
67
- aptible endpoints:tcp:modify [--app APP] ENDPOINT_HOSTNAME # Modify an App TCP Endpoint
68
- aptible endpoints:tls:create [--app APP] SERVICE # Create an App TLS Endpoint
69
- aptible endpoints:tls:modify [--app APP] ENDPOINT_HOSTNAME # Modify an App TLS Endpoint
70
- aptible environment:ca_cert # Retrieve the CA certificate associated with the environment
71
- aptible environment:list # List all environments
72
- aptible help [COMMAND] # Describe available commands or one specific command
73
- aptible log_drain:create:datadog HANDLE --url DATADOG_URL --environment ENVIRONMENT [--drain-apps true/false] [--drain_databases true/false] [--drain_ephemeral_sessions true/false] [--drain_proxies true/false] # Create a Datadog Log Drain
74
- aptible log_drain:create:elasticsearch HANDLE --db DATABASE_HANDLE --environment ENVIRONMENT [--drain-apps true/false] [--drain_databases true/false] [--drain_ephemeral_sessions true/false] [--drain_proxies true/false] # Create an Elasticsearch Log Drain
75
- aptible log_drain:create:https HANDLE --url URL --environment ENVIRONMENT [--drain-apps true/false] [--drain_databases true/false] [--drain_ephemeral_sessions true/false] [--drain_proxies true/false] # Create a HTTPS Drain
76
- aptible log_drain:create:logdna HANDLE --url LOGDNA_URL --environment ENVIRONMENT [--drain-apps true/false] [--drain_databases true/false] [--drain_ephemeral_sessions true/false] [--drain_proxies true/false] # Create a LogDNA Log Drain
77
- aptible log_drain:create:papertrail HANDLE --host PAPERTRAIL_HOST --port PAPERTRAIL_PORT --environment ENVIRONMENT [--drain-apps true/false] [--drain_databases true/false] [--drain_ephemeral_sessions true/false] [--drain_proxies true/false] # Create a Papertrail Log Drain
78
- aptible log_drain:create:sumologic HANDLE --url SUMOLOGIC_URL --environment ENVIRONMENT [--drain-apps true/false] [--drain_databases true/false] [--drain_ephemeral_sessions true/false] [--drain_proxies true/false] # Create a Sumologic Drain
79
- aptible log_drain:create:syslog HANDLE --host SYSLOG_HOST --port SYSLOG_PORT [--token TOKEN] --environment ENVIRONMENT [--drain-apps true/false] [--drain_databases true/false] [--drain_ephemeral_sessions true/false] [--drain_proxies true/false] # Create a Papertrail Log Drain
80
- aptible log_drain:deprovision HANDLE --environment ENVIRONMENT # Deprovisions a log drain
81
- aptible log_drain:list # List all Log Drains
82
- aptible login # Log in to Aptible
83
- aptible logs [--app APP | --database DATABASE] # Follows logs from a running app or database
84
- aptible metric_drain:create:datadog HANDLE --api_key DATADOG_API_KEY --site DATADOG_SITE --environment ENVIRONMENT # Create a Datadog Metric Drain
85
- aptible metric_drain:create:influxdb HANDLE --db DATABASE_HANDLE --environment ENVIRONMENT # Create an InfluxDB Metric Drain
86
- aptible metric_drain:create:influxdb:custom HANDLE --username USERNAME --password PASSWORD --url URL_INCLUDING_PORT --db INFLUX_DATABASE_NAME --environment ENVIRONMENT # Create an InfluxDB Metric Drain
87
- aptible metric_drain:deprovision HANDLE --environment ENVIRONMENT # Deprovisions a Metric Drain
88
- aptible metric_drain:list # List all Metric Drains
89
- aptible operation:cancel OPERATION_ID # Cancel a running operation
90
- aptible rebuild # Rebuild an app, and restart its services
91
- aptible restart # Restart all services associated with an app
92
- aptible services # List Services for an App
93
- aptible ssh [COMMAND] # Run a command against an app
94
- aptible version # Print Aptible CLI version
31
+ aptible apps # List all applications
32
+ aptible apps:create HANDLE # Create a new application
33
+ aptible apps:deprovision # Deprovision an app
34
+ aptible apps:rename OLD_HANDLE NEW_HANDLE [--environment ENVIRONMENT_HANDLE] # Rename an app handle. In order for the new app handle to appear in log drain and metric drain destinations, you must restart the app.
35
+ aptible apps:scale SERVICE [--container-count COUNT] [--container-size SIZE_MB] # Scale a service
36
+ aptible backup:list DB_HANDLE # List backups for a database
37
+ aptible backup:orphaned # List backups associated with deprovisioned databases
38
+ aptible backup:purge BACKUP_ID # Permanently delete a backup and any copies of it
39
+ aptible backup:restore BACKUP_ID [--environment ENVIRONMENT_HANDLE] [--handle HANDLE] [--container-size SIZE_MB] [--disk-size SIZE_GB] [--key-arn KEY_ARN] # Restore a backup
40
+ aptible config # Print an app's current configuration
41
+ aptible config:add [VAR1=VAL1] [VAR2=VAL2] [...] # Add an ENV variable to an app
42
+ aptible config:rm [VAR1] [VAR2] [...] # Remove an ENV variable from an app
43
+ aptible config:set [VAR1=VAL1] [VAR2=VAL2] [...] # Add an ENV variable to an app
44
+ aptible config:unset [VAR1] [VAR2] [...] # Remove an ENV variable from an app
45
+ aptible db:backup HANDLE # Backup a database
46
+ aptible db:clone SOURCE DEST # Clone a database to create a new one
47
+ aptible db:create HANDLE [--type TYPE] [--version VERSION] [--container-size SIZE_MB] [--disk-size SIZE_GB] [--key-arn KEY_ARN] # Create a new database
48
+ aptible db:deprovision HANDLE # Deprovision a database
49
+ aptible db:dump HANDLE [pg_dump options] # Dump a remote database to file
50
+ aptible db:execute HANDLE SQL_FILE [--on-error-stop] # Executes sql against a database
51
+ aptible db:list # List all databases
52
+ aptible db:modify HANDLE [--iops IOPS] [--volume-type [gp2, gp3]] # Modify a database disk
53
+ aptible db:reload HANDLE # Reload a database
54
+ aptible db:rename OLD_HANDLE NEW_HANDLE [--environment ENVIRONMENT_HANDLE] # Rename a database handle. In order for the new database handle to appear in log drain and metric drain destinations, you must reload the database.
55
+ aptible db:replicate HANDLE REPLICA_HANDLE [--container-size SIZE_MB] [--disk-size SIZE_GB] [--logical --version VERSION] [--key-arn KEY_ARN] # Create a replica/follower of a database
56
+ aptible db:restart HANDLE [--container-size SIZE_MB] [--disk-size SIZE_GB] [--iops IOPS] [--volume-type [gp2, gp3]] # Restart a database
57
+ aptible db:tunnel HANDLE # Create a local tunnel to a database
58
+ aptible db:url HANDLE # Display a database URL
59
+ aptible db:versions # List available database versions
60
+ aptible deploy [OPTIONS] [VAR1=VAL1] [VAR2=VAL2] [...] # Deploy an app
61
+ aptible endpoints:database:create DATABASE # Create a Database Endpoint
62
+ aptible endpoints:database:modify --database DATABASE ENDPOINT_HOSTNAME # Modify a Database Endpoint
63
+ aptible endpoints:deprovision [--app APP | --database DATABASE] ENDPOINT_HOSTNAME # Deprovision an App or Database Endpoint
64
+ aptible endpoints:https:create [--app APP] SERVICE # Create an App HTTPS Endpoint
65
+ aptible endpoints:https:modify [--app APP] ENDPOINT_HOSTNAME # Modify an App HTTPS Endpoint
66
+ aptible endpoints:list [--app APP | --database DATABASE] # List Endpoints for an App or Database
67
+ aptible endpoints:renew [--app APP] ENDPOINT_HOSTNAME # Renew an App Managed TLS Endpoint
68
+ aptible endpoints:tcp:create [--app APP] SERVICE # Create an App TCP Endpoint
69
+ aptible endpoints:tcp:modify [--app APP] ENDPOINT_HOSTNAME # Modify an App TCP Endpoint
70
+ aptible endpoints:tls:create [--app APP] SERVICE # Create an App TLS Endpoint
71
+ aptible endpoints:tls:modify [--app APP] ENDPOINT_HOSTNAME # Modify an App TLS Endpoint
72
+ aptible environment:ca_cert # Retrieve the CA certificate associated with the environment
73
+ aptible environment:list # List all environments
74
+ aptible environment:rename OLD_HANDLE NEW_HANDLE # Rename an environment handle. In order for the new environment handle to appear in log drain/metric destinations, you must restart the apps/databases in this environment.
75
+ aptible help [COMMAND] # Describe available commands or one specific command
76
+ aptible log_drain:create:datadog HANDLE --url DATADOG_URL --environment ENVIRONMENT [--drain-apps true/false] [--drain_databases true/false] [--drain_ephemeral_sessions true/false] [--drain_proxies true/false] # Create a Datadog Log Drain
77
+ aptible log_drain:create:elasticsearch HANDLE --db DATABASE_HANDLE --environment ENVIRONMENT [--drain-apps true/false] [--drain_databases true/false] [--drain_ephemeral_sessions true/false] [--drain_proxies true/false] # Create an Elasticsearch Log Drain
78
+ aptible log_drain:create:https HANDLE --url URL --environment ENVIRONMENT [--drain-apps true/false] [--drain_databases true/false] [--drain_ephemeral_sessions true/false] [--drain_proxies true/false] # Create a HTTPS Drain
79
+ aptible log_drain:create:logdna HANDLE --url LOGDNA_URL --environment ENVIRONMENT [--drain-apps true/false] [--drain_databases true/false] [--drain_ephemeral_sessions true/false] [--drain_proxies true/false] # Create a LogDNA Log Drain
80
+ aptible log_drain:create:papertrail HANDLE --host PAPERTRAIL_HOST --port PAPERTRAIL_PORT --environment ENVIRONMENT [--drain-apps true/false] [--drain_databases true/false] [--drain_ephemeral_sessions true/false] [--drain_proxies true/false] # Create a Papertrail Log Drain
81
+ aptible log_drain:create:sumologic HANDLE --url SUMOLOGIC_URL --environment ENVIRONMENT [--drain-apps true/false] [--drain_databases true/false] [--drain_ephemeral_sessions true/false] [--drain_proxies true/false] # Create a Sumologic Drain
82
+ aptible log_drain:create:syslog HANDLE --host SYSLOG_HOST --port SYSLOG_PORT [--token TOKEN] --environment ENVIRONMENT [--drain-apps true/false] [--drain_databases true/false] [--drain_ephemeral_sessions true/false] [--drain_proxies true/false] # Create a Papertrail Log Drain
83
+ aptible log_drain:deprovision HANDLE --environment ENVIRONMENT # Deprovisions a log drain
84
+ aptible log_drain:list # List all Log Drains
85
+ aptible login # Log in to Aptible
86
+ aptible logs [--app APP | --database DATABASE] # Follows logs from a running app or database
87
+ aptible logs_from_archive --bucket NAME --region REGION --stack NAME [ --decryption-keys ONE [OR MORE] ] [ --download-location LOCATION ] [ [ --string-matches ONE [OR MORE] ] | [ --app-id ID | --database-id ID | --endpoint-id ID | --container-id ID ] [ --start-date YYYY-MM-DD --end-date YYYY-MM-DD ] ] --bucket=BUCKET --region=REGION --stack=STACK # Retrieves container logs from an S3 archive in your own AWS account. You must provide your AWS credentials via the environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
88
+ aptible metric_drain:create:datadog HANDLE --api_key DATADOG_API_KEY --site DATADOG_SITE --environment ENVIRONMENT # Create a Datadog Metric Drain
89
+ aptible metric_drain:create:influxdb HANDLE --db DATABASE_HANDLE --environment ENVIRONMENT # Create an InfluxDB Metric Drain
90
+ aptible metric_drain:create:influxdb:custom HANDLE --username USERNAME --password PASSWORD --url URL_INCLUDING_PORT --db INFLUX_DATABASE_NAME --environment ENVIRONMENT # Create an InfluxDB Metric Drain
91
+ aptible metric_drain:deprovision HANDLE --environment ENVIRONMENT # Deprovisions a Metric Drain
92
+ aptible metric_drain:list # List all Metric Drains
93
+ aptible operation:cancel OPERATION_ID # Cancel a running operation
94
+ aptible operation:follow OPERATION_ID # Follow logs of a running operation
95
+ aptible operation:logs OPERATION_ID # View logs for given operation
96
+ aptible rebuild # Rebuild an app, and restart its services
97
+ aptible restart # Restart all services associated with an app
98
+ aptible services # List Services for an App
99
+ aptible ssh [COMMAND] # Run a command against an app
100
+ aptible version # Print Aptible CLI version
95
101
  ```
96
102
  <!-- END USAGE -->
97
103
 
data/aptible-cli.gemspec CHANGED
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_dependency 'term-ansicolor'
30
30
  spec.add_dependency 'chronic_duration', '~> 0.10.6'
31
31
  spec.add_dependency 'cbor'
32
+ spec.add_dependency 'aws-sdk', '~> 2.0'
32
33
 
33
34
  # Temporarily pin ffi until https://github.com/ffi/ffi/issues/868 is fixed
34
35
  spec.add_dependency 'ffi', '<= 1.14.1' if Gem.win_platform?
@@ -21,6 +21,7 @@ require_relative 'helpers/security_key'
21
21
  require_relative 'helpers/config_path'
22
22
  require_relative 'helpers/log_drain'
23
23
  require_relative 'helpers/metric_drain'
24
+ require_relative 'helpers/s3_log_helpers'
24
25
 
25
26
  require_relative 'subcommands/apps'
26
27
  require_relative 'subcommands/config'
@@ -154,6 +154,19 @@ module Aptible
154
154
  raise Thor::Error, err
155
155
  end
156
156
 
157
+ def validate_image_type(type)
158
+ available_types = []
159
+
160
+ Aptible::Api::DatabaseImage.all(token: fetch_token).each do |i|
161
+ return true if i.type == type
162
+ available_types << i.type
163
+ end
164
+
165
+ err = "No Database Image of type \"#{type}\""
166
+ err = "#{err}, valid types: #{available_types.uniq.join(', ')}"
167
+ raise Thor::Error, err
168
+ end
169
+
157
170
  def render_database(database, account)
158
171
  Formatter.render(Renderer.current) do |root|
159
172
  root.keyed_object('connection_url') do |node|
@@ -1,4 +1,5 @@
1
1
  require 'aptible/api'
2
+ require 'net/http'
2
3
 
3
4
  module Aptible
4
5
  module CLI
@@ -48,6 +49,15 @@ module Aptible
48
49
  operation.update!(cancelled: true)
49
50
  end
50
51
 
52
+ def operation_logs(operation)
53
+ res = get_operation_logs_redirect(operation)
54
+ s3_file_request = get_operation_logs_s3_file(res.body)
55
+
56
+ m = "Printing out results of operation logs for #{operation.id}"
57
+ CLI.logger.info m
58
+ puts s3_file_request.body
59
+ end
60
+
51
61
  def prettify_operation(o)
52
62
  bits = [o.status, o.type, "##{o.id}"]
53
63
  if o.resource.respond_to?(:handle)
@@ -55,6 +65,37 @@ module Aptible
55
65
  end
56
66
  bits.join ' '
57
67
  end
68
+
69
+ private
70
+
71
+ def get_operation_logs_redirect(operation)
72
+ uri = URI(operation.logs_url)
73
+ headers = { 'Authorization' => "Bearer #{fetch_token}" }
74
+ http = Net::HTTP.new(uri.host, uri.port)
75
+ http.use_ssl = true
76
+ res = http.request(Net::HTTP::Get.new(uri.request_uri, headers))
77
+ # note: res body with a 200 is target redirect location for download
78
+ if !res || res.code != '200' || res.body.nil?
79
+ raise Thor::Error, 'Unable to retrieve the operation\'s logs. '\
80
+ 'If the issue persists please contact support for assistance.'
81
+ end
82
+ res
83
+ end
84
+
85
+ def get_operation_logs_s3_file(location)
86
+ s3_uri = URI(location)
87
+ http = Net::HTTP.new(s3_uri.host, s3_uri.port)
88
+ http.use_ssl = true
89
+
90
+ # follow the link with redirect and retrieve it from s3 directly
91
+ res = http.request(Net::HTTP::Get.new(s3_uri.request_uri))
92
+ if !res || res.code != '200'
93
+ raise Thor::Error, 'Unable to retrieve operation logs, '\
94
+ "S3 returned response code #{res.code}. "\
95
+ 'If the issue persists please contact support for assistance.'
96
+ end
97
+ res
98
+ end
58
99
  end
59
100
  end
60
101
  end
@@ -0,0 +1,225 @@
1
+ require 'aws-sdk'
2
+ require 'pathname'
3
+
4
+ module Aptible
5
+ module CLI
6
+ module Helpers
7
+ module S3LogHelpers
8
+ def ensure_aws_creds
9
+ cred_errors = []
10
+ unless ENV['AWS_ACCESS_KEY_ID']
11
+ cred_errors << 'Missing environment variable: AWS_ACCESS_KEY_ID'
12
+ end
13
+ unless ENV['AWS_SECRET_ACCESS_KEY']
14
+ cred_errors << 'Missing environment variable: AWS_SECRET_ACCESS_KEY'
15
+ end
16
+ raise Thor::Error, cred_errors.join(' ') if cred_errors.any?
17
+ end
18
+
19
+ def validate_log_search_options(options = {})
20
+ id_options = [
21
+ options[:app_id],
22
+ options[:database_id],
23
+ options[:endpoint_id],
24
+ options[:container_id]
25
+ ]
26
+ date_options = [options[:start_date], options[:end_date]]
27
+ unless options[:string_matches] || id_options.any?
28
+ m = 'You must specify an option to identify the logs to download,' \
29
+ ' either: --string-matches, --app-id, --database-id,' \
30
+ ' --endpoint-id, or --container-id'
31
+ raise Thor::Error, m
32
+ end
33
+
34
+ m = 'You cannot pass --app-id, --database-id, --endpoint-id, or ' \
35
+ '--container-id when using --string-matches.'
36
+ raise Thor::Error, m if options[:string_matches] && id_options.any?
37
+
38
+ m = 'You must specify only one of ' \
39
+ '--app-id, --database-id, --endpoint-id or --container-id'
40
+ raise Thor::Error, m if id_options.any? && !id_options.one?
41
+
42
+ m = 'The options --start-date/--end-date cannot be used when ' \
43
+ 'searching by string'
44
+ raise Thor::Error, m if options[:string_matches] && date_options.any?
45
+
46
+ m = 'You must pass both --start-date and --end-date'
47
+ raise Thor::Error, m if date_options.any? && !date_options.all?
48
+
49
+ if options[:container_id] && options[:container_id].length < 12
50
+ m = 'You must specify at least the first 12 characters of the ' \
51
+ 'container ID'
52
+ raise Thor::Error, m
53
+ end
54
+
55
+ if options[:download_location] && !options[:decryption_keys]
56
+ m = 'You must provide decryption keys with the --decryption-keys' \
57
+ 'option in order to download files.'
58
+ raise Thor::Error, m
59
+ end
60
+ end
61
+
62
+ def info_from_path(file)
63
+ properties = {}
64
+
65
+ properties[:stack], _, properties[:schema],
66
+ properties[:shasum], type_id, *remainder = file.split('/')
67
+
68
+ properties[:id] = type_id.split('-').last.to_i
69
+ properties[:type] = type_id.split('-').first
70
+
71
+ case properties[:schema]
72
+ when 'v2'
73
+ # Eliminate the extensions
74
+ split_by_dot = remainder.pop.split('.') - %w(log bck gz)
75
+ properties[:container_id] = split_by_dot.first.delete!('-json')
76
+ properties[:uploaded_at] = utc_datetime(split_by_dot.last)
77
+ when 'v3'
78
+ case properties[:type]
79
+ when 'apps'
80
+ properties[:service_id] = remainder.first.split('-').last.to_i
81
+ file_name = remainder.second
82
+ else
83
+ file_name = remainder.first
84
+ end
85
+ # The file name may have differing number of elements due to
86
+ # docker file log rotation. So we eliminate some useless items
87
+ # and then work from the beginning or end of the remaining to find
88
+ # known elements, ignoring any .1 .2 (or none at all) extension
89
+ # found in the middle of the file name. EG:
90
+ # ['container_id', 'start_time', 'end_time']
91
+ # or
92
+ # ['container_id', '.1', 'start_time', 'end_time']]
93
+ split_by_dot = file_name.split('.') - %w(log gz archived)
94
+ properties[:container_id] = split_by_dot.first.delete!('-json')
95
+ properties[:start_time] = utc_datetime(split_by_dot[-2])
96
+ properties[:end_time] = utc_datetime(split_by_dot[-1])
97
+ else
98
+ m = "Cannot determine aptible log naming schema from #{file}"
99
+ raise Thor::Error, m
100
+ end
101
+ properties
102
+ end
103
+
104
+ def decrypt_and_translate_s3_file(file, enc_key, region, bucket, path)
105
+ # AWS warns us about using the legacy encryption schema
106
+ s3 = Kernel.silence_warnings do
107
+ Aws::S3::EncryptionV2::Client.new(
108
+ encryption_key: enc_key, region: region,
109
+ key_wrap_schema: :aes_gcm,
110
+ content_encryption_schema: :aes_gcm_no_padding,
111
+ security_profile: :v2_and_legacy
112
+ )
113
+ end
114
+
115
+ # Just write it to a file directly
116
+ location = File.join(path, file.split('/').drop(4).join('/'))
117
+ FileUtils.mkdir_p(File.dirname(location))
118
+ File.open(location, 'wb') do |f|
119
+ CLI.logger.info location
120
+ # Is this memory efficient?
121
+ s3.get_object(bucket: bucket, key: file, response_target: f)
122
+ end
123
+ end
124
+
125
+ def find_s3_files_by_string_match(region, bucket, stack, strings)
126
+ # This function just regex matches a provided string anywhwere
127
+ # in the s3 path
128
+ begin
129
+ stack_logs = s3_client(region).bucket(bucket)
130
+ .objects(prefix: stack)
131
+ .map(&:key)
132
+ rescue => error
133
+ raise Thor::Error, error.message
134
+ end
135
+ strings.each do |s|
136
+ stack_logs = stack_logs.select { |f| f =~ /#{s}/ }
137
+ end
138
+ stack_logs
139
+ end
140
+
141
+ def find_s3_files_by_attrs(region, bucket, stack,
142
+ attrs, time_range = nil)
143
+ # This function uses the known path schema to return files matching
144
+ # any provided criteria. EG:
145
+ # * attrs: { :type => 'app', :id => 123 }
146
+ # * attrs: { :container_id => 'deadbeef' }
147
+
148
+ begin
149
+ stack_logs = s3_client(region).bucket(bucket)
150
+ .objects(prefix: stack)
151
+ .map(&:key)
152
+ rescue => error
153
+ raise Thor::Error, error.message
154
+ end
155
+ attrs.each do |k, v|
156
+ stack_logs = stack_logs.select do |f|
157
+ if k == :container_id
158
+ # Match short container IDs
159
+ info_from_path(f)[k].start_with?(v)
160
+ else
161
+ info_from_path(f)[k] == v
162
+ end
163
+ end
164
+ end
165
+
166
+ if time_range
167
+ # select only logs within the time range
168
+ stack_logs = stack_logs.select do |f|
169
+ info = info_from_path(f)
170
+ first_log = info[:start_time]
171
+ last_log = info[:end_time]
172
+ if first_log.nil? || last_log.nil?
173
+ m = 'Cannot determine precise timestamps of file: ' \
174
+ "#{f.split('/').drop(4).join('/')}"
175
+ CLI.logger.warn m
176
+ false
177
+ else
178
+ time_match?(time_range, first_log, last_log)
179
+ end
180
+ end
181
+ end
182
+
183
+ stack_logs
184
+ end
185
+
186
+ def time_match?(time_range, start_timestamp, end_timestamp)
187
+ return false if start_timestamp.nil? || end_timestamp.nil?
188
+ return false if time_range.last < start_timestamp
189
+ return false if time_range.first > end_timestamp
190
+ true
191
+ end
192
+
193
+ def utc_date(date_string)
194
+ t_fmt = '%Y-%m-%d %Z'
195
+ Time.strptime("#{date_string} UTC", t_fmt)
196
+ rescue ArgumentError
197
+ raise Thor::Error, 'Please provide dates in YYYY-MM-DD format'
198
+ end
199
+
200
+ def utc_datetime(datetime_string)
201
+ Time.parse("#{datetime_string}Z")
202
+ rescue ArgumentError
203
+ nil
204
+ end
205
+
206
+ def encryption_key(filesum, possible_keys)
207
+ # The key can be determined from the sum
208
+ possible_keys.each do |k|
209
+ keysum = Digest::SHA256.hexdigest(Base64.strict_decode64(k))
210
+ next unless keysum == filesum
211
+ return Base64.strict_decode64(k)
212
+ end
213
+ m = "Did not find a matching key for shasum #{filesum}"
214
+ raise Thor::Error, m
215
+ end
216
+
217
+ def s3_client(region)
218
+ @s3_client ||= Kernel.silence_warnings do
219
+ Aws::S3::Resource.new(region: region)
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end
@@ -94,6 +94,29 @@ module Aptible
94
94
  raise if e.response.status != 404
95
95
  end
96
96
  end
97
+
98
+ desc 'apps:rename OLD_HANDLE NEW_HANDLE [--environment'\
99
+ ' ENVIRONMENT_HANDLE]', 'Rename an app handle. In order'\
100
+ ' for the new app handle to appear in log drain and metric'\
101
+ ' drain destinations, you must restart the app.'
102
+ option :environment
103
+ define_method 'apps:rename' do |old_handle, new_handle|
104
+ env = ensure_environment(options)
105
+ app = ensure_app(options.merge(app: old_handle))
106
+ app.update!(handle: new_handle)
107
+ m1 = "In order for the new app name (#{new_handle}) to appear"\
108
+ ' in log drain and metric drain destinations, you must'\
109
+ ' restart the app.'
110
+ m2 = 'You can restart your app with this command: "aptible '\
111
+ "restart --app #{new_handle} --environment #{env.handle}\""
112
+ m3 = 'Warning - Git remote addresses must be updated to match'\
113
+ ' the new handle, if using Dockerfile deploy. '\
114
+ "(git@beta.aptible.com:#{app.account.handle}"\
115
+ "/#{new_handle}.git)"
116
+ CLI.logger.warn m1
117
+ CLI.logger.info m2
118
+ CLI.logger.warn m3
119
+ end
97
120
  end
98
121
  end
99
122
  end