dynamocli 0.1.0 → 0.1.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b1efb4611b31d80e7ec45a7f4ff3ac153f20b4d597fdcf10bc42700b0fafc237
4
- data.tar.gz: 35d2937a8c46b67fbbfcfd26d329aeb65298bd057b5683648a6b6e7ed72229bf
3
+ metadata.gz: 613ace3c66e1db3954fef903bc0f86700787448328964687ebed39520dfe2205
4
+ data.tar.gz: e0fb394d2b04fc78928fe64eb7107301c91a5edcf17719ece5decb65007ca5ed
5
5
  SHA512:
6
- metadata.gz: 2145d414772764d63004a75c6d519281b58827de57796bf938efcdf24cd9dc122fb60c8d7abdf765820449588022e503d87fe2931dd09605fba4f4124855f7a2
7
- data.tar.gz: ded7bc87428451cabdd87b8e50bd382938ff5cad544d43ae477d50fc19ea87ddc795e818c97ade0047ad5eba3c2e29b26b3b0c22dc66f11d59d60b9e63023520
6
+ metadata.gz: fdd626d3441fbeaf0869542d356946f954b554d8daae402a908c15b059b7405f889d408f9ad67a4dfdbc72380e13ec20738a8e16ef743b235d3f75c55c662f29
7
+ data.tar.gz: 802b9d48e4b77c5a40675234c04f7057204601bfa7077f4f45c5646157481389a36f8cd12437b922850bf8b8f533bfbb7c6d655e17e38421932f01da42884009
data/.gitignore CHANGED
@@ -13,3 +13,5 @@
13
13
  .byebug_history
14
14
 
15
15
  *.gem
16
+
17
+ *.swp
data/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # CHANGELOG
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.1.2] - 2019-06-20
6
+ ### Added
7
+ - Command to erase a DynamoDB table.
8
+
9
+ ## [0.1.1] - 2019-05-21 [YANKED]
10
+
11
+ ## [0.1.0] - 2019-05-20
12
+ ### Added
13
+ - Command to import data from a CSV file to a DynamoDB table.
14
+
15
+ [0.1.0]: https://github.com/matheussilvasantos/dynamocli/releases/tag/v0.1.0
data/Gemfile.lock CHANGED
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dynamocli (0.1.0)
4
+ dynamocli (0.1.2)
5
+ aws-sdk-cloudformation
5
6
  aws-sdk-dynamodb (~> 1.28)
6
7
  thor (~> 0.20)
7
8
 
@@ -9,14 +10,17 @@ GEM
9
10
  remote: https://rubygems.org/
10
11
  specs:
11
12
  aws-eventstream (1.0.3)
12
- aws-partitions (1.164.0)
13
- aws-sdk-core (3.52.1)
13
+ aws-partitions (1.174.0)
14
+ aws-sdk-cloudformation (1.22.0)
15
+ aws-sdk-core (~> 3, >= 3.53.0)
16
+ aws-sigv4 (~> 1.1)
17
+ aws-sdk-core (3.54.2)
14
18
  aws-eventstream (~> 1.0, >= 1.0.2)
15
19
  aws-partitions (~> 1.0)
16
20
  aws-sigv4 (~> 1.1)
17
21
  jmespath (~> 1.0)
18
- aws-sdk-dynamodb (1.28.0)
19
- aws-sdk-core (~> 3, >= 3.52.1)
22
+ aws-sdk-dynamodb (1.30.0)
23
+ aws-sdk-core (~> 3, >= 3.53.0)
20
24
  aws-sigv4 (~> 1.1)
21
25
  aws-sigv4 (1.1.0)
22
26
  aws-eventstream (~> 1.0, >= 1.0.2)
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Dynamocli
2
2
 
3
- Utilites for interaction with your DynamoDB tables (only importation of data from a CSV file to a table is available for now).
3
+ Utilites for interaction with your DynamoDB tables.
4
4
 
5
5
  ## Installation
6
6
 
@@ -10,14 +10,52 @@ gem install dynamocli
10
10
 
11
11
  ## Usage
12
12
 
13
+
13
14
  You have to configure AWS in your computer first. The program will use the AWS credentials configured in your computer.
14
15
 
15
- After install the program you will be able to run:
16
+ - Import data from a CSV file to a DynamoDB table
17
+
18
+ ```
19
+ Usage:
20
+ dynamocli import FILE -t, --table, --to=TABLE
21
+
22
+ Options:
23
+ -t, --table, --to=TABLE # table you want to import the data
24
+
25
+ Description:
26
+ `dynamocli import` will import the data in from a file to a table specified.
27
+
28
+ > $ dynamo import users.csv --to users
29
+
30
+ ```
31
+
32
+ - Erase all the data of a DynamoDB table
16
33
 
17
34
  ```
18
- dynamocli import your_data.csv --to your_table
35
+ Usage:
36
+ dynamocli erase TABLE
37
+
38
+ Options:
39
+ [--with-drift], [--no-with-drift] # drop the table and
40
+ recreate it directly instead of use deployments
41
+
42
+ Description:
43
+ `dynamocli erase` will erase all the data of the specified table.
44
+
45
+ It will drop the table and recreate it.
46
+
47
+ If the table is in a stack it will try to deploy
48
+ the stack without the table and then redeploy the
49
+ stack with the original template. You can change
50
+ this behavior passing the option --with-drift.
51
+
52
+ > $ dynamo erase users
19
53
  ```
20
54
 
55
+ From the DynamoDB Guidelines for Working with Tables documentation:
56
+
57
+ > Deleting an entire table is significantly more efficient than removing items one-by-one, which essentially doubles the write throughput as you do as many delete operations as put operations.
58
+
21
59
  ## Development
22
60
 
23
61
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
23
23
 
24
24
  spec.add_dependency "thor", "~> 0.20"
25
25
  spec.add_dependency "aws-sdk-dynamodb", "~> 1.28"
26
+ spec.add_dependency "aws-sdk-cloudformation"
26
27
 
27
28
  spec.add_development_dependency "bundler", "~> 1.17"
28
29
  spec.add_development_dependency "rake", "~> 10.0"
data/lib/dynamocli.rb CHANGED
@@ -1,11 +1,13 @@
1
+ require "thor"
1
2
  require "dynamocli/version"
2
3
  require "dynamocli/import"
4
+ require "dynamocli/erase"
3
5
 
4
6
  module Dynamocli
5
7
  class Client < Thor
6
8
  desc "import FILE", "import data from FILE to dynamodb"
7
9
  long_desc <<-LONGDESC
8
- `dynamo import` will import the data in from a file
10
+ `dynamocli import` will import the data in from a file
9
11
  to a table specified.
10
12
 
11
13
  > $ dynamo import users.csv --to users
@@ -14,5 +16,22 @@ module Dynamocli
14
16
  def import(file)
15
17
  Dynamocli::Import.new(file: file, table: options[:to]).start
16
18
  end
19
+
20
+ desc "erase TABLE", "erase all the data from the DynamoDB TABLE"
21
+ long_desc <<-LONGDESC
22
+ `dynamocli erase` will erase all the data of the specified table.
23
+
24
+ It will drop the table and recreate it.
25
+
26
+ If the table is in a stack it will try to deploy the stack without
27
+ the table and then redeploy the stack with the original template.
28
+ You can change this behavior passing the option --with-drift.
29
+
30
+ > $ dynamo erase users
31
+ LONGDESC
32
+ option "with-drift", desc: "drop the table and recreate it directly instead of use deployments", type: :boolean
33
+ def erase(table)
34
+ Dynamocli::Erase.new(table_name: table, with_drift: options["with-drift"]).start
35
+ end
17
36
  end
18
37
  end
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "yaml"
5
+ require "aws-sdk-dynamodb"
6
+ require "aws-sdk-cloudformation"
7
+
8
+ class Dynamocli::Erase
9
+ def initialize(table_name:, with_drift: false)
10
+ @with_drift = with_drift
11
+ @table_name = table_name
12
+
13
+ @dynamodb = Aws::DynamoDB::Client.new
14
+ @cloudformation = Aws::CloudFormation::Client.new
15
+ @table = Aws::DynamoDB::Table.new(@table_name)
16
+
17
+ set_schema
18
+
19
+ @stack_resources = @cloudformation.describe_stack_resources(physical_resource_id: @table_name).to_h
20
+
21
+ set_stack_information
22
+ rescue Aws::DynamoDB::Errors::ResourceNotFoundException => e
23
+ STDERR.puts "ERROR: #{e.message}"
24
+ exit(42)
25
+ rescue Aws::CloudFormation::Errors::ValidationError
26
+ @stack_resources = nil
27
+ end
28
+
29
+ def start
30
+ erase_table
31
+ rescue Aws::CloudFormation::Errors::ValidationError => e
32
+ STDERR.puts "ERROR: #{e.message}"
33
+ exit(42)
34
+ end
35
+
36
+ private
37
+
38
+ def set_schema
39
+ @schema = @dynamodb.describe_table(table_name: @table_name).to_h[:table].tap do |schema|
40
+ schema.delete(:table_status)
41
+ schema.delete(:creation_date_time)
42
+ schema.delete(:table_size_bytes)
43
+ schema.delete(:item_count)
44
+ schema.delete(:table_arn)
45
+ schema.delete(:table_id)
46
+ schema[:provisioned_throughput].delete(:number_of_decreases_today)
47
+ end
48
+ end
49
+
50
+ def set_stack_information
51
+ return if @stack_resources.nil?
52
+
53
+ set_stack_name
54
+ set_stack
55
+ set_templates
56
+ rescue Aws::CloudFormation::Errors::ValidationError => e
57
+ STDERR.puts "ERROR: #{e.message}"
58
+ exit(42)
59
+ end
60
+
61
+ def set_stack_name
62
+ table_resource = @stack_resources[:stack_resources].find do |resource|
63
+ resource[:physical_resource_id] == @table_name
64
+ end
65
+ @stack_name = table_resource[:stack_name]
66
+ end
67
+
68
+ def set_stack
69
+ @stack = @cloudformation.describe_stacks(stack_name: @stack_name)[0][0]
70
+ end
71
+
72
+ def set_templates
73
+ template_body = @cloudformation.get_template(stack_name: @stack_name).to_h[:template_body]
74
+ @original_template = parse_template(template_body)
75
+ @template_without_table = parse_template(template_body)
76
+
77
+ tables = @original_template["Resources"].select { |_, v| v["Type"] == "AWS::DynamoDB::Table" }
78
+ table = tables.find { |_, v| v["Properties"]["TableName"] == @table_name }
79
+
80
+ if tables.nil?
81
+ STDERR.puts "ERROR: table #{@table_name} not found in the #{@stack_name} stack"
82
+ exit(42)
83
+ end
84
+
85
+ logical_resource_id = table.first
86
+ @template_without_table["Resources"].delete(logical_resource_id)
87
+ end
88
+
89
+ def parse_template(template)
90
+ JSON.parse(template)
91
+ rescue JSON::ParserError
92
+ YAML.load(template)
93
+ end
94
+
95
+ def erase_table
96
+ if @stack_resources.nil? || @with_drift
97
+ check_if_user_wants_to_continue_with_recreation
98
+ delete_and_recreate_the_table
99
+ else
100
+ check_if_user_wants_to_continue_with_deployment
101
+ erase_table_through_cloudformation
102
+ end
103
+ end
104
+
105
+ def check_if_user_wants_to_continue_with_recreation
106
+ STDOUT.print(
107
+ "WARNING: You're going to drop and recreate your #{@table_name} table,\n" \
108
+ "do you really want to continue?\n" \
109
+ "(anything other than 'y' will cancel) > "
110
+ )
111
+
112
+ confirmation = STDIN.gets.strip
113
+ return if confirmation == "y"
114
+
115
+ STDOUT.puts abort_message
116
+ exit(0)
117
+ end
118
+
119
+ def abort_message
120
+ "INFO: Erase of #{@table_name} table canceled"
121
+ end
122
+
123
+ def delete_and_recreate_the_table
124
+ delete_table
125
+ wait_for_deletion_to_complete
126
+ create_table
127
+ end
128
+
129
+ def delete_table
130
+ STDOUT.puts "INFO: Deleting the #{@table_name} table"
131
+
132
+ @table.delete
133
+
134
+ STDOUT.puts "INFO: #{@table_name} table deleted"
135
+ end
136
+
137
+ def wait_for_deletion_to_complete
138
+ waiting_seconds = 0
139
+ while get_table_status == "DELETING"
140
+ STDOUT.puts "INFO: Waiting for deletion to complete"
141
+ sleep waiting_seconds += 1
142
+ end
143
+ rescue Aws::DynamoDB::Errors::ResourceNotFoundException
144
+ true
145
+ end
146
+
147
+ def get_table_status
148
+ @dynamodb.describe_table(table_name: @table_name).table.table_status
149
+ end
150
+
151
+ def create_table
152
+ STDOUT.puts "INFO: Creating the #{@table_name} table"
153
+
154
+ @dynamodb.create_table(@schema)
155
+
156
+ STDOUT.puts "INFO: #{@table_name} table created"
157
+ end
158
+
159
+ def check_if_user_wants_to_continue_with_deployment
160
+ STDOUT.print(
161
+ "WARNING: You are going to deploy and redeploy your #{@stack_name} stack\n" \
162
+ "to drop and recreate the #{@table_name} table, do you really want to continue?\n" \
163
+ "(anything other than 'y' will cancel) > "
164
+ )
165
+
166
+ confirmation = STDIN.gets.strip
167
+ return if confirmation == "y"
168
+
169
+ STDOUT.puts abort_message
170
+ exit(0)
171
+ end
172
+
173
+ def erase_table_through_cloudformation
174
+ deploy_stack_without_the_table
175
+ wait_for_deployment_to_complete
176
+ deploy_stack_with_the_original_template
177
+ end
178
+
179
+ def deploy_stack_without_the_table
180
+ STDOUT.puts "INFO: Deploying the stack without the #{@table_name} table"
181
+
182
+ @cloudformation.update_stack(
183
+ stack_name: @stack_name,
184
+ template_body: @template_without_table.to_json,
185
+ parameters: @stack.parameters.map(&:to_h),
186
+ capabilities: @stack.capabilities,
187
+ role_arn: @stack.role_arn,
188
+ rollback_configuration: @stack.rollback_configuration.to_h,
189
+ stack_policy_body: get_stack_policy_body,
190
+ notification_arns: @stack.notification_arns,
191
+ tags: @stack.tags.map(&:to_h)
192
+ )
193
+
194
+ STDOUT.puts "INFO: Stack deployed without the #{@table_name} table"
195
+ end
196
+
197
+ def get_stack_policy_body
198
+ @cloudformation.get_stack_policy(stack_name: @stack_name).stack_policy_body
199
+ end
200
+
201
+ def wait_for_deployment_to_complete
202
+ waiting_seconds = 0
203
+ while get_stack_status != "UPDATE_COMPLETE"
204
+ STDOUT.puts "INFO: Waiting for deployment to complete"
205
+ sleep waiting_seconds += 1
206
+ end
207
+ end
208
+
209
+ def get_stack_status
210
+ @cloudformation.describe_stacks(stack_name: @stack_name)[0][0].stack_status
211
+ end
212
+
213
+ def deploy_stack_with_the_original_template
214
+ STDOUT.puts "INFO: Deploying the stack with the #{@table_name} table"
215
+
216
+ @cloudformation.update_stack(
217
+ stack_name: @stack_name,
218
+ template_body: @original_template.to_json,
219
+ parameters: @stack.parameters.map(&:to_h),
220
+ capabilities: @stack.capabilities,
221
+ role_arn: @stack.role_arn,
222
+ rollback_configuration: @stack.rollback_configuration.to_h,
223
+ stack_policy_body: get_stack_policy_body,
224
+ notification_arns: @stack.notification_arns,
225
+ tags: @stack.tags.map(&:to_h)
226
+ )
227
+
228
+ STDOUT.puts "INFO: Stack deployed with the #{@table_name} table"
229
+ end
230
+ end
@@ -1,4 +1,3 @@
1
- require "thor"
2
1
  require "csv"
3
2
  require "aws-sdk-dynamodb"
4
3
 
@@ -1,3 +1,3 @@
1
1
  module Dynamocli
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamocli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matheus Silva Santos de Oliveira
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-20 00:00:00.000000000 Z
11
+ date: 2019-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.28'
41
+ - !ruby/object:Gem::Dependency
42
+ name: aws-sdk-cloudformation
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -93,6 +107,7 @@ files:
93
107
  - ".gitignore"
94
108
  - ".rspec"
95
109
  - ".travis.yml"
110
+ - CHANGELOG.md
96
111
  - CODE_OF_CONDUCT.md
97
112
  - Gemfile
98
113
  - Gemfile.lock
@@ -102,8 +117,9 @@ files:
102
117
  - bin/console
103
118
  - bin/dynamocli
104
119
  - bin/setup
105
- - dynamorb.gemspec
120
+ - dynamocli.gemspec
106
121
  - lib/dynamocli.rb
122
+ - lib/dynamocli/erase.rb
107
123
  - lib/dynamocli/import.rb
108
124
  - lib/dynamocli/version.rb
109
125
  homepage: https://github.com/matheussilvasantos/dynamocli