vagabund 0.0.20

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,123 @@
1
+ module Vagabund
2
+ module Settler
3
+ module Projects
4
+ class ProjectConfig
5
+ attr_reader :config, :source
6
+
7
+ # Bit of metaprogramming to define methods like builder, installer,
8
+ # before_build, after_install, etc.
9
+ %w(project bundler puller).each do |action|
10
+ %w(before after).each do |hook|
11
+ hook_action = (action == 'bundler') ? "#{hook}_bundle" : "#{hook}_#{action.gsub(/[eo]r$/, '')}"
12
+
13
+ # Defines before/after 'hook' methods for each action: before_build,
14
+ # before_pull, after_install, etc.
15
+ define_method hook_action.to_sym do |*args, &block|
16
+ if args.first.is_a?(String)
17
+ command = args.shift
18
+ opts = args.extract_options!
19
+
20
+ cmd_proc = Proc.new do |project, machine, channel|
21
+ cmd = "cd #{project.project_path}; #{command}"
22
+ execute cmd, {verbose: true}.merge(opts)
23
+ end
24
+
25
+ config.send "#{hook_action}=".to_sym, [] if config.send(hook_action.to_sym).nil?
26
+ config.send(hook_action.to_sym) << cmd_proc
27
+ end
28
+
29
+ if !args.empty? && (args.first.nil? || args.first.is_a?(Proc))
30
+ config.send "#{hook_action}=".to_sym, [] if config.send(hook_action.to_sym).nil?
31
+ config.send(hook_action.to_sym) << args.shift
32
+ end
33
+
34
+ if !block.nil? # block_given? doesn't work here
35
+ config.send "#{hook_action}=".to_sym, [] if config.send(hook_action.to_sym).nil?
36
+ config.send(hook_action.to_sym) << block
37
+ end
38
+
39
+ config.send "#{hook_action}".to_sym
40
+ end
41
+ end
42
+
43
+ if action == 'project'
44
+ alias_method :before, :before_project
45
+ alias_method :after, :after_project
46
+ next
47
+ end
48
+
49
+ # Defines custom action methods to override the built-in puller,
50
+ # extractor, builder, installer and cleaner.
51
+ define_method action.to_sym do |*args, &block|
52
+ if args.first.is_a?(String)
53
+ command = args.shift
54
+ opts = args.extract_options!
55
+
56
+ cmd_proc = Proc.new do |project, machine, channel|
57
+ cmd = "cd #{project.project_path}; #{command}"
58
+ execute cmd, {verbose: true}.merge(opts)
59
+ end
60
+
61
+ config.send "#{action}=".to_sym, cmd_proc
62
+ end
63
+
64
+ config.send "#{action}=".to_sym, args.shift if !args.empty? && (args.first.nil? || args.first.is_a?(Proc))
65
+ config.send "#{action}=".to_sym, block if !block.nil? # block_given? doesn't work here
66
+
67
+ config.send action.to_sym
68
+ end
69
+ alias_method "#{action}=".to_sym, action.to_sym
70
+ alias_method "#{(action == 'bundle') ? 'bundle' : action.gsub(/[eo]r$/, '')}_with".to_sym, action.to_sym
71
+ end
72
+
73
+ def projects_path
74
+ config.projects_path ||= '/vagrant'
75
+ end
76
+
77
+ def projects_path=(path)
78
+ config.projects_path = path
79
+ end
80
+
81
+ def project_path
82
+ config.project_path ||= File.join(config.projects_path, name)
83
+ end
84
+ alias_method :path, :project_path
85
+
86
+ def project_path=(path)
87
+ config.project_path = path
88
+ end
89
+ alias_method :path=, :project_path=
90
+
91
+ def configure(&block)
92
+ instance_eval &block if block_given?
93
+ end
94
+
95
+ def method_missing(meth, *args, &block)
96
+ config.send meth, *args, &block
97
+ end
98
+
99
+ def respond_to_missing?(meth, include_private=false)
100
+ config.respond_to? meth, include_private
101
+ end
102
+
103
+ protected
104
+
105
+ def initialize(*args, &block)
106
+ @config = OpenStruct.new(args.extract_options!)
107
+
108
+ configure &block if block_given?
109
+
110
+ if config.respond_to?(:git)
111
+ @source = Sources::Git.new(config.git)
112
+ elsif config.respond_to?(:url)
113
+ @source = Sources::Url.new(config.url)
114
+ elsif config.respond_to?(:local)
115
+ @source = Sources::Local.new(config.local)
116
+ #elsif config.respond_to?(:scp)
117
+ # remote scp
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,8 @@
1
+ module Vagabund
2
+ module Settler
3
+ module Projects
4
+ class Rails < Ruby
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,38 @@
1
+ module Vagabund
2
+ module Settler
3
+ module Projects
4
+ class Ruby < Base
5
+
6
+ def provision(machine)
7
+ exec_before :project, machine
8
+ pull machine
9
+ bundle machine
10
+ exec_after :project, machine
11
+ end
12
+
13
+ def bundle(machine)
14
+ exec_before :bundle, machine
15
+ machine.ui.detail "Bundling #{self.class.name.split('::').last.downcase} project #{name}..."
16
+ if config.bundler.nil?
17
+ machine.communicate.execute "cd #{config.project_path}; bundle install" do |type,data|
18
+ color = type == :stderr ? :red : :green
19
+ options = {
20
+ color: color,
21
+ new_line: false,
22
+ prefix: false
23
+ }
24
+
25
+ machine.ui.detail(data, options)
26
+ end
27
+ else
28
+ action_exec config.bundler, machine
29
+ end
30
+ exec_after :bundle, machine
31
+ rescue StandardError => e
32
+ raise Settler::Errors::ProjectError, e
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,19 @@
1
+ require_relative 'errors'
2
+ require_relative 'projects/base'
3
+ require_relative 'projects/ruby'
4
+ require_relative 'projects/rails'
5
+
6
+ module Vagabund
7
+ module Settler
8
+ module Projects
9
+ end
10
+
11
+ class Project < Projects::Base
12
+ def self.new(*args, &block)
13
+ klass = (args.first.is_a?(Symbol) ? args.shift : :base).to_s.capitalize
14
+ eval("Projects::#{klass}.new *args, &block")
15
+ end
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,34 @@
1
+ require_relative 'packages'
2
+ require_relative 'projects'
3
+ require_relative 'sources'
4
+
5
+ module Vagabund
6
+ module Settler
7
+ class Provisioner < Vagrant.plugin(2, :provisioner)
8
+
9
+ def provision
10
+ config.packages.each do |package|
11
+ begin
12
+ machine.ui.info "Provisioning package #{package.name}-#{package.version}..."
13
+ package.provision @machine
14
+ rescue Vagrant::Errors::VagrantError => e
15
+ machine.ui.error "Failed to provision package #{package.name}-#{package.version}!"
16
+ machine.ui.error e.message(false), prefix: false
17
+ machine.ui.detail "#{e.message} in #{[e.backtrace[0..5], '...'].join($/)}", prefix: false
18
+ end
19
+ end
20
+
21
+ config.projects.each do |project|
22
+ begin
23
+ machine.ui.info "Provisioning project #{project.name}"
24
+ project.provision @machine
25
+ rescue Vagrant::Errors::VagrantError => e
26
+ machine.ui.error "Failed to provision project #{project.name}!"
27
+ machine.ui.detail "#{e.message} in #{[e.backtrace[0..5], '...'].join($/)}", prefix: false
28
+ end
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,59 @@
1
+ module Vagabund
2
+ module Settler
3
+ module Sources
4
+ class Git
5
+ attr_reader :origin
6
+
7
+ def clone(machine, target_path)
8
+ machine.ui.detail "Cloning #{origin} into #{target_path}..."
9
+ unless machine.communicate.test "[ -d #{File.dirname(target_path)} ]"
10
+ machine.communicate.sudo "mkdir -p #{File.dirname(target_path)}"
11
+ machine.communicate.sudo "chown -R #{machine.ssh_info[:username]} #{File.dirname(target_path)}"
12
+ machine.communicate.sudo "chgrp -R #{machine.ssh_info[:username]} #{File.dirname(target_path)}"
13
+ end
14
+ machine.communicate.execute "git clone #{origin} #{target_path}" do |type,data|
15
+ color = type == :stderr ? :red : :green
16
+ options = {
17
+ color: color,
18
+ new_line: false,
19
+ prefix: false
20
+ }
21
+
22
+ machine.ui.detail(data, options)
23
+ end
24
+ target_path
25
+ end
26
+
27
+ def update(machine, target_path)
28
+ machine.ui.detail "Updating #{target_path} from #{origin}..."
29
+ machine.communicate.execute "cd #{target_path}; git pull" do |type,data|
30
+ color = type == :stderr ? :red : :green
31
+ options = {
32
+ color: color,
33
+ new_line: false,
34
+ prefix: false
35
+ }
36
+
37
+ machine.ui.detail(data, options)
38
+ end
39
+ target_path
40
+ end
41
+
42
+ def pull(machine, target_path)
43
+ if machine.communicate.test "[ -d #{target_path} ]"
44
+ update machine, target_path
45
+ else
46
+ clone machine, target_path
47
+ end
48
+ end
49
+
50
+ protected
51
+
52
+ def initialize(origin)
53
+ @origin = origin
54
+ end
55
+
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,28 @@
1
+ module Vagabund
2
+ module Settler
3
+ module Sources
4
+ class Local
5
+ attr_reader :origin
6
+
7
+ def upload(machine, target_path)
8
+ machine.ui.detail "Uploading #{origin} to #{target_path}..."
9
+ unless machine.communicate.test "[ -d #{File.dirname(target_path)} ]"
10
+ machine.communicate.sudo "mkdir -p #{File.dirname(target_path)}"
11
+ machine.communicate.sudo "chown -R #{machine.ssh_info[:username]} #{File.dirname(target_path)}"
12
+ machine.communicate.sudo "chgrp -R #{machine.ssh_info[:username]} #{File.dirname(target_path)}"
13
+ end
14
+ machine.communicate.upload origin, target_path
15
+ target_path
16
+ end
17
+ alias_method :pull, :upload
18
+
19
+ protected
20
+
21
+ def initialize(origin)
22
+ @origin = File.expand_path(origin)
23
+ end
24
+
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,37 @@
1
+ module Vagabund
2
+ module Settler
3
+ module Sources
4
+ class Url
5
+ attr_reader :origin
6
+
7
+ def download(machine, target_path)
8
+ machine.ui.detail "Downloading #{origin} to #{target_path}..."
9
+ unless machine.communicate.test "[ -d #{File.dirname(target_path)} ]"
10
+ machine.communicate.sudo "mkdir -p #{File.dirname(target_path)}"
11
+ machine.communicate.sudo "chown -R #{machine.ssh_info[:username]} #{File.dirname(target_path)}"
12
+ machine.communicate.sudo "chgrp -R #{machine.ssh_info[:username]} #{File.dirname(target_path)}"
13
+ end
14
+ machine.communicate.execute "curl -L -o #{target_path} #{origin}" do |type,data|
15
+ color = type == :stderr ? :red : :green
16
+ options = {
17
+ color: color,
18
+ new_line: false,
19
+ prefix: false
20
+ }
21
+
22
+ machine.ui.detail(data, options)
23
+ end
24
+ target_path
25
+ end
26
+ alias_method :pull, :download
27
+
28
+ protected
29
+
30
+ def initialize(origin)
31
+ @origin = origin
32
+ end
33
+
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,10 @@
1
+ module Vagabund
2
+ module Settler
3
+ module Sources
4
+ end
5
+ end
6
+ end
7
+
8
+ require_relative 'sources/git'
9
+ require_relative 'sources/url'
10
+ require_relative 'sources/local'
@@ -0,0 +1,4 @@
1
+ module Vagabund
2
+ module Settler
3
+ end
4
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'user'
2
+
3
+ module Vagabund
4
+ module Squatter
5
+ class Config < Vagrant.plugin(2, :config)
6
+ attr_accessor :guest_home, :host_home
7
+
8
+ DEFAULT_FILES = ['.vimrc', '.viminfo', '.gitconfig', '.ssh/known_hosts']
9
+
10
+ def files
11
+ @files ||= DEFAULT_FILES
12
+ end
13
+
14
+ def files=(file_arr)
15
+ @files = file_arr
16
+ end
17
+
18
+ def file=(filename)
19
+ files << filename
20
+ end
21
+
22
+ def host_home
23
+ @host_home ||= File.expand_path('~')
24
+ end
25
+
26
+ def guest_home
27
+ @guest_home ||= '~'
28
+ end
29
+
30
+ def user
31
+ @user ||= User.new
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,199 @@
1
+ module Vagabund
2
+ module Squatter
3
+ class Provisioner < Vagrant.plugin(2, :provisioner)
4
+
5
+ def configure(root_config)
6
+ @root_config = root_config
7
+ end
8
+
9
+ def provision
10
+ create_user
11
+ upload_files
12
+ end
13
+
14
+ def cleanup
15
+ # remove the user?
16
+ super
17
+ end
18
+
19
+ def create_user
20
+ if config.user.create?
21
+ if @machine.communicate.test "[ `getent passwd | grep -c '^#{config.user.username}:'` == 0 ]"
22
+ @machine.ui.info "Creating user #{config.user.username}..."
23
+ @machine.communicate.sudo config.user.to_s
24
+
25
+ # Copy over the authorized_keys and known_hosts files being used currently for compatibility
26
+ if !@machine.communicate.test "[ -d #{config.user.home}/.ssh ]"
27
+ ssh_user_home = ''
28
+ @machine.communicate.execute "echo $HOME" do |type,data|
29
+ ssh_user_home = data.chomp if type == :stdout
30
+ end
31
+
32
+ @machine.communicate.sudo "mkdir -p #{config.user.home}/.ssh"
33
+
34
+ if config.user.pubkeys.nil?
35
+ # Copy the authorized_keys file if it doesn't exist and no public keys were provided
36
+ if !@machine.communicate.test("[ -f #{config.user.home}/.ssh/authorized_keys ]") && @machine.communicate.test("[ -f #{ssh_user_home}/.ssh/authorized_keys ]")
37
+ @machine.communicate.sudo "cp #{ssh_user_home}/.ssh/authorized_keys #{config.user.home}/.ssh/authorized_keys"
38
+ end
39
+ else
40
+ # Add the public key(s) provided
41
+ @machine.communicate.sudo "echo \"#{config.user.pubkeys}\" > #{config.user.home}/.ssh/authorized_keys"
42
+ end
43
+
44
+ unless config.user.ssh_conf_str.nil?
45
+ @machine.communicate.sudo "echo \"#{config.user.ssh_conf_str}\" > #{config.user.home}/.ssh/config", verbose: true
46
+ end
47
+
48
+ if !@machine.communicate.test("[ -f #{config.user.home}/.ssh/known_hosts ]") && @machine.communicate.test("[ -f #{ssh_user_home}/.ssh/known_hosts ]")
49
+ @machine.communicate.sudo "cp #{ssh_user_home}/.ssh/known_hosts #{config.user.home}/.ssh/known_hosts"
50
+ end
51
+
52
+ @machine.communicate.sudo "chown -R #{config.user.username} #{config.user.home}/.ssh"
53
+ @machine.communicate.sudo "chgrp -R #{config.user.username} #{config.user.home}/.ssh"
54
+ end
55
+
56
+ # Add to sudoers
57
+ if config.user.sudo
58
+ @machine.communicate.sudo "echo \"#{config.user.username} ALL=(ALL) NOPASSWD:ALL\" > /etc/sudoers.d/#{config.user.username}"
59
+ @machine.communicate.sudo "chmod 0440 /etc/sudoers.d/#{config.user.username}"
60
+ end
61
+ else
62
+ @machine.ui.warn "User #{config.user.username} already exists"
63
+ end
64
+
65
+ end
66
+ rescue
67
+ @machine.ui.error "Failed to create user #{config.user.username}"
68
+ @machine.communicate.sudo "userdel -r #{config.user.username}" rescue nil
69
+ @machine.communicate.sudo "rm -rf /etc/sudoers.d/#{config.user.username}"
70
+ end
71
+
72
+ def upload_files
73
+ config.files.each do |file|
74
+ sync *expanded_paths(file)
75
+ end
76
+ end
77
+
78
+ protected
79
+
80
+ def guest_home
81
+ gh_path = config.guest_home
82
+ @machine.communicate.execute "cd #{gh_path}; pwd" do |type, data|
83
+ gh_path = data.chomp if type == :stdout
84
+ end
85
+ gh_path
86
+ end
87
+
88
+ def host_home
89
+ File.expand_path(config.host_home)
90
+ end
91
+
92
+ def expanded_paths(file)
93
+ # separate source and destination, both expanded relative to home if not absolute
94
+ if file.is_a?(Array)
95
+ from = expanded_paths(file[0])[0]
96
+ to = expanded_paths(file[1])[1]
97
+
98
+ # proc should return [from, to]
99
+ elsif file.is_a?(Proc)
100
+ from, to = expanded_paths(clean_room.instance_exec(@machine, @machine.communicate, &file))
101
+
102
+ # remote source file
103
+ elsif file.match(/^(http[s]?|s3):\/\//i)
104
+ from = file
105
+ to = File.join(guest_home, File.basename(file))
106
+
107
+ # already absolute
108
+ elsif Pathname.new(file).absolute?
109
+ from = to = file
110
+
111
+ # expand path relative to home
112
+ else
113
+ from = File.join(host_home, file)
114
+ to = File.join(guest_home, file)
115
+ end
116
+
117
+ [from, to]
118
+ end
119
+
120
+ def sync(from, to)
121
+ if from.match(/^(http[s]?|s3):\/\//i)
122
+ @machine.ui.detail "Downloading #{from}..."
123
+ Dir.mktmpdir do |dir|
124
+ from_file = File.join(dir, File.basename(from))
125
+
126
+ if from.match(/^http[s]?:\/\//i)
127
+ `curl -L -o #{from_file} #{from} 2>&1`
128
+ elsif from.match(/^s3:\/\//i)
129
+ `aws s3 cp #{from} #{from_file}`
130
+ end
131
+
132
+ upload from_file, to
133
+ end
134
+ else
135
+ unless File.exists?(from)
136
+ @machine.ui.warn "Local file #{from} does not exist. Skipping."
137
+ return
138
+ end
139
+ upload from, to
140
+ end
141
+ end
142
+
143
+ def upload(from, to)
144
+ begin
145
+ @machine.ui.detail "Uploading #{from} to #{to}..."
146
+ @machine.communicate.execute "mkdir -p #{File.dirname(to)}" # TODO this should be guest OS agnostic
147
+ @machine.communicate.upload from, to
148
+ rescue Vagrant::Errors::VagrantError => e
149
+ @machine.ui.error "Failed to upload config file #{from} to #{to}"
150
+ raise e
151
+ end
152
+ end
153
+
154
+ def clean_room
155
+ dsl = Struct.new(:machine).new(@machine)
156
+ dsl.class.instance_eval do
157
+ [:ask, :detail, :error, :info, :output, :warn].each do |cmd|
158
+ define_method cmd do |*args, &block|
159
+ machine.ui.send cmd, *args, &block
160
+ end
161
+ end
162
+
163
+ [:execute, :sudo, :test].each do |cmd|
164
+ define_method cmd do |*args, &block|
165
+ opts = {verbose: false}.merge(args.extract_options!)
166
+ if opts[:verbose] == true
167
+ machine.communicate.send cmd, *args, opts do |type,data|
168
+ color = type == :stderr ? :red : :green
169
+ options = {
170
+ color: color,
171
+ new_line: false,
172
+ prefix: false
173
+ }
174
+
175
+ detail(data, options)
176
+ block.call(type, data) unless block.nil?
177
+ end
178
+ else
179
+ machine.communicate.send cmd, *args, opts, &block
180
+ end
181
+ end
182
+ end
183
+
184
+ define_method :capture do |*args, &block|
185
+ output = ''
186
+ machine.communicate.execute *args do |type,data|
187
+ output += data if type == :stdout
188
+ block.call(type, data) unless block.nil?
189
+ end
190
+ output
191
+ end
192
+ end
193
+
194
+ dsl
195
+ end
196
+
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,56 @@
1
+ module Vagabund
2
+ module Squatter
3
+ class User
4
+ attr_accessor :username, :password, :home, :shell, :group, :groups, :sudo, :public_key, :ssh_config
5
+
6
+ def home
7
+ @home || "/home/#{username}"
8
+ end
9
+
10
+ def shell
11
+ @shell ||= "/bin/bash"
12
+ end
13
+
14
+ def pubkeys
15
+ unless @public_key.nil?
16
+ [@public_key].flatten.map do |pubkey|
17
+ begin
18
+ File.read(File.expand_path(pubkey)).chomp
19
+ rescue Exception => e
20
+ pubkey
21
+ end
22
+ end.join($/)
23
+ end
24
+ end
25
+
26
+ def ssh_conf_str
27
+ unless @ssh_config.nil?
28
+ begin
29
+ File.read(File.expand_path(@ssh_config)).chomp
30
+ rescue Exception => e
31
+ @ssh_config
32
+ end
33
+ end
34
+ end
35
+
36
+ def to_s
37
+ cmd_str = "useradd -m -s #{shell}"
38
+ cmd_str += " -d #{home}"
39
+ cmd_str += " -g #{group}" unless group.nil?
40
+ cmd_str += " -G #{[groups].flatten.join(',')}" unless groups.nil?
41
+ cmd_str += " #{username}"
42
+ end
43
+
44
+ def create?
45
+ !username.nil?
46
+ end
47
+
48
+ protected
49
+
50
+ def initialize
51
+ @sudo = true
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,4 @@
1
+ module Vagabund
2
+ module Squatter
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ module Vagabund
2
+ VERSION = "0.0.20"
3
+ end