orca 0.1.0

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,100 @@
1
+ Orca.extension :file_sync do
2
+ class Orca::Package
3
+ def file(config)
4
+ Orca::FileSync.new(self, config).configure
5
+ end
6
+ end
7
+ end
8
+
9
+ class Orca::FileSync
10
+ def initialize(parent, config)
11
+ @parent = parent
12
+ @config = config
13
+ raise ArgumentError.new('A file :source must be provided') unless local_path
14
+ raise ArgumentError.new('A file :destination must be provided') unless remote_path
15
+ end
16
+
17
+ def local_path
18
+ @config[:source]
19
+ end
20
+
21
+ def remote_path
22
+ @config[:destination]
23
+ end
24
+
25
+ def permissions
26
+ @config[:permissions]
27
+ end
28
+
29
+ def user
30
+ @config[:user]
31
+ end
32
+
33
+ def group
34
+ @config[:group]
35
+ end
36
+
37
+ def create_dir
38
+ @config[:create_dir] || @config[:create_dirs]
39
+ end
40
+
41
+ def package_name(suffix)
42
+ "file-#{suffix}[#{remote_path}]"
43
+ end
44
+
45
+ def configure
46
+ fs = self
47
+ add_content_package
48
+ add_permissions_package unless permissions.nil? and user.nil? and group.nil?
49
+ end
50
+
51
+ def add_content_package
52
+ fs = self
53
+ add_package('content') do |package|
54
+ package.command :apply do
55
+ if fs.create_dir
56
+ mk_dir = fs.create_dir == true ? File.dirname(fs.remote_path) : fs.create_dir
57
+ sudo("mkdir -p #{mk_dir}")
58
+ sudo("chown #{fs.user}:#{fs.group || fs.user} #{mk_dir}") if fs.user
59
+ end
60
+ local_file = local(fs.local_path)
61
+ tmp_path = "orca-upload-#{local_file.hash}"
62
+ local_file.copy_to(remote(tmp_path))
63
+ sudo("mv #{tmp_path} #{fs.remote_path}")
64
+ end
65
+
66
+ package.command :remove do
67
+ remote(fs.remote_path).delete!
68
+ end
69
+
70
+ package.command :validate do
71
+ local(fs.local_path).matches?(remote(fs.remote_path))
72
+ end
73
+ end
74
+ end
75
+
76
+ def add_permissions_package
77
+ fs = self
78
+ add_package('permissions') do |package|
79
+ package.command :apply do
80
+ remote(fs.remote_path).set_owner(fs.user, fs.group) unless fs.user.nil? and fs.group.nil?
81
+ remote(fs.remote_path).set_permissions(fs.permissions) unless fs.permissions.nil?
82
+ end
83
+
84
+ package.command :validate do
85
+ r_file = remote(fs.remote_path)
86
+ valid = r_file.permissions == fs.permissions
87
+ valid = valid && r_file.user == fs.user if fs.user
88
+ valid = valid && r_file.group == fs.group if fs.group
89
+ valid
90
+ end
91
+ end
92
+ end
93
+
94
+ def add_package(suffix)
95
+ package = Orca.add_package(package_name(suffix))
96
+ yield(package)
97
+ @parent.triggers(package.name)
98
+ package
99
+ end
100
+ end
@@ -0,0 +1,77 @@
1
+ require 'digest/sha1'
2
+ require 'fileutils'
3
+
4
+ class Orca::LocalFile
5
+ attr_reader :path
6
+
7
+ def initialize(path)
8
+ @path = resolve(path)
9
+ end
10
+
11
+ def resolve(path)
12
+ if path =~ /^\//
13
+ path
14
+ else
15
+ File.join(Orca.root, path)
16
+ end
17
+ end
18
+
19
+ def hash
20
+ return nil unless exists?
21
+ Digest::SHA1.file(path).hexdigest
22
+ end
23
+
24
+ def exists?
25
+ File.exists?(@path)
26
+ end
27
+
28
+ # deosnt check permissions or user. should it?
29
+ def matches?(other)
30
+ self.exists? && other.exists? && self.hash == other.hash
31
+ end
32
+
33
+ def copy_to(destination)
34
+ if destination.is_local?
35
+ duplicate(destination)
36
+ else
37
+ upload(destination)
38
+ end
39
+ destination
40
+ end
41
+
42
+ def copy_from(destination)
43
+ destination.copy_to(self)
44
+ end
45
+
46
+ def duplicate(destination)
47
+ FileUtils.cp(path, destination.path)
48
+ destination
49
+ end
50
+
51
+ def upload(destination)
52
+ destination.upload(self)
53
+ end
54
+
55
+ def delete!
56
+ FileUtils.rm(path) if exists?
57
+ self
58
+ end
59
+
60
+ def set_permissions(mask)
61
+ FileUtils.chmod_R(mask, path)
62
+ self
63
+ end
64
+
65
+ def permissions
66
+ File.stat(path).mode & 0777
67
+ end
68
+
69
+ def set_owner(user, group=nil)
70
+ FileUtils.chown_R(user, group, path)
71
+ self
72
+ end
73
+
74
+ def is_local?
75
+ true
76
+ end
77
+ end
@@ -0,0 +1,83 @@
1
+ require 'net/ssh'
2
+ require 'net/sftp'
3
+
4
+ class Orca::Node
5
+ attr_reader :name, :host
6
+
7
+ def self.find(name)
8
+ return name if name.is_a?(Orca::Node)
9
+ @nodes[name]
10
+ end
11
+
12
+ def self.register(node)
13
+ @nodes ||= {}
14
+ @nodes[node.name] = node
15
+ end
16
+
17
+ def initialize(name, host, options={})
18
+ @name = name
19
+ @host = host
20
+ @options = options
21
+ @connection = nil
22
+ Orca::Node.register(self)
23
+ end
24
+
25
+ def upload(from, to)
26
+ log('sftp', "UPLOAD: #{from} => #{to}")
27
+ sftp.upload!(from, to)
28
+ end
29
+
30
+ def download(from, to)
31
+ log('sftp', "DOWLOAD: #{from} => #{to}")
32
+ sftp.download!(from, to)
33
+ end
34
+
35
+ def remove(path)
36
+ log('sftp', "REMOVE: #{path}")
37
+ sftp.remove!(path)
38
+ end
39
+
40
+ def stat(path)
41
+ log('sftp', "STAT: #{path}")
42
+ sftp.stat!(path)
43
+ end
44
+
45
+ def setstat(path, opts)
46
+ log('sftp', "SET: #{path} - #{opts.inspect}")
47
+ sftp.setstat!(path, opts)
48
+ end
49
+
50
+ def sftp
51
+ @sftp ||= connection.sftp.connect
52
+ end
53
+
54
+ def execute(cmd)
55
+ log('execute', cmd.cyan)
56
+ output = ""
57
+ connection.exec! cmd do |channel, stream, data|
58
+ output += data if stream == :stdout
59
+ data.split("\n").each do |line|
60
+ msg = stream == :stdout ? line.green : line.red
61
+ log(stream, msg)
62
+ end
63
+ end
64
+ output
65
+ end
66
+
67
+ def sudo(cmd)
68
+ execute("sudo #{cmd}")
69
+ end
70
+
71
+ def log(context, msg)
72
+ puts "#{self.to_s} [#{context.to_s.bold}] #{msg}"
73
+ end
74
+
75
+ def connection
76
+ return @connection if @connection
77
+ @connetion = Net::SSH.start(@host, (@options[:user] || 'root'), @options)
78
+ end
79
+
80
+ def to_s
81
+ "#{name}(#{host})"
82
+ end
83
+ end
@@ -0,0 +1,52 @@
1
+ class Orca::Package
2
+ attr_reader :name, :dependancies, :actions, :children
3
+
4
+ def initialize(name)
5
+ @name = name
6
+ @dependancies = []
7
+ @children = []
8
+ @actions = {}
9
+ @commands = {}
10
+ @remove = nil
11
+ end
12
+
13
+ def depends_on(*pkg_names)
14
+ pkg_names.each do |pkg_name|
15
+ @dependancies << pkg_name
16
+ end
17
+ end
18
+
19
+ def triggers(*pkg_names)
20
+ pkg_names.each do |pkg_name|
21
+ @children << pkg_name
22
+ end
23
+ end
24
+
25
+ def validate(&definition)
26
+ command(:validate, &definition)
27
+ end
28
+
29
+ def apply(&definition)
30
+ command(:apply, &definition)
31
+ end
32
+
33
+ def remove(&definition)
34
+ command(:remove, &definition)
35
+ end
36
+
37
+ def action(name, &definition)
38
+ @actions[name] = definition
39
+ end
40
+
41
+ def command(name, &definition)
42
+ if block_given?
43
+ (@commands[name.to_sym] ||= []) << definition
44
+ else
45
+ @commands[name.to_sym]
46
+ end
47
+ end
48
+
49
+ def provides_command?(name)
50
+ @commands.has_key?(name.to_sym)
51
+ end
52
+ end
@@ -0,0 +1,37 @@
1
+ class Orca::PackageIndex
2
+ attr_reader :index_name
3
+
4
+ def initialize(index_name)
5
+ @index_name = index_name
6
+ @packages = {}
7
+ end
8
+
9
+ def self.default
10
+ @default ||= new('default')
11
+ end
12
+
13
+ def add(pkg)
14
+ @packages[pkg.name] = pkg
15
+ end
16
+
17
+ def get(pkg_name)
18
+ pkg = @packages[pkg_name]
19
+ raise MissingPackageError.new(index_name, pkg_name) if pkg.nil?
20
+ pkg
21
+ end
22
+
23
+ def clear!
24
+ @packages = {}
25
+ end
26
+
27
+ class MissingPackageError < StandardError
28
+ def initialize(index_name, package_name)
29
+ @index_name = index_name
30
+ @package_name = package_name
31
+ end
32
+
33
+ def message
34
+ "package #{@package_name} could not be found in the index #{@index_name}"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,104 @@
1
+ require 'digest/sha1'
2
+ require 'fileutils'
3
+
4
+ class Orca::RemoteFile
5
+ attr_reader :path
6
+
7
+ def initialize(context, path)
8
+ @context = context
9
+ @path = path
10
+ @exists = nil
11
+ @permissions = nil
12
+ @owner = nil
13
+ end
14
+
15
+ def hash
16
+ return nil unless exists?
17
+ @hash ||= @context.run("sha1sum #{path}")[0...40]
18
+ end
19
+
20
+ def exists?
21
+ return @exists unless @exists.nil?
22
+ result = @context.run(%[if [ -f #{path} ]; then echo "true"; else echo "false"; fi])
23
+ @exists = result.strip == 'true'
24
+ end
25
+
26
+ # deosnt check permissions or user. should it?
27
+ def matches?(other)
28
+ self.exists? && other.exists? && self.hash == other.hash
29
+ end
30
+
31
+ def copy_to(destination)
32
+ if destination.is_local?
33
+ download(destination)
34
+ else
35
+ duplicate(destination)
36
+ end
37
+ destination
38
+ end
39
+
40
+ def copy_from(destination)
41
+ destination.copy_to(self)
42
+ end
43
+
44
+ def duplicate(destination)
45
+ @context.sudo("cp #{path} #{destination.path}")
46
+ destination
47
+ end
48
+
49
+ def download(destination)
50
+ @context.download(path, destination.path)
51
+ end
52
+
53
+ def upload(source)
54
+ @context.upload(source.path, path)
55
+ end
56
+
57
+ def delete!
58
+ @context.remove(path)
59
+ invalidate!
60
+ self
61
+ end
62
+
63
+ def set_permissions(mask)
64
+ @context.sudo("chmod -R #{sprintf("%o",mask)} #{path}")
65
+ invalidate!
66
+ self
67
+ end
68
+
69
+ def permissions
70
+ @permissions ||= @context.run("stat --format=%a #{path}").strip.to_i(8)
71
+ end
72
+
73
+ def set_owner(user, group=nil)
74
+ @context.sudo("chown -R #{user}:#{group} #{path}")
75
+ invalidate!
76
+ self
77
+ end
78
+
79
+ def owner
80
+ @owner ||= begin
81
+ user, group = @context.run("stat --format=%U:%G #{path}").chomp.split(":")
82
+ {:user => user, :group => group}
83
+ end
84
+ end
85
+
86
+ def user
87
+ owner[:user]
88
+ end
89
+
90
+ def group
91
+ owner[:group]
92
+ end
93
+
94
+ def is_local?
95
+ false
96
+ end
97
+
98
+ private
99
+
100
+ def invalidate!
101
+ @permissions = nil
102
+ @owner = nil
103
+ end
104
+ end