pairzone 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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