anjou 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f9a15edb4af49d3d799f55b5748292f65b6ae3ba
4
+ data.tar.gz: b47599d0848bfea7b65d2c0239f3ced13cff5f4f
5
+ SHA512:
6
+ metadata.gz: 241ee6a63f6f680279f191ea3331ded3783160be2c8485603e5fe146358a98e48d9fa1115d05486cb96bfdd1d7ef92f2e969b2230bba251326e4098818d6cb5d
7
+ data.tar.gz: e6787c8cfdfb9272f6ea330263e56bc174bcdf74f58310ada152bc052882db3652694d3d1ffc9c8662f3c23e55e5837f7d151453e96c6bfbd97925ffeb2ee0e8
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ # added by coy
20
+ .coy
21
+
22
+ # added by coy
23
+ credentials
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in anjou.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Joel Helbling
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,30 @@
1
+ # Anjou
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'anjou'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install anjou
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
30
+ 7. Count your money
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.pattern = 'spec/lib/**/*_spec.rb'
6
+ t.rspec_opts = " --format doc"
7
+ end
8
+
9
+ task default: :spec
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'anjou/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "anjou"
8
+ spec.version = Anjou::VERSION
9
+ spec.authors = ["Joel Helbling"]
10
+ spec.email = ["joel@joelhelbling.com"]
11
+ spec.description = %q{Remote pair programming made super easy}
12
+ spec.summary = %q{Anjou brings you, your stuff, some other programmer and their stuff all together on a pair programming machine.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "aws-sdk", "~> 1.32.0"
22
+ spec.add_dependency "net-sftp", "~> 2.1.2"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency "rspec", "~> 2.13.0"
26
+ spec.add_development_dependency "pry"
27
+ spec.add_development_dependency "rake"
28
+ spec.add_development_dependency "coy"
29
+ spec.add_development_dependency "fakefs", "~> 0.5.0"
30
+ end
@@ -0,0 +1,10 @@
1
+ require "anjou/version"
2
+ require_relative "anjou/authorized_keys"
3
+ require_relative "anjou/ec2"
4
+ require_relative "anjou/instance_user_data"
5
+ require_relative "anjou/launch_instance"
6
+ require_relative "anjou/user_home"
7
+
8
+ module Anjou
9
+ # Your code goes here...
10
+ end
@@ -0,0 +1,25 @@
1
+ require 'net/http'
2
+ require 'json'
3
+
4
+ module Anjou
5
+ class AuthorizedKeys
6
+ attr_reader :keys
7
+
8
+ def initialize(github_username)
9
+ uri = URI.parse("https://api.github.com/users/#{github_username}/keys")
10
+ http = Net::HTTP.new(uri.host, uri.port)
11
+ http.use_ssl = true
12
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
13
+ data = http.get(uri.request_uri)
14
+ @keys = JSON.parse data.body
15
+ raise "Response from GitHub: #{@keys['message']}" if @keys.kind_of? Hash
16
+ end
17
+
18
+ def contents
19
+ @keys.map{|item| item['key']}.join("\n")
20
+ end
21
+
22
+ end
23
+ end
24
+
25
+
@@ -0,0 +1,146 @@
1
+ require 'aws-sdk'
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'credentials', 'anjou'))
3
+
4
+ module Anjou
5
+ class EC2
6
+ attr_reader :api
7
+
8
+ KEY_ID = Anjou::AWS::ACCESS_KEY_ID
9
+ SECRET_KEY = Anjou::AWS::SECRET_ACCESS_KEY
10
+ KEY_PAIR_NAME = Anjou::AWS::KEY_PAIR_NAME
11
+ USER_VOL_SIZE = 1 #Gb
12
+ DEFAULT_ZONE = "us-east-1d"
13
+ DEFAULT_AMI = 'ami-ad184ac4' # Ubuntu Server 13.10 64bit
14
+ DEFAULT_INSTANCE_TYPE = 't1.micro'
15
+ DEFAULT_USER_DATA = "#!/bin/sh\n\necho \"Welcome to Anjou!\n\n\" >> /etc/motd\n"
16
+ DEFAULT_SNAPSHOT_NAME = 'anjou-generic'
17
+ SECURITY_GROUP_NAME = 'Anjou'
18
+
19
+ def initialize(access_key_id=KEY_ID, secret_access_key=SECRET_KEY)
20
+ @api = ::AWS::EC2.new(
21
+ access_key_id: access_key_id,
22
+ secret_access_key: secret_access_key )
23
+ end
24
+
25
+ def create_instance(
26
+ username: nil,
27
+ ami: DEFAULT_AMI,
28
+ key_name: KEY_PAIR_NAME,
29
+ zone: DEFAULT_ZONE,
30
+ instance_type: DEFAULT_INSTANCE_TYPE,
31
+ user_data: DEFAULT_USER_DATA
32
+ )
33
+ key_pair = key_pair_for key_name
34
+ raise "Unable to start instance: no such key pair exists! (key_name: #{key_name})" unless key_pair && key_pair.exists?
35
+ @api.instances.create(
36
+ image_id: ami,
37
+ key_pair: key_pair,
38
+ availability_zone: zone,
39
+ instance_type: instance_type,
40
+ user_data: user_data,
41
+ security_groups: security_groups
42
+ ).tap do |instance|
43
+ if username
44
+ instance.tags.Name = name_tag_for username
45
+ instance.tags.owner = username
46
+ end
47
+ end
48
+ end
49
+
50
+ def instance_for(username)
51
+ @api.instances.tagged('owner').select do |instance|
52
+ instance.tags.to_a.include? ['owner', username]
53
+ end.select{|i| i.status != :terminated }.last
54
+ end
55
+
56
+ def instance_status_for(username)
57
+ instance_for(username).status
58
+ end
59
+
60
+ def key_pair_for(key_name)
61
+ @api.key_pairs.select{ |kp| kp.name == key_name }.last
62
+ end
63
+
64
+ def create_user_volume(username: nil, user_vol_size: USER_VOL_SIZE, zone: DEFAULT_ZONE, snapshot: DEFAULT_SNAPSHOT_NAME)
65
+
66
+ aws_snapshot = @api.snapshots.tagged('Name').select do |snp|
67
+ snp.tags.to_a.include? ['Name', snapshot]
68
+ end.last
69
+
70
+ @api.volumes.create(
71
+ size: user_vol_size,
72
+ availability_zone: zone,
73
+ snapshot: aws_snapshot
74
+ ).tap do |volume|
75
+ if username
76
+ volume.tags.Name = name_tag_for username
77
+ volume.tags.owner = username
78
+ end
79
+ end
80
+ end
81
+
82
+ def delete_user_volume(username)
83
+ detach(username).delete
84
+ end
85
+
86
+ def user_volume_for(username)
87
+ @api.volumes.tagged('owner').select do |vol|
88
+ vol.tags.to_a.include? ['owner', username]
89
+ end.first
90
+ end
91
+
92
+ def user_volume_status_for(username)
93
+ user_volume_for(username).status
94
+ end
95
+
96
+ #@depricated: this method violates Anjou::EC2's "thin wrapper" contract.
97
+ # It's a useful method, but it should be implemented elsewhere.
98
+ def wait_for_user_volume(username, status: :available, timeout: 15)
99
+ seconds = 0
100
+ while user_volume_status_for(username) != status
101
+ raise "Timeout: After #{timeout} seconds, user volume for #{username} is still not #{status}" if seconds >= timeout
102
+ seconds += sleep 1
103
+ end
104
+ status
105
+ end
106
+
107
+ def attach(username, instance, device=nil)
108
+ volume = user_volume_for(username)
109
+ guard_volume_available(volume, username)
110
+ device ||= next_device_for(instance)
111
+ volume.attach_to instance, device
112
+ end
113
+
114
+ def detach(username)
115
+ user_volume_for(username).tap do |volume|
116
+ volume.attachments.each do |attachment|
117
+ attachment.delete(force: true)
118
+ end
119
+ sleep 1 until volume.status == :available
120
+ end
121
+ end
122
+
123
+ def next_device_for(instance)
124
+ '/dev/sda' + (instance.attachments.keys.size + 1).to_s
125
+ end
126
+
127
+ def security_groups
128
+ @api.security_groups.to_a.select{ |sg| sg.name == 'Anjou' }
129
+ end
130
+
131
+ private
132
+
133
+ def name_tag_for(username)
134
+ "anjou-#{username || :generic}"
135
+ end
136
+
137
+ def guard_volume_available(volume, username)
138
+ volume.status.tap do |status|
139
+ unless status == :available
140
+ raise "user volume for \"#{username}\" is not available (status: #{status})"
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+
@@ -0,0 +1,76 @@
1
+ module Anjou
2
+ class InstanceUserData
3
+ PREPENDS = 'scripts/first-boot/prepend'
4
+ OPTIONALS = 'scripts/first-boot/optional'
5
+ APPENDS = 'scripts/first-boot/append'
6
+
7
+ def self.render_mime(*scripts)
8
+ self.new(scripts.flatten).render_mime
9
+ end
10
+
11
+ def initialize(scripts=[])
12
+ @scripts = scripts
13
+ @script_sequence = 0
14
+ end
15
+
16
+ def render_mime
17
+ generate_mime
18
+ mime_sections.join("\n")
19
+ end
20
+
21
+ def to_s
22
+ render_mime
23
+ end
24
+
25
+ private
26
+
27
+ def generate_mime
28
+ add_to PREPENDS, [ 'install-ubuntu-updates', 'install-git' ]
29
+ add_to OPTIONALS, @scripts
30
+ add_to APPENDS, [ 'install-motd' ]
31
+ end
32
+
33
+ def add_to(path, scripts)
34
+ scripts.each do |script|
35
+ filename = "#{script}.sh"
36
+ filepath = "#{path}/#{filename}"
37
+ mime_sections << file_part(filename, File.read(filepath))
38
+ end
39
+ end
40
+
41
+ def boundary
42
+ @boundary ||= "~~Anjou::UserData~~"
43
+ end
44
+
45
+ def mime_sections
46
+ @mime_sections ||= [ message_header ]
47
+ end
48
+
49
+ def message_header
50
+ <<-HEADER
51
+ Content-Type: multipart/mixed; boundary=#{boundary}
52
+ MIME-Version: 1.0
53
+
54
+ HEADER
55
+ end
56
+
57
+ def file_part(filename, file_contents)
58
+ [ part_header(filename), file_contents ].join
59
+ end
60
+
61
+ def file_sequence
62
+ "%03d" % ( @script_sequence += 1 )
63
+ end
64
+
65
+ def part_header(filename, content_type: 'text/x-shellscript')
66
+ <<-PART
67
+ --#{boundary}
68
+ Content-Type: #{content_type}
69
+ MIME-Version: 1.0
70
+ Content-Disposition: attachment; filename="#{file_sequence}-#{filename}"
71
+
72
+ PART
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,84 @@
1
+ require 'anjou'
2
+
3
+ module Anjou
4
+ class LaunchInstance
5
+ LAUNCH_TIMEOUT_SECONDS = 60 * 5
6
+
7
+ class << self
8
+
9
+ def create_and_launch_instance(users=[], host_user: nil)
10
+ host_user ||= users.first
11
+ users << host_user unless users.include? host_user
12
+
13
+ guard_enough users
14
+
15
+ ensure_volumes_for users
16
+
17
+ ensure_authorized_keys_for users
18
+
19
+ user_data = Anjou::InstanceUserData.render_mime 'install-ruby', 'install-mosh'
20
+
21
+ create_instance_for(host_user, user_data).tap do |instance|
22
+ attach users, instance
23
+
24
+ users.each do |user|
25
+ home = Anjou::UserHome.new user, instance.dns_name
26
+ puts "Creating login for #{user}..."
27
+ home.create_linux_user
28
+ puts "Mounting home directory for #{user}..."
29
+ home.mount_home_dir
30
+ puts "Installing authorized_keys for #{user}..."
31
+ home.install_authorized_keys @authorized_keys[user]
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ private
38
+
39
+ def ec2
40
+ @@ec2 ||= Anjou::EC2.new
41
+ end
42
+
43
+ def guard_enough users
44
+ raise "You must provide at least one user for this pairing workstation!" unless users.size > 0
45
+ end
46
+
47
+ def create_instance_for host_user, user_data
48
+ puts "Creating Anjou instance for host user #{host_user}..."
49
+ instance = ec2.create_instance username: host_user, user_data: user_data
50
+
51
+ timeout_seconds = LAUNCH_TIMEOUT_SECONDS
52
+ until instance.status == :running do
53
+ raise "instance failed to start after #{LAUNCH_TIMEOUT_SECONDS}" if timeout_seconds <= 0
54
+ sleep 1; timeout_seconds -= 1
55
+ end
56
+ puts "Anjou instance started at #{instance.dns_name} :)"
57
+ instance
58
+ end
59
+
60
+ def ensure_volumes_for users
61
+ users.each do |user|
62
+ unless ec2.user_volume_for(user)
63
+ puts "Creating user volume for #{user}"
64
+ ec2.create_user_volume(username: user)
65
+ end
66
+ end
67
+ end
68
+
69
+ def ensure_authorized_keys_for users
70
+ @authorized_keys = {}
71
+ users.each do |user|
72
+ @authorized_keys[user] = Anjou::AuthorizedKeys.new(user)
73
+ end
74
+ end
75
+
76
+ def attach users, instance
77
+ users.each do |user|
78
+ puts "Attaching user volume for #{user} to #{instance.dns_name}"
79
+ ec2.attach user, instance
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,75 @@
1
+ require 'net/sftp'
2
+
3
+ module Anjou
4
+ class UserHome
5
+ ANJOU_LOGIN = 'ubuntu'
6
+ SSH_READY_TIMEOUT = 300
7
+ SSH_READY_SLEEP_INCREMENT = 2
8
+
9
+ def initialize(username, hostname)
10
+ @username = username
11
+ @hostname = hostname
12
+ end
13
+
14
+ def create_linux_user
15
+ ssh_do "sudo adduser --disabled-password --gecos '#{@username}' #{@username}"
16
+ ssh_do "echo '#{@username} ALL=(ALL) NOPASSWD:ALL' > /tmp/sudoize-#{@username}"
17
+ ssh_do "sudo chown root:root /tmp/sudoize-#{@username}"
18
+ ssh_do "sudo chmod 440 /tmp/sudoize-#{@username}"
19
+ ssh_do "sudo mv /tmp/sudoize-#{@username} /etc/sudoers.d/"
20
+ end
21
+
22
+ def mount_home_dir
23
+ volume = api.user_volume_for @username
24
+ device = volume.attachments.to_a.first.device.gsub(/sda/, "xvda")
25
+ ssh_do "sudo mount #{device} /home/#{@username}"
26
+ end
27
+
28
+ def install_authorized_keys(authorized_keys=Anjou::AuthorizedKeys.new(@username))
29
+ if ssh_do("sudo ls /home/#@username/.ssh").include? 'authorized_keys'
30
+ puts " ...on second thought, skipping this since #@username already has one..."
31
+ else
32
+ Net::SSH.start(@hostname, ANJOU_LOGIN) do |ssh|
33
+ ssh.sftp.connect do |sftp|
34
+ sftp.file.open("#{@username}-authorized_keys", 'w') do |fh|
35
+ fh.write authorized_keys.contents
36
+ end
37
+ end
38
+ end
39
+ ssh_do "sudo mv ~#{ANJOU_LOGIN}/#@username-authorized_keys ~#@username/.ssh/authorized_keys"
40
+ ssh_do "sudo chmod 0600 ~#@username/.ssh/authorized_keys"
41
+ end
42
+ ssh_do "sudo chown -R #@username:#@username /home/#@username"
43
+ end
44
+
45
+ private
46
+
47
+ def api
48
+ @api ||= Anjou::EC2.new
49
+ end
50
+
51
+ def ssh_do(cmd)
52
+ ensure_ssh_ready
53
+ `#{ssh_cmd} "#{cmd}"`
54
+ end
55
+
56
+ def ensure_ssh_ready
57
+ timeout = 0
58
+ until ssh_ready? do
59
+ raise "SSH connection not ready after #{timeout} attempts." if timeout >= (SSH_READY_TIMEOUT / SSH_READY_SLEEP_INCREMENT)
60
+ puts " ...connection not ready, waiting a bit..."
61
+ sleep SSH_READY_SLEEP_INCREMENT
62
+ timeout += 1
63
+ end
64
+ end
65
+
66
+ def ssh_ready?
67
+ @ssh_ready ||= `#{ssh_cmd} ls /var/lib/cloud/instance/* 2>&1`.include? 'boot-finished'
68
+ end
69
+
70
+ def ssh_cmd
71
+ @ssh_cmd ||= "ssh -o 'StrictHostKeyChecking no' #{ANJOU_LOGIN}@#{@hostname}"
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,3 @@
1
+ module Anjou
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ useradd --disabled-password --gecos "$1" $1
@@ -0,0 +1,13 @@
1
+ #!/bin/sh
2
+
3
+ echo "
4
+
5
+ ## ##
6
+ # #
7
+ # Welcome To Anjou! #
8
+ # Facilitating pair programming since 2014. #
9
+ # #
10
+ ## ##
11
+
12
+ " >> /etc/motd
13
+
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ apt-get -q -y install emacs-nox
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+
3
+ apt-get -q -y install python-software-properties
4
+ add-apt-repository -y ppa:keithw/mosh
5
+ apt-get -q -y install mosh
@@ -0,0 +1,6 @@
1
+ #!/bin/sh
2
+
3
+ apt-get -q -y install \
4
+ ruby2.0 ruby2.0-dev build-essential \
5
+ libssl-dev zlib1g-dev ruby-switch
6
+
@@ -0,0 +1,13 @@
1
+ #!/bin/sh
2
+
3
+ apt-get -q -y install tmux
4
+ git clone git://github.com/zolrath/wemux.git /usr/local/share/wemux
5
+ ln -s /usr/local/share/wemux/wemux /usr/local/bin/wemux
6
+ cp /usr/local/share/wemux/wemux.conf.example /usr/local/etc/wemux.conf
7
+
8
+ # need a way to add participants as hosts...
9
+ sed -i 's/host_list=(change_this)/host_list=(joelhelbling ubuntu anjouhost)/g' /usr/local/etc/wemux.conf
10
+
11
+ wemux start
12
+ chmod 1777 /tmp/wemux-wemux
13
+
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ apt-get -q -y install git-core
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+
3
+ sudo locale-gen en_US.UTF-8
4
+
5
+ apt-get -q -y update
@@ -0,0 +1,16 @@
1
+
2
+ if [ "$1" == "" ]; then
3
+
4
+ echo "Usage: mount-user-volume.sh emmajean /dev/sda2"
5
+
6
+ else
7
+
8
+ USER=$1
9
+ # e.g. /dev/sda2
10
+ DEVICE=$( echo $2 | sed -r 's/sda/xvda/' )
11
+
12
+ mkdir /home/$USER
13
+ mount $DEVICE /home/$USER
14
+
15
+ fi
16
+
@@ -0,0 +1,22 @@
1
+
2
+ module InstallScriptHelpers
3
+
4
+ def make_scripts_dir
5
+ Dir.mkdir 'scripts'
6
+ Dir.mkdir 'scripts/first-boot'
7
+ Dir.mkdir 'scripts/first-boot/prepend'
8
+ Dir.mkdir 'scripts/first-boot/optional'
9
+ Dir.mkdir 'scripts/first-boot/append'
10
+ end
11
+
12
+ def make_install_script(package, subdir = :optional)
13
+ File.open("scripts/first-boot/#{subdir}/install-#{package}.sh", 'w') do |fh|
14
+ fh.write <<-SHELL
15
+ #!/bin/sh
16
+
17
+ apt-get -q -y install #{package}
18
+ SHELL
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,45 @@
1
+ require 'anjou/authorized_keys'
2
+
3
+ module Anjou
4
+ describe AuthorizedKeys do
5
+ let(:gh_user) { 'foogoo' }
6
+ subject { described_class.new(gh_user) }
7
+
8
+ let(:key1) { "ssh-rsa AABBCC==" }
9
+ let(:key2) { "ssh-dss AABBCCDD=" }
10
+ let(:key3) { "ssh-rsa ZZYYXX=" }
11
+ let(:gh_response) do
12
+ "[{\"id\":101,\"key\":\"#{key1}\"}," +
13
+ "{\"id\":102,\"key\":\"#{key2}\"}," +
14
+ "{\"id\":103,\"key\":\"#{key3}\"}]"
15
+ end
16
+ let(:expected_authorized_keys) do
17
+ [key1, key2, key3].join("\n")
18
+ end
19
+
20
+ let(:data) { double }
21
+ before do
22
+ Net::HTTP.any_instance.stub(:get).and_return(data)
23
+ data.stub(:body).and_return(gh_response)
24
+ end
25
+
26
+ its(:keys) { should have(3).items }
27
+ its(:contents) { should == expected_authorized_keys }
28
+
29
+ context 'when user is not on GitHub' do
30
+ subject { described_class }
31
+ let(:gh_response) do
32
+ <<-RESPONSE
33
+ {
34
+ "message": "Not Found",
35
+ "documentation_url": "http://developer.github.com/v3"
36
+ }
37
+ RESPONSE
38
+ end
39
+
40
+ it 'throws an error' do
41
+ expect { described_class.new gh_user }.to raise_error /Not Found/
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,35 @@
1
+ require 'anjou/instance_user_data'
2
+
3
+ module Anjou
4
+ describe InstanceUserData do
5
+ it { should respond_to :render_mime }
6
+
7
+ describe "#render_mime", :fakefs do
8
+ before do
9
+ make_scripts_dir
10
+ make_install_script 'ubuntu-updates', :prepend
11
+ make_install_script 'git', :prepend
12
+ make_install_script 'foo'
13
+ make_install_script 'bar'
14
+ make_install_script 'baz'
15
+ make_install_script 'motd', :append
16
+ end
17
+
18
+ context "for a subset of existing scripts" do
19
+ subject { described_class.new ["install-foo", "install-bar"] }
20
+ its(:render_mime) { should include("apt-get -q -y install foo") }
21
+ its(:render_mime) { should include("install bar") }
22
+ its(:render_mime) { should_not include("install baz") }
23
+ end
24
+
25
+ context "for a non-existant script" do
26
+ subject { described_class.new ["install-bop"] }
27
+ it "throws an error" do
28
+ expect{ subject.render_mime }.to raise_error
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ end # describe InstanceUserData
35
+ end
@@ -0,0 +1,11 @@
1
+ require 'fakefs/spec_helpers'
2
+ require File.join(File.dirname(__FILE__), 'install_script_helpers')
3
+
4
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
5
+
6
+ RSpec.configure do |cfg|
7
+ cfg.treat_symbols_as_metadata_keys_with_true_values = true
8
+ cfg.include FakeFS::SpecHelpers, fakefs: true
9
+ cfg.include InstallScriptHelpers, fakefs: true
10
+ end
11
+
metadata ADDED
@@ -0,0 +1,188 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: anjou
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Joel Helbling
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.32.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.32.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: net-sftp
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.1.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.1.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 2.13.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 2.13.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: coy
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: fakefs
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.5.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.5.0
125
+ description: Remote pair programming made super easy
126
+ email:
127
+ - joel@joelhelbling.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".gitignore"
133
+ - ".rspec"
134
+ - Gemfile
135
+ - LICENSE.txt
136
+ - README.md
137
+ - Rakefile
138
+ - anjou.gemspec
139
+ - lib/anjou.rb
140
+ - lib/anjou/authorized_keys.rb
141
+ - lib/anjou/ec2.rb
142
+ - lib/anjou/instance_user_data.rb
143
+ - lib/anjou/launch_instance.rb
144
+ - lib/anjou/user_home.rb
145
+ - lib/anjou/version.rb
146
+ - scripts/create-user.sh
147
+ - scripts/first-boot/append/install-motd.sh
148
+ - scripts/first-boot/optional/install-emacs.sh
149
+ - scripts/first-boot/optional/install-mosh.sh
150
+ - scripts/first-boot/optional/install-ruby.sh
151
+ - scripts/first-boot/optional/install-wemux.sh
152
+ - scripts/first-boot/prepend/install-git.sh
153
+ - scripts/first-boot/prepend/install-ubuntu-updates.sh
154
+ - scripts/mount-user-volume.sh
155
+ - spec/install_script_helpers.rb
156
+ - spec/lib/anjou/authorized_keys_spec.rb
157
+ - spec/lib/anjou/instance_user_data_spec.rb
158
+ - spec/spec_helper.rb
159
+ homepage: ''
160
+ licenses:
161
+ - MIT
162
+ metadata: {}
163
+ post_install_message:
164
+ rdoc_options: []
165
+ require_paths:
166
+ - lib
167
+ required_ruby_version: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ required_rubygems_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: '0'
177
+ requirements: []
178
+ rubyforge_project:
179
+ rubygems_version: 2.4.3
180
+ signing_key:
181
+ specification_version: 4
182
+ summary: Anjou brings you, your stuff, some other programmer and their stuff all together
183
+ on a pair programming machine.
184
+ test_files:
185
+ - spec/install_script_helpers.rb
186
+ - spec/lib/anjou/authorized_keys_spec.rb
187
+ - spec/lib/anjou/instance_user_data_spec.rb
188
+ - spec/spec_helper.rb