|
# A quick and dirty image deletion |
|
API_KEY = '' |
|
|
|
require 'uri' |
|
require 'net/http' |
|
require 'openssl' |
|
require 'json' |
|
|
|
def json_get(url) |
|
http = Net::HTTP.new(url.host, url.port) |
|
http.use_ssl = true |
|
http.verify_mode = OpenSSL::SSL::VERIFY_PEER |
|
|
|
request = Net::HTTP::Get.new(url) |
|
request['Authorization'] = "Bearer #{API_KEY}" |
|
|
|
response = http.request(request) |
|
JSON.parse(response.read_body) |
|
end |
|
|
|
def delete(url) |
|
http = Net::HTTP.new(url.host, url.port) |
|
http.use_ssl = true |
|
http.verify_mode = OpenSSL::SSL::VERIFY_PEER |
|
|
|
request = Net::HTTP::Delete.new(url) |
|
request['Authorization'] = "Bearer #{API_KEY}" |
|
|
|
response = http.request(request) |
|
raise "Error deleting #{url}" if response.code != '204' |
|
|
|
response |
|
end |
|
|
|
Pagination = Struct.new(:page, :next_page) do |
|
def more? |
|
next_page != nil |
|
end |
|
end |
|
|
|
def parse_pagination(request_json) |
|
pagination = request_json['meta']['pagination'] |
|
|
|
Pagination.new.tap do |pagy| |
|
pagy.page = pagination['page'] |
|
pagy.next_page = pagination['next_page'] |
|
end |
|
end |
|
|
|
Image = Struct.new(:id, :name, :description, :deleted, :type) do |
|
def snapshot? |
|
type == 'snapshot' |
|
end |
|
end |
|
|
|
def parse_server_images(request_json) |
|
[].tap do |images| |
|
request_json['servers'].each do |server| |
|
images << Image.new.tap do |image| |
|
image.id = server['image']['id'] |
|
image.name = server['image']['labels']['imagename'] |
|
image.description = server['image']['description'] |
|
image.deleted = server['image']['deleted'] |
|
image.type = server['image']['type'] |
|
end |
|
end |
|
end |
|
end |
|
|
|
def parse_images(request_json) |
|
[].tap do |images| |
|
request_json['images'].each do |image_json| |
|
images << Image.new.tap do |image| |
|
image.id = image_json['id'] |
|
image.name = image_json['labels']['imagename'] |
|
image.description = image_json['description'] |
|
image.deleted = image_json['deleted'] |
|
image.type = image_json['type'] |
|
end |
|
end |
|
end |
|
end |
|
|
|
images_in_use = [].tap do |images| |
|
page_id = 1 |
|
loop do |
|
json = json_get(URI("https://api.hetzner.cloud/v1/servers?per_page=3&page=#{page_id}&status=running")) |
|
|
|
images.concat(parse_server_images(json)) |
|
|
|
pagy = parse_pagination(json) |
|
break unless pagy.more? |
|
|
|
page_id += 1 |
|
end |
|
end.uniq(&:id).select(&:snapshot?) |
|
|
|
images_available = [].tap do |images| |
|
page_id = 1 |
|
loop do |
|
json = json_get(URI("https://api.hetzner.cloud/v1/images?per_page=3&page=#{page_id}&type=snapshot")) |
|
|
|
images.concat(parse_images(json)) |
|
|
|
pagy = parse_pagination(json) |
|
break unless pagy.more? |
|
|
|
page_id += 1 |
|
end |
|
end.select(&:snapshot?) |
|
|
|
images_to_delete = [].tap do |images| |
|
images_available.each do |available_image| |
|
next if images_in_use.any? { |i| i.id == available_image.id } |
|
|
|
images << available_image |
|
end |
|
end |
|
|
|
puts "#{images_in_use.size} image(s) in use:" |
|
images_in_use.each do |image| |
|
puts "- #{image.id}: #{image.name}" |
|
end |
|
puts '' |
|
|
|
if images_to_delete.any? |
|
puts "#{images_to_delete.size} image(s) to delete:" |
|
images_to_delete.each do |image| |
|
puts "- #{image.id}: #{image.name}" |
|
end |
|
puts '' |
|
print ".. delete #{images_to_delete.size} images? (Y/n)" |
|
|
|
answer = gets.chomp |
|
if answer == 'Y' |
|
images_to_delete.each do |image| |
|
print "Deleting #{image.name}.." |
|
delete(URI("https://api.hetzner.cloud/v1/images/#{image.id}")) |
|
print "!\n" |
|
end |
|
else |
|
puts 'Bye!' |
|
end |
|
else |
|
puts 'No unused images' |
|
puts '.. bye!' |
|
end |