peas-cli 0.6.0 → 0.7.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 +4 -4
- data/VERSION +1 -1
- data/lib/peas/api.rb +52 -8
- data/lib/peas/commands/admin.rb +4 -5
- data/lib/peas/commands/app.rb +5 -3
- data/lib/peas/commands/config.rb +2 -2
- data/lib/peas/config.rb +7 -1
- data/peas-cli.gemspec +3 -3
- data/spec/cli_spec.rb +33 -10
- data/spec/spec_helper.rb +9 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 148aa934a08e354cd9f2fec7d0ac59a9cf4501f2
|
4
|
+
data.tar.gz: fa6cd8baf861cf1e21df8532c58f8659626b0628
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe827c22e2027ab7ca62bffc09f0f803847c2051b38758230999811b76604c9e9ab7f77fd6324225935f5917b18d395e2f465f9a44ea35f4873218c35454034e
|
7
|
+
data.tar.gz: 9089daead2d77e5e4d8c9b29571d2d16775231f4dc85055e3cf62baebf21abb7bdc432b0c1a90798a09cacdc67b4ec291353a04a0ea95076664a58f0578b2e12
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
data/lib/peas/api.rb
CHANGED
@@ -4,7 +4,7 @@ require 'openssl'
|
|
4
4
|
|
5
5
|
class API
|
6
6
|
include HTTParty
|
7
|
-
# TODO: Don't want to genuine SSL cert errors, say if there's a CA root cert
|
7
|
+
# TODO: Don't want to ignore genuine SSL cert errors, say if there's a CA root cert
|
8
8
|
default_options.update(verify: false) # Ignore self-signed SSL error
|
9
9
|
|
10
10
|
LONG_POLL_TIMEOUT = 10 * 60
|
@@ -16,8 +16,20 @@ class API
|
|
16
16
|
end
|
17
17
|
|
18
18
|
# Generic wrapper to the Peas API
|
19
|
-
|
20
|
-
|
19
|
+
# `verb` HTTP verb
|
20
|
+
# `method` API method, eg; /app/create
|
21
|
+
# `query` Query params
|
22
|
+
# `auth` Whether to authenticate against the API. Eg; /auth/request doesn't need auth
|
23
|
+
# `print` Whether to output or return the response
|
24
|
+
def request(verb, method, query = {}, auth = true, print = true)
|
25
|
+
options = { query: query }
|
26
|
+
options[:headers] = { 'x-api-key' => api_key } if auth
|
27
|
+
request = [
|
28
|
+
verb.to_s.downcase,
|
29
|
+
"#{method}",
|
30
|
+
options
|
31
|
+
]
|
32
|
+
response = self.class.send(request.shift, *request).body
|
21
33
|
json = response ? JSON.parse(response) : {}
|
22
34
|
# If there was an HTTP-level error
|
23
35
|
raise json['error'].color(:red) if json.key? 'error'
|
@@ -27,15 +39,42 @@ class API
|
|
27
39
|
API.stream_job json['job']
|
28
40
|
else
|
29
41
|
check_versions(json)
|
30
|
-
if
|
31
|
-
yield json['message']
|
32
|
-
else
|
33
|
-
puts json['message']
|
34
|
-
end
|
42
|
+
puts json['message'] if print
|
35
43
|
json
|
36
44
|
end
|
37
45
|
end
|
38
46
|
|
47
|
+
# Get the API key from local cache, or request a new one
|
48
|
+
def api_key
|
49
|
+
# First check local storage
|
50
|
+
key = Peas.config['api_key']
|
51
|
+
return key if key
|
52
|
+
# Other wise request a new one
|
53
|
+
key_path = "#{ENV['HOME']}/.ssh/id_rsa"
|
54
|
+
unless File.exist? key_path
|
55
|
+
exit_now! 'Please add an SSH key'
|
56
|
+
end
|
57
|
+
username = ENV['USER'] # TODO: Ensure cross platform
|
58
|
+
params = {
|
59
|
+
username: username,
|
60
|
+
public_key: File.read("#{key_path}.pub")
|
61
|
+
}
|
62
|
+
response = request('POST', '/auth/request', params, auth: false, print: false)
|
63
|
+
doc = response['message']['sign']
|
64
|
+
digest = OpenSSL::Digest::SHA256.new
|
65
|
+
keypair = OpenSSL::PKey::RSA.new(File.read(key_path))
|
66
|
+
signature = keypair.sign(digest, doc)
|
67
|
+
encoded = Base64.urlsafe_encode64(signature)
|
68
|
+
params = {
|
69
|
+
username: username,
|
70
|
+
signed: encoded
|
71
|
+
}
|
72
|
+
response = request('POST', '/auth/verify', params, auth: false, print: false)
|
73
|
+
key = response['message']['api_key']
|
74
|
+
Peas.update_config api_key: key
|
75
|
+
key
|
76
|
+
end
|
77
|
+
|
39
78
|
# Check CLI client is up to date.
|
40
79
|
def check_versions(json)
|
41
80
|
# Only check major and minor versions
|
@@ -58,6 +97,11 @@ class API
|
|
58
97
|
ssl = OpenSSL::SSL::SSLSocket.new socket
|
59
98
|
ssl.sync_close = true
|
60
99
|
ssl.connect
|
100
|
+
ssl.puts API.new.api_key
|
101
|
+
unless ssl.gets.strip == 'AUTHORISED'
|
102
|
+
ssl.close
|
103
|
+
raise 'Unauthoirsed access to Switchboard connection.'
|
104
|
+
end
|
61
105
|
ssl
|
62
106
|
end
|
63
107
|
|
data/lib/peas/commands/admin.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
def format_settings(hash)
|
2
2
|
puts "Available settings"
|
3
3
|
puts ''
|
4
|
-
hash.each do |type, settings|
|
4
|
+
hash['message'].each do |type, settings|
|
5
5
|
puts "#{type.capitalize}:"
|
6
6
|
settings.each do |setting, value|
|
7
7
|
value = '[unset]' if value == ''
|
@@ -23,13 +23,12 @@ command :admin do |admin|
|
|
23
23
|
# Update Git config
|
24
24
|
Git.sh "git config peas.domain #{domain}"
|
25
25
|
# Update file
|
26
|
-
|
27
|
-
File.open(Peas.config_file, 'w+') { |f| f.write(content) }
|
26
|
+
Peas.update_config domain: domain
|
28
27
|
@api = API.new # Refresh settings from git/file because there's a new domain URI
|
29
28
|
end
|
30
|
-
@api.request(:put, '/admin/settings', args[0] => args[1]
|
29
|
+
format_settings @api.request(:put, '/admin/settings', { args[0] => args[1] }, true, false)
|
31
30
|
else
|
32
|
-
@api.request(:get, '/admin/settings'
|
31
|
+
format_settings @api.request(:get, '/admin/settings', true, false)
|
33
32
|
end
|
34
33
|
end
|
35
34
|
end
|
data/lib/peas/commands/app.rb
CHANGED
@@ -15,11 +15,13 @@ command :create do |c|
|
|
15
15
|
unless File.exist? public_key_path
|
16
16
|
exit_now! "Couldn't find an SSH public key", 1
|
17
17
|
end
|
18
|
+
params = {
|
19
|
+
'muse' => Git.name_from_remote(Git.remote('origin')),
|
20
|
+
}
|
18
21
|
response = @api.request(
|
19
22
|
:post,
|
20
23
|
'/app',
|
21
|
-
|
22
|
-
public_key: File.open(public_key_path).read
|
24
|
+
params
|
23
25
|
)
|
24
26
|
Git.add_remote response['remote_uri']
|
25
27
|
end
|
@@ -55,7 +57,7 @@ command :scale do |c|
|
|
55
57
|
@api.request(
|
56
58
|
:put,
|
57
59
|
"/app/#{Git.name_from_remote}/scale",
|
58
|
-
scaling_hash
|
60
|
+
'scaling_hash' => scaling_hash.to_json
|
59
61
|
)
|
60
62
|
end
|
61
63
|
end
|
data/lib/peas/commands/config.rb
CHANGED
@@ -11,7 +11,7 @@ command :config do |c|
|
|
11
11
|
@api.request(
|
12
12
|
:delete,
|
13
13
|
"/app/#{Git.name_from_remote}/config",
|
14
|
-
keys
|
14
|
+
'keys' => args.to_json
|
15
15
|
)
|
16
16
|
end
|
17
17
|
end
|
@@ -32,7 +32,7 @@ command :config do |c|
|
|
32
32
|
@api.request(
|
33
33
|
:put,
|
34
34
|
"/app/#{Git.name_from_remote}/config",
|
35
|
-
vars
|
35
|
+
'vars' => vars.to_json
|
36
36
|
)
|
37
37
|
end
|
38
38
|
end
|
data/lib/peas/config.rb
CHANGED
@@ -14,6 +14,12 @@ module Peas
|
|
14
14
|
JSON.parse contents
|
15
15
|
end
|
16
16
|
|
17
|
+
# Merge new key/values into the config file
|
18
|
+
def self.update_config(hash)
|
19
|
+
content = Peas.config.merge(hash).to_json
|
20
|
+
File.open(Peas.config_file, 'w+') { |f| f.write(content) }
|
21
|
+
end
|
22
|
+
|
17
23
|
# Hierarchy of sources for the Peas API domain
|
18
24
|
def self.api_domain
|
19
25
|
git_domain = Git.sh 'git config peas.domain'
|
@@ -25,7 +31,7 @@ module Peas
|
|
25
31
|
elsif Peas.config['domain']
|
26
32
|
Peas.config['domain']
|
27
33
|
else
|
28
|
-
'vcap.me:
|
34
|
+
'vcap.me:4443'
|
29
35
|
end
|
30
36
|
unless domain[/\Ahttp:\/\//] || domain[/\Ahttps:\/\//]
|
31
37
|
"https://#{domain}"
|
data/peas-cli.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: peas-cli 0.
|
5
|
+
# stub: peas-cli 0.7.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "peas-cli"
|
9
|
-
s.version = "0.
|
9
|
+
s.version = "0.7.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Tom Buckley-Houston"]
|
14
|
-
s.date = "2015-01-
|
14
|
+
s.date = "2015-01-17"
|
15
15
|
s.description = "Peas is an open source Heroku-style PaaS written in Ruby and using Docker"
|
16
16
|
s.email = "tom@tombh.co.uk"
|
17
17
|
s.executables = ["peas"]
|
data/spec/cli_spec.rb
CHANGED
@@ -5,6 +5,7 @@ describe 'Peas CLI' do
|
|
5
5
|
allow(Git).to receive(:sh).and_return(nil)
|
6
6
|
allow(Git).to receive(:remote).and_return('git@github.com:test-test.git')
|
7
7
|
allow_any_instance_of(API).to receive(:sleep).and_return(nil)
|
8
|
+
allow_any_instance_of(API).to receive(:api_key).and_return 'APIKEY'
|
8
9
|
allow(Peas).to receive(:config_file).and_return('/tmp/.peas')
|
9
10
|
File.delete '/tmp/.peas' if File.exist? '/tmp/.peas'
|
10
11
|
end
|
@@ -19,7 +20,8 @@ describe 'Peas CLI' do
|
|
19
20
|
describe 'Settings' do
|
20
21
|
it 'should set and use the domain setting' do
|
21
22
|
stub_request(:put, 'https://new-domain.com:4000/admin/settings?peas.domain=new-domain.com:4000')
|
22
|
-
.
|
23
|
+
.with(headers: { 'X-Api-Key' => 'APIKEY' })
|
24
|
+
.to_return(body: response_mock)
|
23
25
|
expect(Git).to receive(:sh).with("git config peas.domain https://new-domain.com:4000")
|
24
26
|
cli %w(admin settings peas.domain new-domain.com:4000)
|
25
27
|
config = JSON.parse File.open('/tmp/.peas').read
|
@@ -27,7 +29,8 @@ describe 'Peas CLI' do
|
|
27
29
|
end
|
28
30
|
|
29
31
|
it 'should set a normal setting' do
|
30
|
-
stub_request(:put, '
|
32
|
+
stub_request(:put, TEST_DOMAIN + '/admin/settings?mongodb.uri=mongodb://uri')
|
33
|
+
.with(headers: { 'X-Api-Key' => 'APIKEY' })
|
31
34
|
.to_return(body: response_mock(
|
32
35
|
defaults: { 'peas.domain' => 'https://boss.com' },
|
33
36
|
services: {
|
@@ -44,17 +47,15 @@ describe 'Peas CLI' do
|
|
44
47
|
describe 'App methods' do
|
45
48
|
it 'should list all apps' do
|
46
49
|
stub_request(:get, TEST_DOMAIN + '/app')
|
50
|
+
.with(headers: { 'X-Api-Key' => 'APIKEY' })
|
47
51
|
.to_return(body: response_mock(["coolapp"]))
|
48
52
|
output = cli ['apps']
|
49
53
|
expect(output).to eq "coolapp\n"
|
50
54
|
end
|
51
55
|
|
52
56
|
it 'should create an app and its remote' do
|
53
|
-
|
54
|
-
|
55
|
-
expect(File).to receive(:exist?).with(public_key_path) { true }
|
56
|
-
expect(File).to receive(:open).with(public_key_path) { double(read: 'apublickey') }
|
57
|
-
stub_request(:post, TEST_DOMAIN + '/app?muse=test-test&public_key=apublickey')
|
57
|
+
stub_request(:post, TEST_DOMAIN + '/app?muse=test-test')
|
58
|
+
.with(headers: { 'X-Api-Key' => 'APIKEY' })
|
58
59
|
.to_return(
|
59
60
|
body: {
|
60
61
|
version: Peas::VERSION,
|
@@ -71,6 +72,7 @@ describe 'Peas CLI' do
|
|
71
72
|
|
72
73
|
it 'should destroy an app' do
|
73
74
|
stub_request(:delete, TEST_DOMAIN + '/app/test-test')
|
75
|
+
.with(headers: { 'X-Api-Key' => 'APIKEY' })
|
74
76
|
.to_return(body: response_mock("App 'test' successfully destroyed"))
|
75
77
|
expect(Git).to receive(:remove_remote)
|
76
78
|
output = cli ['destroy']
|
@@ -79,11 +81,15 @@ describe 'Peas CLI' do
|
|
79
81
|
|
80
82
|
it 'should scale an app', :with_socket do
|
81
83
|
expect(@socket).to receive(:puts).with('subscribe.job_progress.123')
|
84
|
+
expect(@socket).to receive(:puts).with "APIKEY"
|
82
85
|
stub_request(
|
83
86
|
:put,
|
84
87
|
TEST_DOMAIN + '/app/test-test/scale?scaling_hash=%7B%22web%22:%223%22,%22worker%22:%222%22%7D'
|
85
|
-
)
|
88
|
+
)
|
89
|
+
.with(headers: { 'X-Api-Key' => 'APIKEY' })
|
90
|
+
.to_return(body: '{"job": "123"}')
|
86
91
|
allow(@socket).to receive(:gets).and_return(
|
92
|
+
'AUTHORISED',
|
87
93
|
'{"body":"scaling"}',
|
88
94
|
'{"status":"complete"}'
|
89
95
|
)
|
@@ -116,6 +122,7 @@ describe 'Peas CLI' do
|
|
116
122
|
describe 'Config ENV vars' do
|
117
123
|
it 'should set config for an app' do
|
118
124
|
stub_request(:put, TEST_DOMAIN + '/app/test-test/config?vars=%7B%22FOO%22:%22BAR%22%7D')
|
125
|
+
.with(headers: { 'X-Api-Key' => 'APIKEY' })
|
119
126
|
.to_return(body: response_mock("{'FOO' => 'BAR'}"))
|
120
127
|
output = cli %w(config set FOO=BAR)
|
121
128
|
expect(output).to eq "{'FOO' => 'BAR'}\n"
|
@@ -123,6 +130,7 @@ describe 'Peas CLI' do
|
|
123
130
|
|
124
131
|
it 'delete config for an app' do
|
125
132
|
stub_request(:delete, TEST_DOMAIN + '/app/test-test/config?keys=%5B%22FOO%22%5D')
|
133
|
+
.with(headers: { 'X-Api-Key' => 'APIKEY' })
|
126
134
|
.to_return(body: response_mock(nil))
|
127
135
|
output = cli %w(config rm FOO)
|
128
136
|
expect(output).to eq "\n"
|
@@ -130,6 +138,7 @@ describe 'Peas CLI' do
|
|
130
138
|
|
131
139
|
it 'should list all config for an app' do
|
132
140
|
stub_request(:get, TEST_DOMAIN + '/app/test-test/config')
|
141
|
+
.with(headers: { 'X-Api-Key' => 'APIKEY' })
|
133
142
|
.to_return(body: response_mock("{'FOO' => 'BAR'}\n{'MOO' => 'CAR'}"))
|
134
143
|
output = cli %w(config)
|
135
144
|
expect(output).to eq "{'FOO' => 'BAR'}\n{'MOO' => 'CAR'}\n"
|
@@ -140,7 +149,13 @@ describe 'Peas CLI' do
|
|
140
149
|
describe 'Logs' do
|
141
150
|
it 'should stream logs', :with_socket do
|
142
151
|
expect(@socket).to receive(:puts).with('stream_logs.test-test')
|
143
|
-
|
152
|
+
expect(@socket).to receive(:puts).with "APIKEY"
|
153
|
+
allow(@socket).to receive(:gets).and_return(
|
154
|
+
"AUTHORISED",
|
155
|
+
"Here's ya logs",
|
156
|
+
"MOAR",
|
157
|
+
false
|
158
|
+
)
|
144
159
|
output = cli %w(logs)
|
145
160
|
expect(output).to eq "Here's ya logs\nMOAR\n"
|
146
161
|
end
|
@@ -148,7 +163,14 @@ describe 'Peas CLI' do
|
|
148
163
|
|
149
164
|
it 'should retrieve and output a long-running command', :with_socket do
|
150
165
|
expect(@socket).to receive(:puts).with('subscribe.job_progress.123')
|
151
|
-
|
166
|
+
expect(@socket).to receive(:puts).with "APIKEY"
|
167
|
+
allow(@socket).to receive(:gets).and_return(
|
168
|
+
"AUTHORISED",
|
169
|
+
"doing",
|
170
|
+
"something",
|
171
|
+
"done",
|
172
|
+
false
|
173
|
+
)
|
152
174
|
expect(API).to receive(:puts).with "doing"
|
153
175
|
expect(API).to receive(:puts).with "something"
|
154
176
|
expect(API).to receive(:puts).with "done"
|
@@ -157,6 +179,7 @@ describe 'Peas CLI' do
|
|
157
179
|
|
158
180
|
it 'should show a warning when there is a version mismatch' do
|
159
181
|
stub_request(:get, TEST_DOMAIN + '/app/test-test/config')
|
182
|
+
.with(headers: { 'X-Api-Key' => 'APIKEY' })
|
160
183
|
.to_return(body: '{"version": "100000.1000000.100000"}')
|
161
184
|
output = cli %w(config)
|
162
185
|
expect(output).to include 'Your version of the CLI client is out of date'
|
data/spec/spec_helper.rb
CHANGED
@@ -9,7 +9,7 @@ require 'lib/peas'
|
|
9
9
|
ENV['GLI_ENV'] = 'test'
|
10
10
|
ROOT = File.join(File.expand_path(File.dirname(__FILE__)), '..')
|
11
11
|
$LOAD_PATH.unshift(File.join(ROOT, 'lib'))
|
12
|
-
TEST_DOMAIN = 'https://vcap.me:
|
12
|
+
TEST_DOMAIN = 'https://vcap.me:4443'
|
13
13
|
SWITCHBOARD_TEST_PORT = 79345
|
14
14
|
SSL_KEY_PATH = "#{ROOT}/../contrib/ssl-keys/server.key"
|
15
15
|
SSL_KEY = OpenSSL::PKey::RSA.new File.read(SSL_KEY_PATH)
|
@@ -34,6 +34,7 @@ RSpec.configure do |config|
|
|
34
34
|
end
|
35
35
|
|
36
36
|
config.before(:each, :with_echo_server) do
|
37
|
+
allow_any_instance_of(API).to receive(:api_key).and_return('APIKEY')
|
37
38
|
tcp_server = TCPServer.new 'vcap.me', SWITCHBOARD_TEST_PORT
|
38
39
|
context = OpenSSL::SSL::SSLContext.new
|
39
40
|
context.key = SSL_KEY
|
@@ -43,6 +44,12 @@ RSpec.configure do |config|
|
|
43
44
|
@connection = @server.accept
|
44
45
|
begin
|
45
46
|
Timeout.timeout(2) do
|
47
|
+
auth = @connection.gets.strip
|
48
|
+
if auth == 'APIKEY'
|
49
|
+
@connection.puts 'AUTHORISED'
|
50
|
+
else
|
51
|
+
@connection.puts 'UNAUTHORISED'
|
52
|
+
end
|
46
53
|
while (line = @connection.gets)
|
47
54
|
@connection.write line
|
48
55
|
@connection.close if line.strip == 'FINAL COMMAND'
|
@@ -73,7 +80,7 @@ end
|
|
73
80
|
|
74
81
|
# Form a response as the API would. Useful as you only need to provide a string without any JSON
|
75
82
|
# formatting
|
76
|
-
def response_mock(response, key = :message)
|
83
|
+
def response_mock(response = {}, key = :message)
|
77
84
|
{
|
78
85
|
'version' => Peas::VERSION,
|
79
86
|
key => response
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: peas-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Buckley-Houston
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: gli
|