panoramix 0.5.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,47 @@
1
+ require "panoramix/locale"
2
+
3
+ module Panoramix
4
+ module Plugin
5
+
6
+ class ValidationError < StandardError; end
7
+
8
+ class Base
9
+
10
+ def initialize host
11
+ @host = host
12
+ end
13
+
14
+ # Return current timestamp
15
+ def timestamp
16
+ raise "Not implemented"
17
+ end
18
+
19
+ # When this instance needs to be executed
20
+ def needed? timestamps
21
+ raise "Not implemented"
22
+ end
23
+
24
+ # Has this instance already been created
25
+ def created?
26
+ raise "Not implemented"
27
+ end
28
+
29
+ # Default action for this task
30
+ def run_default
31
+ raise "Not implemented"
32
+ end
33
+
34
+ def shell(cmd, silent=false, env=Hash.new)
35
+ # Get a connection for the current host
36
+ connection = Panoramix.connection(@host)
37
+ # Run the shell command
38
+ connection.shell(cmd, silent, env)
39
+ end
40
+
41
+ def shell_ready?
42
+ Panoramix.connection(@host).ready?
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,83 @@
1
+ require "panoramix/plugin/docker_image_base"
2
+
3
+ module Panoramix
4
+ module Plugin
5
+
6
+ class DockerBuild < DockerImageBase
7
+
8
+ attr_reader :src
9
+
10
+ def initialize(dst, src, host)
11
+ super(dst, host)
12
+ @src = src
13
+ end
14
+
15
+ # When this instance needs to be executed
16
+ def needed? timestamps
17
+ return false if ENV['NO_BUILD'].split(":").any? { |image| image == @dst }
18
+ this_time = timestamp
19
+ timestamps.any? { |t| t > this_time }
20
+ end
21
+
22
+ # Default action for this task
23
+ def run_default
24
+ shell "docker build -t #{dst} #{File.dirname(@src)}"
25
+ end
26
+
27
+ def ps
28
+ super ("Built image")
29
+ end
30
+
31
+ def validate
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
38
+
39
+ def docker_image(args, block=nil)
40
+
41
+ puts "Warning using obsolete docker_image
42
+ \t => #{"docker_image".bold} <image_name> => {dockerfile: <path_dockerfile>}.\n\tchange to:
43
+ \t => #{"docker_build".bold} <image_name> => {dockerfile: <path_dockerfile>}".red
44
+ docker_build(args, block)
45
+ end
46
+
47
+ def docker_build(args, &block)
48
+ # Get task name
49
+ name = args.keys[0]
50
+
51
+ # Get configuration hash
52
+ config = args[name].select { |dep| dep.is_a? Hash }.first
53
+
54
+ # Get dockerfile if given
55
+ dockerfile = config.fetch(:dockerfile, nil)
56
+
57
+ # Raise error if :dockerfile value is nil
58
+ unless dockerfile
59
+ message = I18n.t('errors.docker_build.no_dockerfile_provided')
60
+ raise message
61
+ end
62
+
63
+ # Select provided dependencies
64
+ prerequisites = args[name].select { |d| ! d.is_a? Hash }
65
+
66
+ # Merge prerequisites with the Dockerfile file_task
67
+ prerequisites = prerequisites.push(dockerfile) unless dockerfile.nil?
68
+
69
+ # Set host as a new prerequisite if needed
70
+ host_dep = Panoramix.resolve_host_dependencies
71
+ prerequisites = prerequisites.push(host_dep) unless host_dep.nil?
72
+ prerequisites.flatten!
73
+
74
+ # Get hostname for this scope
75
+ host = Panoramix.current_host_name
76
+
77
+ instance = Panoramix::Plugin::DockerBuild.new(name, dockerfile, host)
78
+
79
+ descriptions = I18n.t('docker_build')
80
+ descriptions = Hash.new if descriptions.class != Hash
81
+
82
+ Panoramix.define_tasks(name,descriptions, instance, prerequisites, block)
83
+ end
@@ -0,0 +1,67 @@
1
+ require "panoramix/plugin/docker_image_base"
2
+
3
+ module Panoramix
4
+ module Plugin
5
+
6
+ class DockerImage < DockerImageBase
7
+
8
+ attr_reader :src
9
+
10
+ def initialize(dst, src, host)
11
+ super(dst, host)
12
+ @src = src
13
+ end
14
+
15
+ # When this instance needs to be executed
16
+ def needed? timestamps
17
+ true
18
+ end
19
+
20
+ # Action clobber for this task
21
+ def clobber
22
+ super
23
+ info = created?
24
+ if info
25
+ id = info.split(" ")[2]
26
+ shell "docker rmi -f #{id}"
27
+ end
28
+ end
29
+
30
+ # Default action for this task
31
+ def run_default
32
+ shell "docker pull #{@src}" unless ENV["NO_PULL"]
33
+
34
+ # Tag image with required tag
35
+ shell "docker tag -f #{@src} #{@dst}"
36
+ end
37
+
38
+ def ps
39
+ super ("Pulled image")
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+ end
46
+
47
+
48
+ def external_docker_image(args, block)
49
+ name = args.keys[0]
50
+ src = args[name][:dockerimage] || args[name][:docker]
51
+
52
+ prerequisites = []
53
+
54
+ # Set host as a new prerequisite if needed
55
+ host_dep = Panoramix.resolve_host_dependencies
56
+ prerequisites = prerequisites.push(host_dep) unless host_dep.nil?
57
+
58
+ # Get hostname for this scope
59
+ host = Panoramix.current_host_name
60
+
61
+ instance = Panoramix::Plugin::DockerImage.new(name, src, host)
62
+
63
+ descriptions = I18n.t('docker_image')
64
+ descriptions = Hash.new if descriptions.class != Hash
65
+
66
+ Panoramix.define_tasks(name, descriptions, instance, prerequisites, block)
67
+ end
@@ -0,0 +1,61 @@
1
+ require "time"
2
+ require "panoramix/plugin/base"
3
+
4
+ module Panoramix
5
+ module Plugin
6
+
7
+ class DockerImageBase < Base
8
+
9
+ attr_reader :dst
10
+
11
+ def initialize(dst, host)
12
+ super host
13
+ @dst = dst
14
+ @tag = "latest"
15
+ end
16
+
17
+ # Return timestamp of the image
18
+ def timestamp
19
+ info = created?
20
+ if info
21
+ # Get image timestamp
22
+ id = info.split(" ")[2]
23
+ time = shell("docker inspect -f {{.Created}} #{id}", true)[:out]
24
+ return Time.parse(time)
25
+ else
26
+ return Time.at 0
27
+ end
28
+ end
29
+
30
+ # Has this image already been created
31
+ def created?
32
+ return @created if @created
33
+ return nil unless shell_ready?
34
+ info = shell("docker images | grep -w '^#{@dst}' | grep -w '#{@tag}'", true)[:out]
35
+ @created = info = info.empty? ? nil: info
36
+ end
37
+
38
+ # Action clobber for this task
39
+ def clobber
40
+ info = created?
41
+ if info
42
+ id = info.split(" ")[2]
43
+ shell "docker rmi -f #{id}"
44
+ end
45
+ end
46
+
47
+ def ps(message)
48
+ puts "#{message}: #{@dst}"
49
+ info = created?
50
+ if info
51
+ puts "Timestamp: #{timestamp}"
52
+ puts info
53
+ else
54
+ puts "Timestamp: Not created"
55
+ end
56
+ puts
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,178 @@
1
+ require "time"
2
+ require "panoramix/plugin/base"
3
+
4
+ module Panoramix
5
+ module Plugin
6
+
7
+ class DockerUpExceptionError < StandardError; end
8
+
9
+ class DockerUp < Base
10
+
11
+ attr_reader :dst
12
+ attr_reader :src
13
+ attr_reader :env
14
+
15
+ def initialize(dst, src, env, host)
16
+ super (host)
17
+ @dst = dst
18
+ @src = src
19
+ @env = env
20
+ end
21
+
22
+ # Return current timestamp for the container
23
+ def timestamp
24
+ info = created?
25
+ if info
26
+ id = info.split("\n").first
27
+ time = shell("docker inspect -f {{.Created}} #{id}", true, @env)[:out]
28
+ return Time.parse(time)
29
+ else
30
+ Time.at 0
31
+ end
32
+ end
33
+
34
+ # Has this image already been created
35
+ def created?
36
+ return nil unless shell_ready?
37
+ info = shell("docker-compose -p #{@dst} -f #{@src} ps -q", true, @env)[:out]
38
+ @created = info = info.empty? ? nil: info
39
+ end
40
+
41
+ # Action clobber for this task
42
+ def clobber
43
+ shell("docker-compose -p #{@dst} -f #{@src} kill; docker-compose -p #{@dst} -f #{@src} rm -v --force", false, @env) if created?
44
+ end
45
+
46
+ # When this instance needs to be executed
47
+ def needed? timestamps
48
+ this_time = timestamp
49
+ timestamps.any? { |t| t > this_time }
50
+ end
51
+
52
+ # Default action for this task
53
+ def run_default
54
+ @env = parse_env @env.keys
55
+ # Raise an exception if already has been created
56
+ if created?
57
+ message = I18n.t('errors.docker_up.cluster_deployed', {:id => @dst})
58
+ raise(DockerUpExceptionError, message)
59
+ end
60
+ # Run docker-compose up otherwise
61
+ shell("docker-compose -p #{dst} -f #{@src} up -d", false, @env)
62
+ end
63
+
64
+ def stop
65
+ if created?
66
+ shell("docker-compose -p #{dst} -f #{@src} stop", false, @env)
67
+ end
68
+ end
69
+
70
+ def start
71
+ if created?
72
+ shell("docker-compose -p #{dst} -f #{@src} up -d", false, @env)
73
+ end
74
+ end
75
+
76
+ # Print docker-compose project logs
77
+ def logs
78
+ info = created?
79
+ if info
80
+ containers = info.split("\n")
81
+ colors = ['red', 'green','brown','blue','magenta','cyan']
82
+ containers.each do |c|
83
+ out = shell("docker logs #{c} | tail -n 200", true, @env)
84
+ name = shell("docker inspect -f {{.Name}} #{c}", true, @env)[:out]
85
+ name.gsub!("\n", "")
86
+ name.gsub!("/", "")
87
+ name = eval %& "#{name} >".#{colors.first}&
88
+ colors.push colors.shift
89
+ out[:out].split("\n").each{ |out| puts name + out }
90
+ out[:err].split("\n").each{ |out| puts name + out }
91
+ end
92
+ end
93
+ end
94
+
95
+ # Print docker-compose ps
96
+ def ps
97
+ puts "Service: #{@dst}"
98
+ if created?
99
+ puts "Timestamp: #{timestamp}"
100
+ info = shell("docker-compose -p #{@dst} -f #{@src} ps", true, @env)[:out]
101
+ puts info
102
+ else
103
+ puts "Timestamp: Not created"
104
+ end
105
+ puts
106
+ end
107
+
108
+ def validate
109
+ keys_errors = ""
110
+ @env.each do |key, value|
111
+ if value.nil?
112
+ keys_errors << "#{key} " if Panoramix::Link[key].nil?
113
+ end
114
+ end
115
+
116
+ if keys_errors != ""
117
+ message = I18n.t('errors.docker_up.env_var_not_given', {:env => keys_errors})
118
+ raise(ValidationError, message)
119
+ end
120
+ end
121
+
122
+ end
123
+
124
+ end
125
+ end
126
+
127
+
128
+ def parse_env environment
129
+ res=Hash.new
130
+
131
+ #TODO: chech if some env variable is a link
132
+ environment.each do |var|
133
+ res[var]=ENV[var]
134
+ end
135
+
136
+ res
137
+ end
138
+
139
+ def docker_up(args, &block)
140
+ name = args.keys[0]
141
+
142
+ deps = args[name]
143
+
144
+ compose_params = deps.select { |d| d.is_a? Hash }
145
+
146
+ # Get docker-compose file path
147
+ compose = compose_params.first[:compose]
148
+
149
+ # Raise error if :compose value is nil
150
+ unless compose
151
+ message = I18n.t('errors.docker_up.no_compose_provided')
152
+ raise(Panoramix::Plugin::DockerUpExceptionError, message)
153
+ end
154
+
155
+ # Get flags
156
+ flags = {}
157
+
158
+ # Get environment
159
+ environment = parse_env(compose_params.first[:env] || [])
160
+
161
+ # Merge deps with the compose file_task
162
+ prerequisites = deps.select { |d| ! d.is_a? Hash }
163
+ prerequisites = prerequisites.push(compose)
164
+
165
+ # Set host as a new prerequisite if needed
166
+ host_dep = Panoramix.resolve_host_dependencies
167
+ prerequisites = prerequisites.push(host_dep) unless host_dep.nil?
168
+
169
+ # Get hostname for this scope
170
+ host = Panoramix.current_host_name
171
+
172
+ instance = Panoramix::Plugin::DockerUp.new(name, compose, environment, host)
173
+
174
+ descriptions = I18n.t('docker_up')
175
+ descriptions = Hash.new if descriptions.class != Hash
176
+
177
+ Panoramix.define_tasks(name, descriptions, instance, prerequisites, block)
178
+ end
@@ -0,0 +1,109 @@
1
+ require "time"
2
+ require "panoramix/plugin/base"
3
+
4
+ module Panoramix
5
+ module Plugin
6
+ class Git < Base
7
+
8
+ attr_reader :dst
9
+ attr_reader :src
10
+ attr_reader :tag
11
+
12
+ def initialize(dst, src, tag, host)
13
+ super host
14
+ # Get git project name
15
+ repo_name=src.split("/").last.gsub(".git", "")
16
+
17
+ # Base path under which project will be downloaded
18
+ base_path=dst.split(repo_name)[0..-1].join(repo_name)
19
+
20
+ @dst=File.join(base_path, repo_name)
21
+ @src = src
22
+ @tag = tag
23
+ end
24
+
25
+ # Return current timestamp
26
+ def timestamp
27
+ return Time.at 0 unless created?
28
+ git_time = shell("git -C #{@dst} log -1 --format=\"%cd\"", true)[:out]
29
+ #TODO check exit code
30
+ Time.parse(git_time)
31
+ end
32
+
33
+ # When this instance needs to be executed
34
+ def needed? timestamps
35
+ return true if !created?
36
+ # Get remote branches
37
+ remote = shell("git ls-remote #{src}", true)[:out].split("\n")
38
+
39
+ # Is @tag a branch?
40
+ is_branch = remote.select { |r| r.include?("refs/heads/#{tag}") }
41
+
42
+ if is_branch.any?
43
+ # Compare local refs with remote ref
44
+ remote_ref = is_branch.first.gsub /\t/, ' '
45
+ local_refs = shell("git -C #{@dst} show-ref", true)[:out].split("\n")
46
+
47
+ return !local_refs.include?(remote_ref)
48
+ end
49
+
50
+ #TODO: To be implemented for tags and commits
51
+
52
+ return true
53
+ end
54
+
55
+ # Has this instance already been created
56
+ def created?
57
+ File.directory?(@dst)
58
+ end
59
+
60
+ # Action clean fot this task
61
+ def clean
62
+ shell "rm -rf #{@dst}"
63
+ end
64
+
65
+ # Default action for this task
66
+ def run_default
67
+ if created?
68
+ shell "git -C #{@dst} pull"
69
+ # File does not exists, so clone the full repository
70
+ else
71
+ shell "git clone #{@src} #{@dst}"
72
+ end
73
+
74
+ # Switch to tag
75
+ shell "git -C #{@dst} checkout #{@tag}"
76
+ end
77
+
78
+ def validate
79
+ out = shell("git ls-remote #{@src} | grep \'#{@tag}$\' | wc -l", true)
80
+ if (! out[:exit_status].success? || out[:out] =~ /0/)
81
+ message = I18n.t('errors.git.not_found', {:branch => @src, :tag => @tag})
82
+ raise(ValidationError, message)
83
+ end
84
+ end
85
+
86
+ end
87
+ end
88
+ end
89
+
90
+ def git(args, prerequisites, block=nil)
91
+ # Name of the task
92
+ name = args.keys[0]
93
+ # Get :git value from arguments
94
+ src = args.values[0][:git]
95
+ # Get :tag value from arguments
96
+ tag = args.values[0][:tag]
97
+
98
+ # Git tasks always run as local
99
+ host = "local"
100
+
101
+ # Generate a new instance using the provided arguments
102
+ instance = Panoramix::Plugin::Git.new(name, src, tag, host)
103
+
104
+ descriptions = I18n.t('git')
105
+ descriptions = Hash.new if descriptions.class != Hash
106
+
107
+ # Define git instance tasks
108
+ Panoramix.define_tasks(name, descriptions, instance, prerequisites, block)
109
+ end
@@ -0,0 +1,95 @@
1
+ require "panoramix/plugin/base"
2
+
3
+ module Panoramix
4
+ module Plugin
5
+ class Provision < Base
6
+
7
+ attr_reader :host
8
+ attr_accessor :config
9
+
10
+ def initialize(host, config)
11
+ super host
12
+ @host = host
13
+ @config = config
14
+ end
15
+
16
+ # Return current timestamp
17
+ def timestamp
18
+ if created?
19
+ @host.timestamp
20
+ else
21
+ Time.at 0
22
+ end
23
+ end
24
+
25
+ # When this instance needs to be executed
26
+ def needed? timestamps
27
+ !created?
28
+ end
29
+
30
+ # Has this instance already been created
31
+ def created?
32
+ @host.deployed?
33
+ end
34
+
35
+ # Action clobber for this task
36
+ def clobber
37
+ terminate_instance
38
+ end
39
+
40
+ # Run a new instance
41
+ def run_instance args
42
+ @host.run_instance args
43
+ end
44
+
45
+ # Terminate instance
46
+ def terminate_instance args=nil
47
+ unless args
48
+ args=@config
49
+ end
50
+
51
+ @host.terminate_instance args
52
+ end
53
+
54
+ # Default action for this task
55
+ def run_default
56
+ raise "Machine already created" if created?
57
+ run_instance @config
58
+ end
59
+
60
+ # Get aws status
61
+ def ps
62
+ if created?
63
+ puts "Timestamp: #{timestamp}"
64
+ puts @host.status
65
+ else
66
+ puts "Timestamp: Not created"
67
+ end
68
+ puts
69
+ end
70
+
71
+
72
+ end
73
+ end
74
+ end
75
+
76
+
77
+ def provision(&block)
78
+
79
+ raise "Provision defined outside an scope" unless Rake.application.current_scope.any?
80
+
81
+ # Get host for this task
82
+ name = Rake.application.current_scope.first
83
+
84
+ # Get host for this task
85
+ host = Panoramix::Hosts[name][:instance]
86
+
87
+ instance = Panoramix::Plugin::Provision.new(host, host.machine.config)
88
+
89
+ descriptions = I18n.t('provision')
90
+ descriptions = Hash.new if descriptions.class != Hash
91
+
92
+ prerequisites = []
93
+
94
+ Panoramix.define_tasks(:host, descriptions, instance, prerequisites, block)
95
+ end