vagabund 0.0.20

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.
@@ -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