volume_sweeper 1.0.1 → 2.0.0

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: e0e43d0e652b965e49ef224c43f51f8435aee3e8d3b799a6aeeab02ee6eb2632
4
- data.tar.gz: 7d2a5fcf66205a476774cb23aa5d415d9e97e1354feefe2a5e3355e7d5f3f121
3
+ metadata.gz: 69061fddbb0ab861a6e3061356f675e420dc37b899c6e997555be5f1f55bcf13
4
+ data.tar.gz: 51c187ffb879566b2faf1d4e71e8fb6651d9bee3e4bccd4240348f8d7de2139e
5
5
  SHA512:
6
- metadata.gz: 4f6f7d8f1bf11ffac5881035bd39c11c949dd836b61e8a13f1cb5770eb604257e12f6123e06a5efe8f55849235b438986a85a9c4b3b12e9868551eb2f90d1225
7
- data.tar.gz: c6f169797f6ed188ad4b78be7425ee55debb72b7551557340c48bd60ee553a6c94569c7c43bea810ac0717bb9fa87b588186b90fb456a82ed68907eeeed48d39
6
+ metadata.gz: 8e089dee93af6b64d99c02328087732ef139bc3b91fae9e0b3ab3a7c14b895e8060e2e0c2b1942099cf3e26bfc482cc1c457c094eaee96a37ccc6eaa5908c5e9
7
+ data.tar.gz: 29e41d99d2f45464d6cb281a4821e06dfc2454886c1355f8ae30103dfca7ad3d34ecc1fc7a524f87add8f5af859062fd23f9cda2d463b0ad535ccc1ee71c0948
data/README.md CHANGED
@@ -7,7 +7,7 @@ A tool to scan and clean cloud infrastruture for unattached block volumes withou
7
7
  ## Supported Clouds
8
8
 
9
9
  - [x] OCI
10
- - [ ] AWS.
10
+ - [x] AWS.
11
11
  - [ ] GCP.
12
12
 
13
13
  ## Supported Kubernetes
@@ -31,7 +31,7 @@ $ gem install volume_sweeper
31
31
  To scan and generate a report:
32
32
 
33
33
  ```bash
34
- volume_sweeper --account-id <ID> --cloud aws|oci
34
+ volume_sweeper --account-id <ID> --cloud aws|oci --region <region>
35
35
  ```
36
36
 
37
37
  To apply deletion for unattached block volumes:
@@ -40,6 +40,12 @@ To apply deletion for unattached block volumes:
40
40
  volume_sweeper --mode delete
41
41
  ```
42
42
 
43
+ For all options:
44
+
45
+ ```bash
46
+ volume_sweeper -h
47
+ ```
48
+
43
49
  ## Contributing
44
50
 
45
51
  Bug reports and pull requests are welcome on GitHub at https://github.com/abarrak/volume_sweeper.
@@ -29,6 +29,7 @@ module VolumeSweeper
29
29
  opt.on('-m', '--mode [MODE]', 'The run modes: either audit, or delete.') { |o| options.mode = o }
30
30
  opt.on('-c', '--cloud [CLOUD]', 'Supported clouds: aws, oci.') { |o| options.cloud = o }
31
31
  opt.on('-f', '--config-path [PATH]', 'The file location for cloud config file') { |o| options.config_path = o }
32
+ opt.on('-s', '--creds-path [PATH]', 'The file location for cloud crednetials file') { |o| options.creds_path = o }
32
33
  opt.on('-r', '--region [REGION]', 'The provider region of the account.') { |o| options.region = o }
33
34
  opt.on('-a', '--account-id [Id]', 'The account or compartment Id.') { |o| options.account_id = o }
34
35
  opt.on('-d', '--released-since [DAYS]', 'Volumes threshold duration') { |o| options.released_in_days = o }
@@ -17,6 +17,7 @@ module VolumeSweeper
17
17
  mode = opts.mode&.to_sym
18
18
  options = {
19
19
  config_path: opts.config_path,
20
+ creds_path: opts.creds_path,
20
21
  account_id: opts.account_id,
21
22
  region: opts.region,
22
23
  mode: opts.mode,
@@ -1,4 +1,5 @@
1
1
  require 'active_support/core_ext/object/blank'
2
+ require 'aws-sdk-ec2'
2
3
  require_relative 'base'
3
4
  require_relative '../utils/log'
4
5
 
@@ -6,26 +7,80 @@ module VolumeSweeper
6
7
  module Providers
7
8
 
8
9
  class Aws < Base
10
+
9
11
  DEFAULT_REGION = 'us-west-2'
12
+ DEFAULT_CONFIF_PATH = '~/.aws/config'
13
+ DEFAULT_CREDS_PATH = '~/.aws/credentials'
14
+
15
+ BASE_CONSOLE_URL = "console.aws.amazon.com/ec2/home"
16
+ VOLUME_ATTRS = %i{ volume_id displayName state size attachments create_time availability_zone tags }
10
17
 
11
18
  def initialize config_path: nil, region: nil, mode: :audit, **kwargs
12
19
  super
13
20
  @region ||= DEFAULT_REGION
21
+ set_console_base_url
14
22
  validate_attrs
23
+ prepare_config config_path, kwargs[:creds_path]
15
24
  end
16
25
 
17
26
  def scan_block_volumes
18
- raise NotImplementedError
27
+ volumes = Array.new
28
+ next_token = nil
29
+ opts = { max_results: 200 }
30
+
31
+ run_api_call do |client|
32
+ loop do
33
+ response = client.describe_volumes opts.merge(next_token: next_token)
34
+ response&.volumes&.map do |v|
35
+ volumes << v.to_hash.compact.slice(*VOLUME_ATTRS).transform_keys(volume_id: :id)
36
+ end
37
+ break if response.nil? || response.next_token.nil?
38
+ next_token = response.next_token
39
+ sleep 2
40
+ end
41
+ @log.msg "aws: collected #{volumes.size} block volumes from the account."
42
+ end
43
+
44
+ @log.msg "aws: filtering out any block volume with an active attachment."
45
+ result = volumes&.reject { |v| v[:state] != 'available' || v[:attachments]&.count > 0 } || []
46
+
47
+ @log.msg "aws: found #{result.count} unattached block volumes."
48
+ [volumes.size, result]
19
49
  end
20
50
 
21
51
  def delete_block_volumes ids_list
22
- return if ids_list.blank? || @run_mode != :delete
23
- raise NotImplementedError
52
+ @log.msg "aws: #{ids_list&.count || 0} block volumes are eligible for cleanup."
53
+ return if ids_list.blank?
54
+
55
+ unless @run_mode == :delete
56
+ @log.msg "aws: running in :#{@run_mode} mode, exiting without delete operations."
57
+ return
58
+ end
59
+
60
+ @log.msg "aws: unused volume clean-up operation started."
61
+
62
+ ids_list.each do |id|
63
+ @log.msg "aws: deleting block volume #{id} .."
64
+ run_api_call do |client|
65
+ output = client.delete_volume({ volume_id: id.to_s })
66
+ if output&.successful?
67
+ @log.msg "aws: block volume #{id} is deleted successfully."
68
+ else
69
+ @log.msg "aws: block volume #{id} has failed."
70
+ end
71
+ sleep 2.5
72
+ end
73
+ end
24
74
  end
25
75
 
26
76
  private
27
77
 
28
- def prepare_config
78
+ def prepare_config config_path, creds_path
79
+ # SDK automtically picks up config and credentails files in the
80
+ # default place for various methods.
81
+ @config_location ||= DEFAULT_CONFIF_PATH
82
+ @creds_location ||= DEFAULT_CREDS_PATH
83
+ ::Aws.config.update({ region: @region })
29
84
  end
30
85
 
31
86
  def validate_attrs
@@ -33,6 +88,25 @@ module VolumeSweeper
33
88
  @log.msg "provider error: aws account id is not assigned", level: :error
34
89
  exit 1
35
90
  end
91
+
92
+ def run_api_call
93
+ current_tries = 0
94
+ ec2_client = ::Aws::EC2::Client.new(region: @region, account_id: @account_id)
95
+
96
+ yield ec2_client if block_given?
97
+
98
+ rescue ::Aws::EC2::Errors::ServiceError => err
99
+ # https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/EBS/Errors.html
100
+ @log.msg errr&.context&.data, level: :error
101
+ raise if err&.code.to_s != '304'
102
+ rescue StandardError => err
103
+ @log.msg err, level: :error
104
+ raise
105
+ end
106
+
107
+ def set_console_base_url
108
+ @base_link = "https://#{@region}.#{BASE_CONSOLE_URL}?region=#{@region}#VolumeDetails:volumeId="
109
+ end
36
110
  end
37
111
 
38
112
  end
@@ -7,6 +7,7 @@ module VolumeSweeper
7
7
  def initialize **kwargs
8
8
  @run_mode = kwargs[:mode]&.to_sym || :audit
9
9
  @config_location = kwargs[:config_path]
10
+ @creds_location = kwargs[:creds_location]
10
11
  @account_id = kwargs[:account_id]
11
12
  @compartment_id = kwargs[:account_id]
12
13
  @region = kwargs[:region]
@@ -19,7 +19,7 @@ module VolumeSweeper
19
19
  def initialize config_path: nil, region: nil, mode: :audit, **kwargs
20
20
  super
21
21
  @region ||= DEFAULT_REGION
22
- @base_link = "https://cloud.oracle.com/block-storage/volumes"
22
+ @base_link = "https://cloud.oracle.com/block-storage/volumes/"
23
23
  validate_attrs
24
24
  end
25
25
 
@@ -113,20 +113,50 @@ module VolumeSweeper
113
113
  }
114
114
  .content {
115
115
  margin: 20px 20px 0 20px;
116
+ margin: 0 20px 0 20px;
116
117
  padding: 10px;
117
118
  background-color: white;
118
119
  }
120
+ .header {
121
+ margin: 20px 20px 0 20px;
122
+ padding: 10px 10px 42px 10px;
123
+ border: 0;
124
+ text-align: right;
125
+ background-color: rgb(226, 225, 225);
126
+ color: rgb(69, 69, 69);
127
+ font-family: Georgia, 'Times New Roman', Times, serif, Times;
128
+ font-size: 16px;
129
+ }
130
+ .header p {
131
+ text-align: right;
132
+ margin: 0;
133
+ margin-top: 6px;
134
+ margin-right: 20px;
135
+ float: right;
136
+ display: block;
137
+ }
138
+ .header img {
139
+ margin: 0;
140
+ padding: 0;
141
+ border: 0;
142
+ float: left;
143
+ }
119
144
  .foot {
120
145
  margin: 0 20px 20px 20px;
121
146
  padding: 5px;
122
147
  border: 0;
123
148
  text-align: center;
149
+ text-align: left;
124
150
  background-color: black;
125
151
  color: white;
126
152
  font-family: Arial, Times;
127
153
  font-size: 13px;
128
154
  }
129
155
  </style>
156
+ <div class="header">
157
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAeFBMVEX///8AAADl5eVdXV1jY2Pd3d2xsbH4+Pi2trbz8/Ph4eGampr8/PwwMDDw8PDMzMyJiYmrq6vGxsalpaWPj4+Dg4PV1dW9vb1XV1dAQEAQEBA3NzegoKBra2twcHDPz89RUVF6enofHx8+Pj4XFxd9fX0kJCRJSUmiWCO7AAAEmElEQVR4nO3c6XaqMBQFYIMoooBYwXmonXz/N7zVgGYg2isHEl37+wsrnq2IIYOdDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAUTQe7fzdaBzZLqQhYcpKaWi7mCYMmGhguxx6KZOltguiNmKqke2SaO21gIztbRdF6qMi4YftoijNKwIyNrddFqFFZcKF7bLoBJUBGQtsF0ZmaEjo2S6MzJsh4dh2YWRMCd9sF0amZ0jYs10YmdCQ8IX635+VAZe2yyI0rUw4tV0Woagy4Us9CPcrAvZtF0XL1wL6tkui9q4EfLddEL2DFPBgu5wmbISAG9vFNEK827zYXaaAhM8PCZ8fEj4/JHx+SPj8Bi+SMEyGvUpDcYJtZDopcXx0an6IWV3xwdkZmzCrna6UOTmjUT2e9ij3xuECdZSirnfHPsZkRRyQsVViO5QoEQo7+nUchbfKpYiXO2haf7Kld1mXEhNURmRXlHSg+e4El/ZImiNQLiWhW+dUdn9cWZCy5uVQzncWc6prwiZrGPNqaIdAi8FVN+bBt+daPolb5RfGlrjVhwTNfGWKK8OF3/19Q3f22Jl7De+PZuTt8n68C/3Tw7kS+ieeuTM/iXzuc0jeLl9I5cI8avdcCf0CLu/cbpe83f+HhI9Cwva8fkK/0YR276VeP8vzNDYkHG/ybPKXH5HhJMs3et+FJ4zTPM/6dpbaetLibbWGcuRtObvTzGxZnKn2Xjyx+YWFjMo6LrmCYHk9crs/J4yxLuVetie/QOvzAOpCNTmhNPJ9K6I0iCz33pWEbUdUX15OeJCPmR9jx/KJUif09ks0TluIJ768ujTf/GSlznKINyYtYau31UR9dSmhuk2Nme42M/VEcUOblrDV8VN9l4+YcK0eNG1T0za0iUMhesI256T0vXZCQn1B8M7QzFY7U1g2rCdsc7mfPo8mJNT3AJm+QfqyWuEHQ09IP45gNrmVsPOlHswNzeTqieKGPT3hpMFEKu0WISXsqgdNg+Ha6m+xn60nvNc9IqV9TGJCrXDT3Ly200R8K7SEXw3m0WkTvtLPsTKZ+G1s5ls+cSUe0xK2eZH+WiovLyWUuyor876KSH4vpM6PmrDtHTbBz42E8kd8q7clxZiaDzH20/rodyTf6pUY1/14x9s9keR4OVOZu5ITbm1ssJnthB3a6gcVTU+lr/z7U25v/ulSPU7VCELCj12rt1FRGIbRwnQpBr8H/9RKFIYVlyBPuPg9aHuZ1OuPRCHho5CwPUj4KHcS8sdY+h8s/vziwlIF/jRM3y3mz6BtPvWa8PVL9ANhvFfowv9JFb0r6o5HWN0btCJu5HLiF78byxOLgTfa0cxiRNaNv8sqxtZoV9kVY64uLInqXMa4Kf8UqZi5c+VP3coh4DXVhZqUo+bO/K/EZVwmo7ijXvdtuLH28uw6yu9PZl4d+8l1dMSN20xBG70mYBopt0Sfq6nLqU/wZHy/5v/i0HewFGkTozWkztxFJcGo/t68k3jkyA99FW+Qbbt1bLOBE51tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgGf2D4WAKMqRFhM1AAAAAElFTkSuQmCC" width="35px" height="35px">
158
+ <p>< Maintenance Bot ></p>
159
+ </div>
130
160
  <div class="content">
131
161
  Hello,
132
162
  <br><br>
@@ -27,13 +27,13 @@ module VolumeSweeper
27
27
  <<~HTML
28
28
  The environment is scanned.<br>
29
29
  * Active volumes: #{active_count}<br>
30
- * Unused volumes: #{unused_list&.count || 0}<br>
30
+ * Unused volumes: #{unused_list&.count || 0}<br><br>
31
31
 
32
32
  Found the following volumes without instance bound or K8S PV relation.<br>
33
- <u>(#{notice}):</u> <br>
33
+ <u>(#{notice}).</u> <br>
34
34
  <ul style="color: #400707">
35
35
  <% unused_list.each do |vol| %>
36
- <li>volume: <a href="#{@provider_base_url}/<%= vol %>"><%= vol %></a>.</li>
36
+ <li><a href="#{@provider_base_url}<%= vol %>"><%= vol %></a>.</li>
37
37
  <% end %>
38
38
  </ul>
39
39
  HTML
@@ -1,3 +1,3 @@
1
1
  module VolumeSweeper
2
- VERSION = "1.0.1"
2
+ VERSION = "2.0.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: volume_sweeper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdullah Barrak
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-05-29 00:00:00.000000000 Z
11
+ date: 2025-01-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cowsay
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.3.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-sdk-ec2
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.498'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.498'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: oci
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +122,20 @@ dependencies:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
124
  version: '3'
125
+ - !ruby/object:Gem::Dependency
126
+ name: nokogiri
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.18'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.18'
111
139
  - !ruby/object:Gem::Dependency
112
140
  name: rspec
113
141
  requirement: !ruby/object:Gem::Requirement
@@ -206,8 +234,8 @@ dependencies:
206
234
  - - ">="
207
235
  - !ruby/object:Gem::Version
208
236
  version: '0'
209
- description: 'This is a scanning tool for cloud infrastructure cross referenced with
210
- related clusters. '
237
+ description: This is a scanning tool for unused block volumes in kubernetes clusters
238
+ (AWS, GCP, OCI).
211
239
  email:
212
240
  - abdullah@abarrak.com
213
241
  executables: []
@@ -264,5 +292,5 @@ requirements: []
264
292
  rubygems_version: 3.3.26
265
293
  signing_key:
266
294
  specification_version: 4
267
- summary: A CLI for block volumes sweeping and cleanup
295
+ summary: A CLI for kubernetes volumes cleanup in multiple cloud provides.
268
296
  test_files: []