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.
Files changed (46) hide show
  1. data/.gitignore +26 -0
  2. data/.rspec +1 -0
  3. data/.rvmrc +7 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +76 -0
  6. data/README.md +44 -0
  7. data/Rakefile +14 -0
  8. data/bin/pairzone +83 -0
  9. data/config/cucumber.yml +4 -0
  10. data/features/collaborator_joins_pairzone.feature +20 -0
  11. data/features/developer_signs_in.feature +11 -0
  12. data/features/developer_starts_pairzone.feature +35 -0
  13. data/features/keys/bob.key +12 -0
  14. data/features/keys/bob.key.pub +1 -0
  15. data/features/keys/derek.key +27 -0
  16. data/features/keys/derek.key.pub +1 -0
  17. data/features/server/stub-pairzone-server.rb +76 -0
  18. data/features/step_definitions/authorisation_steps.rb +8 -0
  19. data/features/step_definitions/info_steps.rb +5 -0
  20. data/features/step_definitions/repository_steps.rb +6 -0
  21. data/features/step_definitions/running_steps.rb +32 -0
  22. data/features/step_definitions/starting_steps.rb +47 -0
  23. data/features/support/aruba.rb +1 -0
  24. data/features/support/env.rb +5 -0
  25. data/features/support/helpers/server_helper.rb +40 -0
  26. data/features/support/hooks.rb +24 -0
  27. data/features/support/pairzone-config.rb +13 -0
  28. data/lib/pairzone.rb +14 -0
  29. data/lib/pairzone/api/base.rb +27 -0
  30. data/lib/pairzone/api/pairzone.rb +25 -0
  31. data/lib/pairzone/api/pairzone_connection.rb +32 -0
  32. data/lib/pairzone/api/pairzone_lifecycle.rb +34 -0
  33. data/lib/pairzone/api/user.rb +16 -0
  34. data/lib/pairzone/authenticate.rb +51 -0
  35. data/lib/pairzone/commands/info.rb +25 -0
  36. data/lib/pairzone/commands/join.rb +21 -0
  37. data/lib/pairzone/commands/start.rb +29 -0
  38. data/lib/pairzone/logger.rb +33 -0
  39. data/lib/pairzone/shell.rb +38 -0
  40. data/lib/pairzone/version.rb +3 -0
  41. data/pairzone.gemspec +35 -0
  42. data/spec/pairzone/api/pairzone_lifecycle_spec.rb +26 -0
  43. data/spec/pairzone/authentication_spec.rb +44 -0
  44. data/spec/pairzone/logger_spec.rb +41 -0
  45. data/spec/spec_helper.rb +7 -0
  46. 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,5 @@
1
+ Then /^he sees information about his pairzone account$/ do
2
+ Then %{the output should contain "Pairzone Account Information"}
3
+ And %{the output should contain "Username: derek"}
4
+ end
5
+
@@ -0,0 +1,6 @@
1
+ Given /^I have a local git repository$/ do
2
+ Given 'I run "git init"'
3
+ Given 'I run "echo foo >foo"'
4
+ Given 'I run "git add ."'
5
+ Given %{I run "git commit -m 'Initial commit'"}
6
+ 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,5 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', '..', 'lib'))
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+ require 'pairzone'
@@ -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
@@ -0,0 +1,13 @@
1
+ require 'fileutils'
2
+
3
+ def config_directory
4
+ "#{File.dirname(__FILE__)}/../pairzone-tmp-config"
5
+ end
6
+
7
+ def tokens
8
+ @tokens ||= {}
9
+ end
10
+
11
+ After do
12
+ FileUtils.rm_rf(config_directory)
13
+ 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