azuki 0.0.1
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 +7 -0
- data/README.md +71 -0
- data/bin/azuki +17 -0
- data/data/cacert.pem +3988 -0
- data/lib/azuki.rb +17 -0
- data/lib/azuki/auth.rb +339 -0
- data/lib/azuki/cli.rb +38 -0
- data/lib/azuki/client.rb +764 -0
- data/lib/azuki/client/azuki_postgresql.rb +141 -0
- data/lib/azuki/client/cisaurus.rb +26 -0
- data/lib/azuki/client/pgbackups.rb +113 -0
- data/lib/azuki/client/rendezvous.rb +108 -0
- data/lib/azuki/client/ssl_endpoint.rb +25 -0
- data/lib/azuki/command.rb +294 -0
- data/lib/azuki/command/account.rb +23 -0
- data/lib/azuki/command/accounts.rb +34 -0
- data/lib/azuki/command/addons.rb +305 -0
- data/lib/azuki/command/apps.rb +393 -0
- data/lib/azuki/command/auth.rb +86 -0
- data/lib/azuki/command/base.rb +230 -0
- data/lib/azuki/command/certs.rb +209 -0
- data/lib/azuki/command/config.rb +137 -0
- data/lib/azuki/command/db.rb +218 -0
- data/lib/azuki/command/domains.rb +85 -0
- data/lib/azuki/command/drains.rb +46 -0
- data/lib/azuki/command/fork.rb +164 -0
- data/lib/azuki/command/git.rb +64 -0
- data/lib/azuki/command/help.rb +179 -0
- data/lib/azuki/command/keys.rb +115 -0
- data/lib/azuki/command/labs.rb +147 -0
- data/lib/azuki/command/logs.rb +45 -0
- data/lib/azuki/command/maintenance.rb +61 -0
- data/lib/azuki/command/pg.rb +269 -0
- data/lib/azuki/command/pgbackups.rb +329 -0
- data/lib/azuki/command/plugins.rb +110 -0
- data/lib/azuki/command/ps.rb +232 -0
- data/lib/azuki/command/regions.rb +22 -0
- data/lib/azuki/command/releases.rb +124 -0
- data/lib/azuki/command/run.rb +180 -0
- data/lib/azuki/command/sharing.rb +89 -0
- data/lib/azuki/command/ssl.rb +43 -0
- data/lib/azuki/command/stack.rb +62 -0
- data/lib/azuki/command/status.rb +51 -0
- data/lib/azuki/command/update.rb +47 -0
- data/lib/azuki/command/version.rb +23 -0
- data/lib/azuki/deprecated.rb +5 -0
- data/lib/azuki/deprecated/help.rb +38 -0
- data/lib/azuki/distribution.rb +9 -0
- data/lib/azuki/excon.rb +9 -0
- data/lib/azuki/helpers.rb +517 -0
- data/lib/azuki/helpers/azuki_postgresql.rb +165 -0
- data/lib/azuki/helpers/log_displayer.rb +70 -0
- data/lib/azuki/plugin.rb +163 -0
- data/lib/azuki/updater.rb +171 -0
- data/lib/azuki/version.rb +3 -0
- data/lib/vendor/azuki/okjson.rb +598 -0
- data/spec/azuki/auth_spec.rb +256 -0
- data/spec/azuki/client/azuki_postgresql_spec.rb +71 -0
- data/spec/azuki/client/pgbackups_spec.rb +43 -0
- data/spec/azuki/client/rendezvous_spec.rb +62 -0
- data/spec/azuki/client/ssl_endpoint_spec.rb +48 -0
- data/spec/azuki/client_spec.rb +564 -0
- data/spec/azuki/command/addons_spec.rb +601 -0
- data/spec/azuki/command/apps_spec.rb +351 -0
- data/spec/azuki/command/auth_spec.rb +38 -0
- data/spec/azuki/command/base_spec.rb +109 -0
- data/spec/azuki/command/certs_spec.rb +178 -0
- data/spec/azuki/command/config_spec.rb +144 -0
- data/spec/azuki/command/db_spec.rb +110 -0
- data/spec/azuki/command/domains_spec.rb +87 -0
- data/spec/azuki/command/drains_spec.rb +34 -0
- data/spec/azuki/command/fork_spec.rb +56 -0
- data/spec/azuki/command/git_spec.rb +144 -0
- data/spec/azuki/command/help_spec.rb +93 -0
- data/spec/azuki/command/keys_spec.rb +120 -0
- data/spec/azuki/command/labs_spec.rb +100 -0
- data/spec/azuki/command/logs_spec.rb +60 -0
- data/spec/azuki/command/maintenance_spec.rb +51 -0
- data/spec/azuki/command/pg_spec.rb +236 -0
- data/spec/azuki/command/pgbackups_spec.rb +307 -0
- data/spec/azuki/command/plugins_spec.rb +104 -0
- data/spec/azuki/command/ps_spec.rb +195 -0
- data/spec/azuki/command/releases_spec.rb +130 -0
- data/spec/azuki/command/run_spec.rb +83 -0
- data/spec/azuki/command/sharing_spec.rb +59 -0
- data/spec/azuki/command/stack_spec.rb +46 -0
- data/spec/azuki/command/status_spec.rb +48 -0
- data/spec/azuki/command/version_spec.rb +16 -0
- data/spec/azuki/command_spec.rb +211 -0
- data/spec/azuki/helpers/azuki_postgresql_spec.rb +155 -0
- data/spec/azuki/helpers_spec.rb +48 -0
- data/spec/azuki/plugin_spec.rb +172 -0
- data/spec/azuki/updater_spec.rb +44 -0
- data/spec/helper/legacy_help.rb +16 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +224 -0
- data/spec/support/display_message_matcher.rb +49 -0
- data/spec/support/openssl_mock_helper.rb +8 -0
- metadata +211 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
require "azuki/command/base"
|
|
2
|
+
|
|
3
|
+
# manage optional features
|
|
4
|
+
#
|
|
5
|
+
class Azuki::Command::Labs < Azuki::Command::Base
|
|
6
|
+
|
|
7
|
+
# labs
|
|
8
|
+
#
|
|
9
|
+
# list experimental features
|
|
10
|
+
#
|
|
11
|
+
#Example:
|
|
12
|
+
#
|
|
13
|
+
# === User Features (david@azukiapp.com)
|
|
14
|
+
# [+] dashboard Use Azuki Dashboard by default
|
|
15
|
+
#
|
|
16
|
+
# === App Features (glacial-retreat-5913)
|
|
17
|
+
# [ ] preboot Provide seamless web dyno deploys
|
|
18
|
+
# [ ] user-env-compile Add user config vars to the environment during slug compilation # $ azuki labs -a example
|
|
19
|
+
#
|
|
20
|
+
def index
|
|
21
|
+
validate_arguments!
|
|
22
|
+
|
|
23
|
+
user_features, app_features = api.get_features(app).body.sort_by do |feature|
|
|
24
|
+
feature["name"]
|
|
25
|
+
end.partition do |feature|
|
|
26
|
+
feature["kind"] == "user"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
display_app = app || "no app specified"
|
|
30
|
+
|
|
31
|
+
styled_header "User Features (#{Azuki::Auth.user})"
|
|
32
|
+
display_features user_features
|
|
33
|
+
display
|
|
34
|
+
styled_header "App Features (#{display_app})"
|
|
35
|
+
display_features app_features
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
alias_command "labs:list", "labs"
|
|
39
|
+
|
|
40
|
+
# labs:info FEATURE
|
|
41
|
+
#
|
|
42
|
+
# displays additional information about FEATURE
|
|
43
|
+
#
|
|
44
|
+
#Example:
|
|
45
|
+
#
|
|
46
|
+
# $ azuki labs:info user_env_compile
|
|
47
|
+
# === user_env_compile
|
|
48
|
+
# Docs: http://devcenter.azukiapp.com/articles/labs-user-env-compile
|
|
49
|
+
# Summary: Add user config vars to the environment during slug compilation
|
|
50
|
+
#
|
|
51
|
+
def info
|
|
52
|
+
unless feature_name = shift_argument
|
|
53
|
+
error("Usage: azuki labs:info FEATURE\nMust specify FEATURE for info.")
|
|
54
|
+
end
|
|
55
|
+
validate_arguments!
|
|
56
|
+
|
|
57
|
+
feature_data = api.get_feature(feature_name, app).body
|
|
58
|
+
styled_header(feature_data['name'])
|
|
59
|
+
styled_hash({
|
|
60
|
+
'Summary' => feature_data['summary'],
|
|
61
|
+
'Docs' => feature_data['docs']
|
|
62
|
+
})
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# labs:disable FEATURE
|
|
66
|
+
#
|
|
67
|
+
# disables an experimental feature
|
|
68
|
+
#
|
|
69
|
+
#Example:
|
|
70
|
+
#
|
|
71
|
+
# $ azuki labs:disable ninja-power
|
|
72
|
+
# Disabling ninja-power feature for me@example.org... done
|
|
73
|
+
#
|
|
74
|
+
def disable
|
|
75
|
+
feature_name = shift_argument
|
|
76
|
+
error "Usage: azuki labs:disable FEATURE\nMust specify FEATURE to disable." unless feature_name
|
|
77
|
+
validate_arguments!
|
|
78
|
+
|
|
79
|
+
feature = api.get_features(app).body.detect { |f| f["name"] == feature_name }
|
|
80
|
+
message = "Disabling #{feature_name} "
|
|
81
|
+
|
|
82
|
+
error "No such feature: #{feature_name}" unless feature
|
|
83
|
+
|
|
84
|
+
if feature["kind"] == "user"
|
|
85
|
+
message += "for #{Azuki::Auth.user}"
|
|
86
|
+
else
|
|
87
|
+
error "Must specify an app" unless app
|
|
88
|
+
message += "for #{app}"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
action message do
|
|
92
|
+
api.delete_feature feature_name, app
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# labs:enable FEATURE
|
|
97
|
+
#
|
|
98
|
+
# enables an experimental feature
|
|
99
|
+
#
|
|
100
|
+
#Example:
|
|
101
|
+
#
|
|
102
|
+
# $ azuki labs:enable ninja-power
|
|
103
|
+
# Enabling ninja-power feature for me@example.org... done
|
|
104
|
+
#
|
|
105
|
+
def enable
|
|
106
|
+
feature_name = shift_argument
|
|
107
|
+
error "Usage: azuki labs:enable FEATURE\nMust specify FEATURE to enable." unless feature_name
|
|
108
|
+
validate_arguments!
|
|
109
|
+
|
|
110
|
+
feature = api.get_features.body.detect { |f| f["name"] == feature_name }
|
|
111
|
+
message = "Enabling #{feature_name} "
|
|
112
|
+
|
|
113
|
+
error "No such feature: #{feature_name}" unless feature
|
|
114
|
+
|
|
115
|
+
if feature["kind"] == "user"
|
|
116
|
+
message += "for #{Azuki::Auth.user}"
|
|
117
|
+
else
|
|
118
|
+
error "Must specify an app" unless app
|
|
119
|
+
message += "for #{app}"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
feature_data = action(message) do
|
|
123
|
+
api.post_feature(feature_name, app).body
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
display "WARNING: This feature is experimental and may change or be removed without notice."
|
|
127
|
+
display "For more information see: #{feature_data["docs"]}" if feature_data["docs"]
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
# app is not required for these commands, so rescue if there is none
|
|
133
|
+
def app
|
|
134
|
+
super
|
|
135
|
+
rescue Azuki::Command::CommandFailed
|
|
136
|
+
nil
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def display_features(features)
|
|
140
|
+
longest_name = features.map { |f| f["name"].to_s.length }.sort.last
|
|
141
|
+
features.each do |feature|
|
|
142
|
+
toggle = feature["enabled"] ? "[+]" : "[ ]"
|
|
143
|
+
display "%s %-#{longest_name}s %s" % [ toggle, feature["name"], feature["summary"] ]
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require "azuki/command/base"
|
|
2
|
+
require "azuki/helpers/log_displayer"
|
|
3
|
+
|
|
4
|
+
# display logs for an app
|
|
5
|
+
#
|
|
6
|
+
class Azuki::Command::Logs < Azuki::Command::Base
|
|
7
|
+
|
|
8
|
+
# logs
|
|
9
|
+
#
|
|
10
|
+
# display recent log output
|
|
11
|
+
#
|
|
12
|
+
# -n, --num NUM # the number of lines to display
|
|
13
|
+
# -p, --ps PS # only display logs from the given process
|
|
14
|
+
# -s, --source SOURCE # only display logs from the given source
|
|
15
|
+
# -t, --tail # continually stream logs
|
|
16
|
+
#
|
|
17
|
+
#Example:
|
|
18
|
+
#
|
|
19
|
+
# $ azuki logs
|
|
20
|
+
# 2012-01-01T12:00:00+00:00 azuki[api]: Config add EXAMPLE by email@example.com
|
|
21
|
+
# 2012-01-01T12:00:01+00:00 azuki[api]: Release v1 created by email@example.com
|
|
22
|
+
#
|
|
23
|
+
def index
|
|
24
|
+
validate_arguments!
|
|
25
|
+
|
|
26
|
+
opts = []
|
|
27
|
+
opts << "tail=1" if options[:tail]
|
|
28
|
+
opts << "num=#{options[:num]}" if options[:num]
|
|
29
|
+
opts << "ps=#{URI.encode(options[:ps])}" if options[:ps]
|
|
30
|
+
opts << "source=#{URI.encode(options[:source])}" if options[:source]
|
|
31
|
+
|
|
32
|
+
log_displayer = ::Azuki::Helpers::LogDisplayer.new(azuki, app, opts)
|
|
33
|
+
log_displayer.display_logs
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# logs:drains
|
|
37
|
+
#
|
|
38
|
+
# DEPRECATED: use `azuki drains`
|
|
39
|
+
#
|
|
40
|
+
def drains
|
|
41
|
+
# deprecation notice added 09/30/2011
|
|
42
|
+
display("~ `azuki logs:drains` has been deprecated and replaced with `azuki drains`")
|
|
43
|
+
Azuki::Command::Drains.new.index
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require "azuki/command/base"
|
|
2
|
+
|
|
3
|
+
# manage maintenance mode for an app
|
|
4
|
+
#
|
|
5
|
+
class Azuki::Command::Maintenance < Azuki::Command::Base
|
|
6
|
+
|
|
7
|
+
# maintenance
|
|
8
|
+
#
|
|
9
|
+
# display the current maintenance status of app
|
|
10
|
+
#
|
|
11
|
+
#Example:
|
|
12
|
+
#
|
|
13
|
+
# $ azuki maintenance
|
|
14
|
+
# off
|
|
15
|
+
#
|
|
16
|
+
def index
|
|
17
|
+
validate_arguments!
|
|
18
|
+
|
|
19
|
+
case api.get_app_maintenance(app).body['maintenance']
|
|
20
|
+
when true
|
|
21
|
+
display('on')
|
|
22
|
+
when false
|
|
23
|
+
display('off')
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# maintenance:on
|
|
28
|
+
#
|
|
29
|
+
# put the app into maintenance mode
|
|
30
|
+
#
|
|
31
|
+
#Example:
|
|
32
|
+
#
|
|
33
|
+
# $ azuki maintenance:on
|
|
34
|
+
# Enabling maintenance mode for example
|
|
35
|
+
#
|
|
36
|
+
def on
|
|
37
|
+
validate_arguments!
|
|
38
|
+
|
|
39
|
+
action("Enabling maintenance mode for #{app}") do
|
|
40
|
+
api.post_app_maintenance(app, '1')
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# maintenance:off
|
|
45
|
+
#
|
|
46
|
+
# take the app out of maintenance mode
|
|
47
|
+
#
|
|
48
|
+
#Example:
|
|
49
|
+
#
|
|
50
|
+
# $ azuki maintenance:off
|
|
51
|
+
# Disabling maintenance mode for example
|
|
52
|
+
#
|
|
53
|
+
def off
|
|
54
|
+
validate_arguments!
|
|
55
|
+
|
|
56
|
+
action("Disabling maintenance mode for #{app}") do
|
|
57
|
+
api.post_app_maintenance(app, '0')
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
require "azuki/client/azuki_postgresql"
|
|
2
|
+
require "azuki/command/base"
|
|
3
|
+
require "azuki/helpers/azuki_postgresql"
|
|
4
|
+
|
|
5
|
+
# manage azuki-postgresql databases
|
|
6
|
+
#
|
|
7
|
+
class Azuki::Command::Pg < Azuki::Command::Base
|
|
8
|
+
|
|
9
|
+
include Azuki::Helpers::AzukiPostgresql
|
|
10
|
+
|
|
11
|
+
# pg
|
|
12
|
+
#
|
|
13
|
+
# List databases for an app
|
|
14
|
+
#
|
|
15
|
+
def index
|
|
16
|
+
validate_arguments!
|
|
17
|
+
|
|
18
|
+
if hpg_databases_with_info.empty?
|
|
19
|
+
display("#{app} has no azuki-postgresql databases.")
|
|
20
|
+
else
|
|
21
|
+
hpg_databases_with_info.keys.sort.each do |name|
|
|
22
|
+
display_db name, hpg_databases_with_info[name]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# pg:info [DATABASE]
|
|
28
|
+
#
|
|
29
|
+
# -x, --extended # Show extended information
|
|
30
|
+
#
|
|
31
|
+
# Display database information
|
|
32
|
+
#
|
|
33
|
+
# If DATABASE is not specified, displays all databases
|
|
34
|
+
#
|
|
35
|
+
def info
|
|
36
|
+
db = shift_argument
|
|
37
|
+
validate_arguments!
|
|
38
|
+
|
|
39
|
+
if db
|
|
40
|
+
attachment = hpg_resolve(db)
|
|
41
|
+
display_db attachment.display_name, hpg_info(attachment, options[:extended])
|
|
42
|
+
else
|
|
43
|
+
index
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# pg:promote DATABASE
|
|
48
|
+
#
|
|
49
|
+
# Sets DATABASE as your DATABASE_URL
|
|
50
|
+
#
|
|
51
|
+
def promote
|
|
52
|
+
unless db = shift_argument
|
|
53
|
+
error("Usage: azuki pg:promote DATABASE\nMust specify DATABASE to promote.")
|
|
54
|
+
end
|
|
55
|
+
validate_arguments!
|
|
56
|
+
|
|
57
|
+
attachment = hpg_resolve(db)
|
|
58
|
+
|
|
59
|
+
action "Promoting #{attachment.display_name} to DATABASE_URL" do
|
|
60
|
+
hpg_promote(attachment.url)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# pg:psql [DATABASE]
|
|
65
|
+
#
|
|
66
|
+
# Open a psql shell to the database
|
|
67
|
+
#
|
|
68
|
+
# defaults to DATABASE_URL databases if no DATABASE is specified
|
|
69
|
+
#
|
|
70
|
+
def psql
|
|
71
|
+
attachment = hpg_resolve(shift_argument, "DATABASE_URL")
|
|
72
|
+
validate_arguments!
|
|
73
|
+
|
|
74
|
+
uri = URI.parse( attachment.url )
|
|
75
|
+
begin
|
|
76
|
+
ENV["PGPASSWORD"] = uri.password
|
|
77
|
+
ENV["PGSSLMODE"] = 'require'
|
|
78
|
+
exec "psql -U #{uri.user} -h #{uri.host} -p #{uri.port || 5432} #{uri.path[1..-1]}"
|
|
79
|
+
rescue Errno::ENOENT
|
|
80
|
+
output_with_bang "The local psql command could not be located"
|
|
81
|
+
output_with_bang "For help installing psql, see http://devcenter.azukiapp.com/articles/local-postgresql"
|
|
82
|
+
abort
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# pg:reset DATABASE
|
|
87
|
+
#
|
|
88
|
+
# Delete all data in DATABASE
|
|
89
|
+
#
|
|
90
|
+
def reset
|
|
91
|
+
unless db = shift_argument
|
|
92
|
+
error("Usage: azuki pg:reset DATABASE\nMust specify DATABASE to reset.")
|
|
93
|
+
end
|
|
94
|
+
validate_arguments!
|
|
95
|
+
|
|
96
|
+
attachment = hpg_resolve(db) unless db == "SHARED_DATABASE"
|
|
97
|
+
return unless confirm_command
|
|
98
|
+
|
|
99
|
+
if db == "SHARED_DATABASE"
|
|
100
|
+
action("Resetting SHARED_DATABASE") { azuki.database_reset(app) }
|
|
101
|
+
else
|
|
102
|
+
action("Resetting #{attachment.display_name}") do
|
|
103
|
+
hpg_client(attachment).reset
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# pg:unfollow REPLICA
|
|
109
|
+
#
|
|
110
|
+
# stop a replica from following and make it a read/write database
|
|
111
|
+
#
|
|
112
|
+
def unfollow
|
|
113
|
+
unless db = shift_argument
|
|
114
|
+
error("Usage: azuki pg:unfollow REPLICA\nMust specify REPLICA to unfollow.")
|
|
115
|
+
end
|
|
116
|
+
validate_arguments!
|
|
117
|
+
|
|
118
|
+
replica = hpg_resolve(db)
|
|
119
|
+
replica_info = hpg_info(replica)
|
|
120
|
+
|
|
121
|
+
unless replica_info[:following]
|
|
122
|
+
error("#{replica.display_name} is not following another database.")
|
|
123
|
+
end
|
|
124
|
+
origin_url = replica_info[:following]
|
|
125
|
+
origin_name = database_name_from_url(origin_url)
|
|
126
|
+
|
|
127
|
+
output_with_bang "#{replica.display_name} will become writable and no longer"
|
|
128
|
+
output_with_bang "follow #{origin_name}. This cannot be undone."
|
|
129
|
+
return unless confirm_command
|
|
130
|
+
|
|
131
|
+
action "Unfollowing #{replica.display_name}" do
|
|
132
|
+
hpg_client(replica).unfollow
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# pg:wait [DATABASE]
|
|
137
|
+
#
|
|
138
|
+
# monitor database creation, exit when complete
|
|
139
|
+
#
|
|
140
|
+
# defaults to all databases if no DATABASE is specified
|
|
141
|
+
#
|
|
142
|
+
def wait
|
|
143
|
+
db = shift_argument
|
|
144
|
+
validate_arguments!
|
|
145
|
+
|
|
146
|
+
if db
|
|
147
|
+
wait_for hpg_resolve(db)
|
|
148
|
+
else
|
|
149
|
+
hpg_databases.values.each do |attach|
|
|
150
|
+
wait_for(attach)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# pg:credentials DATABASE
|
|
156
|
+
#
|
|
157
|
+
# Display the DATABASE credentials.
|
|
158
|
+
#
|
|
159
|
+
# --reset # Reset credentials on the specified database.
|
|
160
|
+
#
|
|
161
|
+
def credentials
|
|
162
|
+
unless db = shift_argument
|
|
163
|
+
error("Usage: azuki pg:credentials DATABASE\nMust specify DATABASE to display credentials.")
|
|
164
|
+
end
|
|
165
|
+
validate_arguments!
|
|
166
|
+
|
|
167
|
+
attachment = hpg_resolve(db)
|
|
168
|
+
|
|
169
|
+
if options[:reset]
|
|
170
|
+
action "Resetting credentials for #{attachment.display_name}" do
|
|
171
|
+
hpg_client(attachment).rotate_credentials
|
|
172
|
+
end
|
|
173
|
+
if attachment.primary_attachment?
|
|
174
|
+
forget_config!
|
|
175
|
+
attachment = hpg_resolve(db)
|
|
176
|
+
action "Promoting #{attachment.display_name}" do
|
|
177
|
+
hpg_promote(attachment.url)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
else
|
|
181
|
+
uri = URI.parse( attachment.url )
|
|
182
|
+
display "Connection info string:"
|
|
183
|
+
display " \"dbname=#{uri.path[1..-1]} host=#{uri.host} port=#{uri.port || 5432} user=#{uri.user} password=#{uri.password} sslmode=require\""
|
|
184
|
+
display "Connection URL:"
|
|
185
|
+
display " " + attachment.url
|
|
186
|
+
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
private
|
|
191
|
+
|
|
192
|
+
def database_name_from_url(url)
|
|
193
|
+
vars = app_config_vars.reject {|key,value| key == 'DATABASE_URL'}
|
|
194
|
+
if var = vars.invert[url]
|
|
195
|
+
var.gsub(/_URL$/, '')
|
|
196
|
+
else
|
|
197
|
+
uri = URI.parse(url)
|
|
198
|
+
"Database on #{uri.host}:#{uri.port || 5432}#{uri.path}"
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def display_db(name, db)
|
|
203
|
+
styled_header(name)
|
|
204
|
+
styled_hash(db[:info].inject({}) do |hash, item|
|
|
205
|
+
hash.update(item["name"] => hpg_info_display(item))
|
|
206
|
+
end, db[:info].map {|item| item['name']})
|
|
207
|
+
|
|
208
|
+
display
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def hpg_client(attachment)
|
|
212
|
+
Azuki::Client::AzukiPostgresql.new(attachment)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def hpg_databases_with_info
|
|
216
|
+
return @hpg_databases_with_info if @hpg_databases_with_info
|
|
217
|
+
|
|
218
|
+
@hpg_databases_with_info = Hash[ hpg_databases.map { |config, att| [att.display_name, hpg_info(att, options[:extended])] } ]
|
|
219
|
+
|
|
220
|
+
return @hpg_databases_with_info
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def hpg_info(attachment, extended=false)
|
|
224
|
+
if attachment.resource_name == "SHARED_DATABASE"
|
|
225
|
+
data = api.get_app(app).body
|
|
226
|
+
{:info => [{
|
|
227
|
+
'name' => 'Data Size',
|
|
228
|
+
'values' => [format_bytes(data['database_size'])]
|
|
229
|
+
}]}
|
|
230
|
+
else
|
|
231
|
+
hpg_client(attachment).get_database(extended)
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def hpg_info_display(item)
|
|
236
|
+
item["values"] = [item["value"]] if item["value"]
|
|
237
|
+
item["values"].map do |value|
|
|
238
|
+
if item["resolve_db_name"]
|
|
239
|
+
database_name_from_url(value)
|
|
240
|
+
else
|
|
241
|
+
value
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def ticking
|
|
247
|
+
ticks = 0
|
|
248
|
+
loop do
|
|
249
|
+
yield(ticks)
|
|
250
|
+
ticks +=1
|
|
251
|
+
sleep 1
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def wait_for(attach)
|
|
256
|
+
ticking do |ticks|
|
|
257
|
+
status = hpg_client(attach).get_wait_status
|
|
258
|
+
error status[:message] if status[:error?]
|
|
259
|
+
break if !status[:waiting?] && ticks.zero?
|
|
260
|
+
redisplay("Waiting for database %s... %s%s" % [
|
|
261
|
+
attach.display_name,
|
|
262
|
+
status[:waiting?] ? "#{spinner(ticks)} " : "",
|
|
263
|
+
status[:message]],
|
|
264
|
+
!status[:waiting?]) # only display a newline on the last tick
|
|
265
|
+
break unless status[:waiting?]
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
end
|