pairzone 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.
- data/.gitignore +26 -0
- data/.rspec +1 -0
- data/.rvmrc +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +76 -0
- data/README.md +44 -0
- data/Rakefile +14 -0
- data/bin/pairzone +83 -0
- data/config/cucumber.yml +4 -0
- data/features/collaborator_joins_pairzone.feature +20 -0
- data/features/developer_signs_in.feature +11 -0
- data/features/developer_starts_pairzone.feature +35 -0
- data/features/keys/bob.key +12 -0
- data/features/keys/bob.key.pub +1 -0
- data/features/keys/derek.key +27 -0
- data/features/keys/derek.key.pub +1 -0
- data/features/server/stub-pairzone-server.rb +76 -0
- data/features/step_definitions/authorisation_steps.rb +8 -0
- data/features/step_definitions/info_steps.rb +5 -0
- data/features/step_definitions/repository_steps.rb +6 -0
- data/features/step_definitions/running_steps.rb +32 -0
- data/features/step_definitions/starting_steps.rb +47 -0
- data/features/support/aruba.rb +1 -0
- data/features/support/env.rb +5 -0
- data/features/support/helpers/server_helper.rb +40 -0
- data/features/support/hooks.rb +24 -0
- data/features/support/pairzone-config.rb +13 -0
- data/lib/pairzone.rb +14 -0
- data/lib/pairzone/api/base.rb +27 -0
- data/lib/pairzone/api/pairzone.rb +25 -0
- data/lib/pairzone/api/pairzone_connection.rb +32 -0
- data/lib/pairzone/api/pairzone_lifecycle.rb +34 -0
- data/lib/pairzone/api/user.rb +16 -0
- data/lib/pairzone/authenticate.rb +51 -0
- data/lib/pairzone/commands/info.rb +25 -0
- data/lib/pairzone/commands/join.rb +21 -0
- data/lib/pairzone/commands/start.rb +29 -0
- data/lib/pairzone/logger.rb +33 -0
- data/lib/pairzone/shell.rb +38 -0
- data/lib/pairzone/version.rb +3 -0
- data/pairzone.gemspec +35 -0
- data/spec/pairzone/api/pairzone_lifecycle_spec.rb +26 -0
- data/spec/pairzone/authentication_spec.rb +44 -0
- data/spec/pairzone/logger_spec.rb +41 -0
- data/spec/spec_helper.rb +7 -0
- metadata +292 -0
@@ -0,0 +1,8 @@
|
|
1
|
+
When /^"([^"]*)" supplies his credentials$/ do |developer_name|
|
2
|
+
When %{I type "#{developer_name}@example.com"}
|
3
|
+
And %{I type "#{developer_name}_password"}
|
4
|
+
end
|
5
|
+
|
6
|
+
Then /^his API key is saved$/ do
|
7
|
+
Then %{the file "#{config_directory}/authentication_token" should contain "#{@apikey}"}
|
8
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
def save_token(username)
|
2
|
+
create_api_account_for(username) unless tokens.include? username
|
3
|
+
Pairzone::Authenticate.save_token("#{config_directory}/authentication_token", tokens[username])
|
4
|
+
end
|
5
|
+
|
6
|
+
def pairzone_command(command, identity = 'bob', interactive = false)
|
7
|
+
pairzone, *args = command.split
|
8
|
+
interactively = interactive ? " interactively" : ""
|
9
|
+
When %{I run "#{pairzone} --server=localhost:54101 --debug --config=#{config_directory} --identity=../../features/keys/#{identity}.key #{args.join(' ')}"#{interactively}}
|
10
|
+
end
|
11
|
+
|
12
|
+
When /^"([^"]*)" runs? "([^"]*)" in test mode$/ do |developer_name, command|
|
13
|
+
save_token(developer_name)
|
14
|
+
pairzone_command(command, developer_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
When /^"([^"]*)" runs "([^"]*)" in test mode with no api key$/ do |developer_name, command|
|
18
|
+
create_api_account_for(developer_name)
|
19
|
+
pairzone_command(command, developer_name, true)
|
20
|
+
end
|
21
|
+
|
22
|
+
Given /^"([^"]*)" has already started a pairzone with "([^"]*)" as a collaborator$/ do |developer_name, collaborator_name|
|
23
|
+
Given "I have a local git repository"
|
24
|
+
save_token(developer_name)
|
25
|
+
pairzone_command("pairzone start --background -c #{collaborator_name}", developer_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
Given /^"([^"]*)" has already started a pairzone$/ do |developer_name|
|
29
|
+
Given "I have a local git repository"
|
30
|
+
save_token(developer_name)
|
31
|
+
pairzone_command("pairzone start --background", developer_name)
|
32
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
def look_for(text)
|
2
|
+
Then %{the output should contain "#{text}"}
|
3
|
+
end
|
4
|
+
|
5
|
+
Then /^a new pairzone is started for "([^"]*)"$/ do |developer_name|
|
6
|
+
look_for "Starting Pairzone for project 'aruba'..."
|
7
|
+
look_for "Pairzone '#{developer_name}-aruba' started."
|
8
|
+
look_for "Pairzone booted: ip address 127.0.0.1"
|
9
|
+
end
|
10
|
+
|
11
|
+
Then /^no pairzone should be started$/ do
|
12
|
+
And %{the output should not contain "started"}
|
13
|
+
end
|
14
|
+
|
15
|
+
Then /^a new pairzone is started for "([^"]*)" in the background$/ do |developer_name|
|
16
|
+
Then %{a new pairzone is started for "#{developer_name}"}
|
17
|
+
And %{the output should not contain "pairzone-tmux"} # We cannot run the session, but if this shows up we tried to
|
18
|
+
end
|
19
|
+
|
20
|
+
Then /^the code in my current directory is pushed to the server$/ do
|
21
|
+
look_for "pushing local code to pairzone instance"
|
22
|
+
look_for "master -> master (forced update)" # the git printout
|
23
|
+
end
|
24
|
+
|
25
|
+
Then /^an SSH connection is made to my new pairzone$/ do
|
26
|
+
look_for "/usr/local/bin/pairzone-tmux.sh: No such file or directory" # We cannot run the session, but this will show we tried to
|
27
|
+
end
|
28
|
+
|
29
|
+
Then /^the code is downloaded to a "([^"]*)" branch when I quit the session$/ do |branch_name|
|
30
|
+
Then %{the output should match /master.*->.*#{branch_name}/}
|
31
|
+
end
|
32
|
+
|
33
|
+
Then /^"([^"]*)" is added as a collaborator on my new pairzone$/ do |collaborator_name|
|
34
|
+
look_for "Collaborator '#{collaborator_name}' added."
|
35
|
+
end
|
36
|
+
|
37
|
+
Then /^"([^"]*)" is not added as a collaborator on my new pairzone$/ do |collaborator_name|
|
38
|
+
Then %{the output should not contain "Collaborator '#{collaborator_name}' added."}
|
39
|
+
end
|
40
|
+
|
41
|
+
Then /^"([^"]*)" should be connected to "([^"]*)"$/ do |arg1, arg2|
|
42
|
+
look_for "/usr/local/bin/pairzone-tmux.sh: No such file or directory" # We cannot run the session, but this will show we tried to
|
43
|
+
end
|
44
|
+
|
45
|
+
Then /^"([^"]*)" should not be able to connect$/ do |collaborator_name|
|
46
|
+
look_for "No pairzone exists"
|
47
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'aruba/cucumber'
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'background_process'
|
2
|
+
|
3
|
+
module ServerHelper
|
4
|
+
SERVER_PATH = "#{File.dirname(__FILE__)}/../../../../pairzone-web"
|
5
|
+
def server(command)
|
6
|
+
wrapped = "bash -lc 'cd #{SERVER_PATH} && BUNDLE_GEMFILE=#{SERVER_PATH}/Gemfile && #{command}'"
|
7
|
+
BackgroundProcess.run(wrapped)
|
8
|
+
end
|
9
|
+
|
10
|
+
def start_server
|
11
|
+
@server = server("rails s -e smoke -p 54101")
|
12
|
+
@server.detect {|line| out << line; line =~ /WEBrick::HTTPServer#start/ }
|
13
|
+
end
|
14
|
+
|
15
|
+
def out
|
16
|
+
@out ||= []
|
17
|
+
end
|
18
|
+
|
19
|
+
def stop_server
|
20
|
+
if (@server && @server.running?)
|
21
|
+
# only reliable way I've found to kill it, as it's in a subshell
|
22
|
+
`ps ax | grep "rails s -e smoke -p 54101" | grep -v bash | grep -v grep | cut -d' ' -f 1 | xargs kill -s INT`
|
23
|
+
@server.wait
|
24
|
+
out << @server.stdout.read
|
25
|
+
out << @server.stderr.read
|
26
|
+
end
|
27
|
+
puts "\nTest server output:\n" + out.join if @show_web_server_output
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_api_account_for(developer_name)
|
31
|
+
@rake = server("RAILS_ENV=smoke rake db:reset db:setup pairzone:test:clear_users pairzone:test:add_user[#{developer_name},#{developer_name}_password]")
|
32
|
+
@rake.wait
|
33
|
+
out << @rake.stderr.read
|
34
|
+
key = @rake.stdout.read.split.last.strip
|
35
|
+
out << "Created api account for #{developer_name}. Key: #{key}\n\n"
|
36
|
+
tokens[developer_name] = key
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
World(ServerHelper)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Before("@api_server") do
|
2
|
+
start_server
|
3
|
+
end
|
4
|
+
|
5
|
+
Before("@announce") do
|
6
|
+
@show_web_server_output = true
|
7
|
+
end
|
8
|
+
|
9
|
+
Before('@derek') do
|
10
|
+
create_api_account_for('derek')
|
11
|
+
end
|
12
|
+
|
13
|
+
Before('@bob') do
|
14
|
+
create_api_account_for('bob')
|
15
|
+
end
|
16
|
+
|
17
|
+
Before('@jim') do
|
18
|
+
create_api_account_for('jim')
|
19
|
+
end
|
20
|
+
|
21
|
+
After do
|
22
|
+
stop_server
|
23
|
+
@show_web_server_output = false
|
24
|
+
end
|
data/lib/pairzone.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'pairzone/version'
|
2
|
+
require 'pairzone/authenticate'
|
3
|
+
require 'pairzone/shell'
|
4
|
+
require 'pairzone/logger'
|
5
|
+
|
6
|
+
require 'pairzone/api/base'
|
7
|
+
require 'pairzone/api/pairzone_lifecycle'
|
8
|
+
require 'pairzone/api/pairzone_connection'
|
9
|
+
require 'pairzone/api/pairzone'
|
10
|
+
require 'pairzone/api/user'
|
11
|
+
|
12
|
+
require 'pairzone/commands/info'
|
13
|
+
require 'pairzone/commands/start'
|
14
|
+
require 'pairzone/commands/join'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'party_resource'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Pairzone
|
5
|
+
module Api
|
6
|
+
class Base
|
7
|
+
include PartyResource
|
8
|
+
|
9
|
+
def initialize(data)
|
10
|
+
Logger.debug("Received DATA: #{data}")
|
11
|
+
populate_properties(data.values.first)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.connect_to(host, options)
|
15
|
+
params = {
|
16
|
+
base_uri: host,
|
17
|
+
headers: {
|
18
|
+
'Accept' => 'application/json',
|
19
|
+
'User-agent' => 'pairzone-gem/' + VERSION
|
20
|
+
}
|
21
|
+
}.merge(options)
|
22
|
+
Logger.debug("Connecting to API server with params: #{params.inspect}")
|
23
|
+
PartyResource::Connector.add(:pairzone, params)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Pairzone
|
2
|
+
module Api
|
3
|
+
class Pairzone < Base
|
4
|
+
include PairzoneConnection
|
5
|
+
extend PairzoneLifecycle
|
6
|
+
|
7
|
+
attr :identity
|
8
|
+
|
9
|
+
property :name
|
10
|
+
property :owner
|
11
|
+
property :collaborators
|
12
|
+
property :ip
|
13
|
+
property :project_name
|
14
|
+
property :status
|
15
|
+
|
16
|
+
connect :find_by_project_name, :get => '/pairzones/:name.json', :with => :name, :on => :class, :rescue => { 'ResourceNotFound' => nil }
|
17
|
+
|
18
|
+
connect :create, :post => '/pairzones', :with => :pairzone, :on => :class
|
19
|
+
|
20
|
+
def to_str
|
21
|
+
"<%= color('This Pairzone: #{name}', CYAN + BOLD) %>\nOwner: #{owner}\nCollaborators: #{collaborators.join(', ')}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Pairzone
|
2
|
+
module Api
|
3
|
+
module PairzoneConnection
|
4
|
+
def connect(identity)
|
5
|
+
Logger.info("Connecting to Pairzone...")
|
6
|
+
shell(identity).run(%{ssh -i #{identity} -t pairzone@#{ip} "/usr/local/bin/pairzone-tmux.sh"})
|
7
|
+
end
|
8
|
+
|
9
|
+
def push_code(identity)
|
10
|
+
Logger.debug("pushing local code to pairzone instance")
|
11
|
+
shell(identity).remote(ip, "git init #{project_name} &&
|
12
|
+
cd #{project_name} &&
|
13
|
+
git config receive.denyCurrentBranch ignore")
|
14
|
+
shell(identity).git("git push --mirror #{git_location}")
|
15
|
+
shell(identity).remote(ip, "cd #{project_name} && git reset --hard")
|
16
|
+
end
|
17
|
+
|
18
|
+
def fetch_code(identity)
|
19
|
+
shell(identity).git "git fetch #{git_location} master:pairzone"
|
20
|
+
Logger.info("Your git code has been placed in a local 'pairzone' branch. To merge this code into your HEAD, run this commmand:\n\ngit merge pairzone")
|
21
|
+
end
|
22
|
+
|
23
|
+
def shell(identity)
|
24
|
+
@shell ||= Shell.new(identity)
|
25
|
+
end
|
26
|
+
|
27
|
+
def git_location
|
28
|
+
"pairzone@#{ip}:#{project_name}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
module Pairzone
|
3
|
+
module Api
|
4
|
+
module PairzoneLifecycle
|
5
|
+
attr_accessor :start_wait_interval
|
6
|
+
DEFAULT_START_WAIT_INTERVAL = 5
|
7
|
+
|
8
|
+
def start(options)
|
9
|
+
Logger.info("Starting Pairzone for project '#{options[:project_name]}'...")
|
10
|
+
pairzone = create(:project_name => options[:project_name], :collaborators => options[:collaborators])
|
11
|
+
report_starting_status(pairzone)
|
12
|
+
|
13
|
+
wait_for(pairzone)
|
14
|
+
end
|
15
|
+
|
16
|
+
def report_starting_status(pairzone)
|
17
|
+
Logger.info("Pairzone '#{pairzone.name}' started.")
|
18
|
+
pairzone.collaborators.each do |collaborator|
|
19
|
+
Logger.info("Collaborator '#{collaborator}' added.")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def wait_for(pairzone)
|
24
|
+
while pairzone.status != 'started' do
|
25
|
+
Logger.info("Waiting for pairzone to boot...")
|
26
|
+
sleep start_wait_interval || DEFAULT_START_WAIT_INTERVAL
|
27
|
+
pairzone = find_by_project_name(pairzone.name)
|
28
|
+
end
|
29
|
+
Logger.info("Pairzone booted: ip address #{pairzone.ip}")
|
30
|
+
pairzone
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Pairzone
|
2
|
+
module Api
|
3
|
+
class User < Base
|
4
|
+
property :email
|
5
|
+
property :username
|
6
|
+
property :authentication_token
|
7
|
+
|
8
|
+
connect :current_user, :get => '/user'
|
9
|
+
|
10
|
+
def to_str
|
11
|
+
"Username: #{username}\n" +
|
12
|
+
"Email: #{email}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'highline'
|
3
|
+
|
4
|
+
module Pairzone
|
5
|
+
class Authenticate
|
6
|
+
def self.retrieve_key(config, server)
|
7
|
+
@api_key_location = "#{config}/authentication_token"
|
8
|
+
Logger.debug("retrieving pairzone key")
|
9
|
+
Logger.debug("checking for file #{@api_key_location}")
|
10
|
+
if (File.exist?(@api_key_location))
|
11
|
+
key = key_from_file
|
12
|
+
else
|
13
|
+
key = key_from_server(server)
|
14
|
+
end
|
15
|
+
Logger.debug("api key discovered '#{key}'")
|
16
|
+
key
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.key_from_file
|
20
|
+
File.read(@api_key_location).strip
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.key_from_server(server)
|
24
|
+
Logger.info "<%= color('Hello, welcome to Pairzone.', CYAN + BOLD) %>"
|
25
|
+
Logger.info "We need some information so we can tie this computer to your account."
|
26
|
+
user = nil
|
27
|
+
begin
|
28
|
+
email = Logger.ask("Pairzone email: ")
|
29
|
+
password = Logger.ask("Pairzone password: ") { |q| q.echo = "." }
|
30
|
+
|
31
|
+
Pairzone::Api::Base.connect_to(server, username: email, password: password, default: false)
|
32
|
+
Logger.debug("Retrieving key from server with supplied credentials")
|
33
|
+
user = Pairzone::Api::User.current_user
|
34
|
+
unless user
|
35
|
+
Logger.info "Cannot authenticate: please try again."
|
36
|
+
end
|
37
|
+
end while user.nil?
|
38
|
+
save_token(@api_key_location, user.authentication_token)
|
39
|
+
user.authentication_token
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.save_token(location, token)
|
43
|
+
FileUtils.mkdir_p(File.dirname(location))
|
44
|
+
Logger.debug("Saving API key to #{location}")
|
45
|
+
File.open(location, "w") do |f|
|
46
|
+
f.puts(token)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Pairzone
|
2
|
+
module Commands
|
3
|
+
class Info
|
4
|
+
def initialize(project_name)
|
5
|
+
@project_name = project_name
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute
|
9
|
+
Logger.info("<%= color('Pairzone Account Information', CYAN + BOLD) %>")
|
10
|
+
@user = Pairzone::Api::User.current_user
|
11
|
+
Logger.info(@user)
|
12
|
+
Logger.info("")
|
13
|
+
Logger.debug("Finding information about local pairzone: #{local_pairzone_name}")
|
14
|
+
pairzone = Pairzone::Api::Pairzone.find_by_project_name(local_pairzone_name)
|
15
|
+
Logger.info(pairzone || "No pairzone found for this directory (create one with <%= color('pairzone create', CYAN) %>).")
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def local_pairzone_name
|
21
|
+
"#{@user.username}-#{@project_name}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module Pairzone
|
4
|
+
module Commands
|
5
|
+
class Join
|
6
|
+
def initialize(pairzone_name, identity)
|
7
|
+
@pairzone_name = pairzone_name
|
8
|
+
@identity = File.expand_path(identity)
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
pairzone = Pairzone::Api::Pairzone.find_by_project_name(@pairzone_name)
|
13
|
+
if (pairzone.nil?)
|
14
|
+
Logger.error("No pairzone exists with the name '#{@pairzone_name}'.")
|
15
|
+
else
|
16
|
+
pairzone.connect(@identity)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|