Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds porcelain option to droplets command #294

Merged
merged 1 commit into from
Dec 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,44 @@ defaults:
pearkes-admin-001 (ip: 30.30.30.3, status: active, region: nyc2, id: 13231512)
pearkes-api-001 (ip: 30.30.30.5, status: active, region: nyc2, id: 13231513)

If you wish to use the droplet listing as part of scripting or munging output, you can use the `--porcelain`:

$ tugboat droplets --attribute=ip4
pearkes-web-001,30.30.30.1
pearkes-admin-001,30.30.30.3
pearkes-api-001,30.30.30.5

Or `--attribute` parameter:

$ tugboat droplets --porcelain
name pearkes-web-001
id 13231515
status active
ip4 330.30.30.1
region lon1
image 6918990
size 1gb
backups_active false

name pearkes-admin-001
id 13231513
status active
ip4 30.30.30.3
region lon1
image 6918990
size 1gb
backups_active false

name pearkes-web-001
id 13231514
status active
ip4 30.30.30.5
region lon1
image 6918990
size 1gb
backups_active true


### Fuzzy name matching

You can pass a unique fragment of a droplets name for interactions
Expand Down
16 changes: 15 additions & 1 deletion lib/tugboat/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,26 @@ def verify
default: 20,
aliases: '-p',
desc: 'Chose how many results to fetch from the DigitalOcean API (larger is slower)'
method_option 'attribute',
type: :string,
aliases: '-a',
desc: 'The name of the attribute to print.'
method_option 'porcelain',
type: :boolean,
desc: 'Give the output in an easy-to-parse format for scripts.'
method_option 'include_name',
type: :boolean,
default: true,
desc: 'Include the name when listing attributes from droplets.'
desc 'droplets [OPTIONS]', 'Retrieve a list of your droplets'
def droplets
Middleware.sequence_list_droplets.call('tugboat_action' => __method__,
'user_quiet' => options[:quiet],
'include_urls' => options['include_urls'],
'per_page' => options['per_page'],)
'per_page' => options['per_page'],
'attribute' => options[:attribute],
'porcelain' => options[:porcelain],
'include_name' => options[:include_name])
end

desc 'images [OPTIONS]', 'Retrieve a list of images'
Expand Down
60 changes: 60 additions & 0 deletions lib/tugboat/middleware/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,66 @@ def restart_droplet(hard_restart, ocean, droplet_id = '', droplet_name = '')
end
end

def print_droplet_info(droplet, attribute, porcelain, include_urls, include_name)
droplet_ip4_public = droplet.networks.v4.find { |address| address.type == 'public' }.ip_address unless droplet.networks.v4.empty?
droplet_ip6_public = droplet.networks.v6.find { |address| address.type == 'public' }.ip_address unless droplet.networks.v6.empty?
check_private_ip = droplet.networks.v4.find { |address| address.type == 'private' }
droplet_private_ip = check_private_ip.ip_address if check_private_ip

attributes_list = [
['name', droplet.name],
['id', droplet.id],
['status', droplet.status],
['ip4', droplet_ip4_public],
['ip6', droplet_ip6_public],
['private_ip', droplet_private_ip],
['region', droplet.region.slug],
['image', droplet.image.id],
['size', droplet.size_slug],
['backups_active', !droplet.backup_ids.empty?]
]
attributes = Hash[*attributes_list.flatten(1)]

if attribute
if attributes.key? attribute
if include_name
say "#{attributes['name']},#{attributes[attribute]}"
else
say attributes[attribute]
end
else
say "Invalid attribute \"#{attribute}\"", :red
say 'Provide one of the following:', :red
attributes_list.each { |a| say " #{a[0]}", :red }
exit 1
end
else
if porcelain
attributes_list.select { |a| !a[1].nil? }.each { |a| say "#{a[0]} #{a[1]}" }
say ""
else
print_droplet_info_full(droplet, include_urls)
end
end
end

def print_droplet_info_full(droplet, include_urls)
private_addr = droplet.networks.v4.find { |address| address.type == 'private' }
if private_addr
private_ip = ", private_ip: #{private_addr.ip_address}"
end

status_color = if droplet.status == 'active'
GREEN
else
RED
end

public_addr = droplet.networks.v4.find { |address| address.type == 'public' }

say "#{droplet.name} (ip: #{public_addr.ip_address}#{private_ip}, status: #{status_color}#{droplet.status}#{CLEAR}, region: #{droplet.region.slug}, size: #{droplet.size_slug}, id: #{droplet.id}#{include_urls ? droplet_id_to_url(droplet.id) : ''})"
end

# Get all pages of droplets
def get_droplet_list(ocean, per_page = 20)
verify_credentials(ocean)
Expand Down
15 changes: 1 addition & 14 deletions lib/tugboat/middleware/list_droplets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,7 @@ def call(env)
droplet_list.each do |droplet|
has_one = true

private_addr = droplet.networks.v4.find { |address| address.type == 'private' }
if private_addr
private_ip = ", private_ip: #{private_addr.ip_address}"
end

status_color = if droplet.status == 'active'
GREEN
else
RED
end

public_addr = droplet.networks.v4.find { |address| address.type == 'public' }

say "#{droplet.name} (ip: #{public_addr.ip_address}#{private_ip}, status: #{status_color}#{droplet.status}#{CLEAR}, region: #{droplet.region.slug}, size: #{droplet.size_slug}, id: #{droplet.id}#{env['include_urls'] ? droplet_id_to_url(droplet.id) : ''})"
print_droplet_info(droplet, env['attribute'], env['porcelain'], env['include_urls'], env['include_name'])
end

unless has_one
Expand Down
148 changes: 148 additions & 0 deletions spec/cli/droplets_cli_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,154 @@
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=2&per_page=3')).to have_been_made
end

it 'gives porcelain output when set' do
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
to_return(status: 200, body: fixture('show_droplets'), headers: {})

stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20').
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
to_return(status: 200, body: fixture('show_droplets'), headers: { 'Content-Type' => 'application/json' })

cli.options = cli.options.merge('porcelain' => true)

expected_string = <<-eos
name example.com
id 6918990
status active
ip4 104.236.32.182
ip6 2604:A880:0800:0010:0000:0000:02DD:4001
region nyc3
image 6918990
size 512mb
backups_active true

name example2.com
id 3164956
status active
ip4 104.236.32.172
ip6 2604:A880:0800:0010:0000:0000:02DD:4001
region nyc3
image 6918990
size 512mb
backups_active true

name example3.com
id 3164444
status off
ip4 104.236.32.173
ip6 2604:A880:0800:0010:0000:0000:02DD:4001
region nyc3
image 6918990
size 512mb
backups_active true

eos

expect { cli.droplets }.to output(expected_string).to_stdout

expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1')).to have_been_made.twice
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20')).to have_been_made
end

it 'gives ipv4 attribute output when set' do
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
to_return(status: 200, body: fixture('show_droplets'), headers: {})

stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20').
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
to_return(status: 200, body: fixture('show_droplets'), headers: { 'Content-Type' => 'application/json' })

cli.options = cli.options.merge('attribute' => 'ip4', 'include_name' => true)

expected_string = <<-eos
example.com,104.236.32.182
example2.com,104.236.32.172
example3.com,104.236.32.173
eos

expect { cli.droplets }.to output(expected_string).to_stdout

expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1')).to have_been_made.twice
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20')).to have_been_made
end

it 'gives status attribute output when set' do
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
to_return(status: 200, body: fixture('show_droplets'), headers: {})

stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20').
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
to_return(status: 200, body: fixture('show_droplets'), headers: { 'Content-Type' => 'application/json' })

cli.options = cli.options.merge('attribute' => 'status', 'include_name' => true)

expected_string = <<-eos
example.com,active
example2.com,active
example3.com,off
eos

expect { cli.droplets }.to output(expected_string).to_stdout

expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1')).to have_been_made.twice
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20')).to have_been_made
end

it 'gives only ip4 attribute output when set and include_name set to false' do
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
to_return(status: 200, body: fixture('show_droplets'), headers: {})

stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20').
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
to_return(status: 200, body: fixture('show_droplets'), headers: { 'Content-Type' => 'application/json' })

cli.options = cli.options.merge('attribute' => 'ip4', 'include_name' => false)

expected_string = <<-eos
104.236.32.182
104.236.32.172
104.236.32.173
eos

expect { cli.droplets }.to output(expected_string).to_stdout

expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1')).to have_been_made.twice
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20')).to have_been_made
end

it 'shows error if attribute asked for does not exist' do
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
to_return(status: 200, body: fixture('show_droplets'), headers: {})

stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20').
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
to_return(status: 200, body: fixture('show_droplets'), headers: { 'Content-Type' => 'application/json' })

cli.options = cli.options.merge('attribute' => 'foo')

expected_string = <<-eos
Invalid attribute \"foo\"
Provide one of the following:
name
id
status
ip4
ip6
private_ip
region
image
size
backups_active
eos

expect { cli.droplets }.to raise_error(SystemExit).and output(expected_string).to_stdout
end

it 'shows error on failure in initial stage' do
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
Expand Down