shuttl 0.3.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cdced374d119718cdd22b9d9886632a5eda06260
4
+ data.tar.gz: 621609539b377b2f2f78f8839089a136de80bd6c
5
+ SHA512:
6
+ metadata.gz: 8073a8497ab099c178a28bf200264e79c12f01483727da95fe89442048a98ce4736b1ef5b2a36ef8574b68dd587b057247982f72d0a37449c974489556636a43
7
+ data.tar.gz: 95d6d4e9a61d177012f7ce0e68f612b0db3ab4fe152ee5caa5ed278d20be32d774003d7e9e9b95fe2e474f8297534648fd50bafca870b03fb674a1c74966e1a9
data/bin/shuttl ADDED
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'optparse'
4
+ require 'colorize'
5
+ require_relative '../src/dsl/eval'
6
+ require_relative '../src/commands/start'
7
+ require_relative '../src/commands/stop'
8
+ require_relative '../src/commands/build'
9
+ require_relative '../src/commands/info'
10
+ require_relative '../src/commands/run'
11
+ require_relative '../src/commands/ssh'
12
+ require_relative '../src/commands/deploy'
13
+ require_relative '../src/commands/install'
14
+ require_relative '../src/settings'
15
+
16
+ Docker.validate_version!
17
+
18
+ VERSION = "#{ShuttlSettings::VERSION} build #{ShuttlSettings::BUILD}"
19
+
20
+ options = {
21
+ :fileName => './Shuttlfile'
22
+ }
23
+
24
+ globals = OptionParser.new do |opts|
25
+ opts.banner = "Usage: shuttl [command]"
26
+ end
27
+
28
+ subcommands = Hash[
29
+ "start" => {
30
+ :opts => OptionParser.new do |opts|
31
+ opts.banner = "Starts a new instance of the app"
32
+ opts.on('--file FILE', 'Defines the file to use as the build file') do |file|
33
+ options[:fileName] = file
34
+ end
35
+ options[:stage] = 'dev'
36
+ end,
37
+ :runner => Start.new
38
+ },
39
+ 'build' => {
40
+ :opts => OptionParser.new do |opts|
41
+ options[:stage] = 'dev'
42
+ options[:clean] = false
43
+ options[:env] = nil
44
+ opts.on('--file FILE', 'Defines the file to use as the build file') do |file|
45
+ options[:fileName] = file
46
+ end
47
+ opts.on('-s STAGE', '--stage STAGE', 'The build stage') do |stage|
48
+ options[:stage] = stage
49
+ end
50
+ opts.on('--clean', 'Preform a clean build (Don\'t use the cache)') do
51
+ options[:clean] = true
52
+ end
53
+ opts.on('--env FILE', 'defines the build arguments to use') do |file|
54
+ options[:env] = file
55
+ end
56
+ end,
57
+ :runner => Build.new
58
+ },
59
+ 'show' => {
60
+ :opts => OptionParser.new do |opts|
61
+ options[:stage] = 'dev'
62
+ options[:showIP] = false
63
+ options[:showDocker] = false
64
+ options[:container] = false
65
+ options[:status] = false
66
+ opts.on('--docker-file', 'Show the docker file') do |file|
67
+ options[:showDocker] = true
68
+ end
69
+ opts.on('--file FILE', 'Defines the file to use as the build file') do |file|
70
+ options[:fileName] = file
71
+ end
72
+ opts.on('-s STAGE', '--stage STAGE', 'The build stage') do |stage|
73
+ options[:stage] = stage
74
+ end
75
+ opts.on('--ip', 'Shows the IP address of the running container') do
76
+ options[:showIP] = true
77
+ end
78
+ opts.on('--container', 'Shows the container\'s json information') do
79
+ options[:container] = true
80
+ end
81
+ opts.on('--status', 'Shows the container\'s status') do
82
+ options[:status] = true
83
+ end
84
+ end,
85
+ :runner => Info.new
86
+ },
87
+ 'stop' => {
88
+ :opts => OptionParser.new do |opts|
89
+ options[:stage] = 'dev'
90
+ end,
91
+ :runner => Stop.new
92
+ },
93
+ 'run' => {
94
+ :opts => OptionParser.new do |opts|
95
+ end,
96
+ :runner => Run.new
97
+ },
98
+ 'ssh' => {
99
+ :opts => OptionParser.new do |opts|
100
+ end,
101
+ :runner => SSH.new
102
+ },
103
+ 'deploy' => {
104
+ :opts => OptionParser.new do |opts|
105
+ options[:stage] = 'production'
106
+ opts.on('-s STAGE', '--stage STAGE', 'The build stage') do |stage|
107
+ options[:stage] = stage
108
+ end
109
+ opts.on('-t TAG', '--registry TAG', 'The registry to push to') do |tag|
110
+ options[:tag] = tag
111
+ end
112
+ end,
113
+ :runner => Deploy.new
114
+ },
115
+ 'install' => {
116
+ :opts => OptionParser.new do |opts|
117
+ options[:installPath] = "#{Dir.home}/.shuttl"
118
+ opts.on('-p PATH', '--install-path PATH', 'The path to install to') do |path|
119
+ options[:installPath] = path
120
+ end
121
+ end,
122
+ :runner => Install.new
123
+ }
124
+ ]
125
+
126
+ global = OptionParser.new do |opts|
127
+ opts.banner = "Usage: shuttl [options] [subcommand [options]]"
128
+ opts.on('--version', 'get the version of the app') do
129
+ options[:skip_all] = true
130
+ puts "Shuttl Version: #{VERSION}".green
131
+ end
132
+ # ...
133
+ end
134
+
135
+ global.order!
136
+ if !options[:skip_all]
137
+ command = ARGV.shift
138
+ subcommands[command][:opts].order!
139
+ subcommands[command][:runner].handle options, ARGV
140
+ end
@@ -0,0 +1,39 @@
1
+
2
+ class Loader
3
+ # require '../dsl/eval'
4
+ def initialize ()
5
+ @dirs = []
6
+ shuttlDir = File.join(Dir.home, '.shuttl/definitions')
7
+ @dirs << shuttlDir
8
+ if ENV.key('SHUTTL_PATH')
9
+ @dirs << ENV['SHUTTL_PATH'].split(":")
10
+ end
11
+ @dirs << Dir.getwd
12
+ end
13
+
14
+ def find (name, stage)
15
+ found = nil
16
+ [name, "#{name}.shuttlfile"].each do |fileName|
17
+ found = findFile(fileName, stage)
18
+ if !found.nil?
19
+ break
20
+ end
21
+ end
22
+ if found.nil?
23
+ throw "No shuttl file found"
24
+ end
25
+ found
26
+ end
27
+
28
+ def findFile (file, stage)
29
+ found = nil
30
+ @dirs.each do | dir |
31
+ potentialFileName = File.join(dir, file)
32
+ if File.exist? potentialFileName
33
+ found = ShuttlDSL.load potentialFileName, stage
34
+ break
35
+ end
36
+ end
37
+ found
38
+ end
39
+ end
@@ -0,0 +1,9 @@
1
+ # class ShuttlCommand
2
+ # def initialize
3
+
4
+ # end
5
+
6
+ # def merge (context)
7
+ # context.docker.concat @docker
8
+ # end
9
+ # end
@@ -0,0 +1,48 @@
1
+ require 'json'
2
+
3
+ class CommandBase
4
+ def initialize
5
+ @fileLocation = File.join(Dir.home, '.shuttl')
6
+ File.open(File.join(@fileLocation, 'info'), 'r') do |fi|
7
+ @info = JSON.parse(fi.read)
8
+ end
9
+ @cwd = Dir.getwd
10
+ end
11
+
12
+ def handle (options, args)
13
+ @args = args
14
+ self.run options
15
+ self.cleanUp options
16
+ end
17
+
18
+ def run (options)
19
+ end
20
+
21
+ def cleanUp (options)
22
+ File.open(File.join(Dir.home, '.shuttl/info'), 'w') do |fi|
23
+ fi.write @info.to_json
24
+ end
25
+ end
26
+
27
+ def hasImage?
28
+ hasImage = @info['images'].key?(@cwd)
29
+ if hasImage
30
+ @current_image_info = @info['images'][@cwd]
31
+ @image = Docker::Image.get(@info['images'][@cwd]["image_id"])
32
+ end
33
+ hasImage
34
+ end
35
+
36
+ def isRunning?
37
+ if (@info['containers'].key?(@cwd))
38
+ begin
39
+ @container = Docker::Container.get(@info['containers'][@cwd]["container_id"])
40
+ rescue Docker::Error::NotFoundError
41
+ @info['containers'].delete(@cwd)
42
+ return false
43
+ end
44
+ return @container.json['State']['Running']
45
+ end
46
+ false
47
+ end
48
+ end
@@ -0,0 +1,39 @@
1
+ require 'docker'
2
+ require 'json'
3
+
4
+ require_relative '../dsl/eval'
5
+ require_relative 'base'
6
+
7
+ class Build < CommandBase
8
+
9
+ def build (options)
10
+ Docker.options[:read_timeout] = 180
11
+ $stdout.print "Building new image\n"
12
+ file = File.expand_path(options[:fileName], Dir.getwd)
13
+ shuttlConfig = ShuttlDSL.load file, options[:stage]
14
+ shuttlConfig.setEnvFile options[:env]
15
+ # tar = shuttlConfig.makeImage options[:stage], @cwd
16
+ begin
17
+ step = 1
18
+ @image = shuttlConfig.build options[:stage], @cwd, options[:clean] do |v|
19
+ if (log = JSON.parse(v)) && log.has_key?("stream")
20
+ $stdout.puts log['stream']
21
+ if log['stream'].include? 'Step'
22
+ step += 1
23
+ end
24
+ end
25
+ end
26
+ if !shuttlConfig.getName().nil?
27
+ @image.tag('repo' => shuttlConfig.getName())
28
+ end
29
+ @info['images'][@cwd] = {:image_id => @image.id, :volumes => shuttlConfig.gatherVolume(options[:stage]), :built => Time.now.to_i, :stage => options[:stage]}
30
+ rescue Docker::Error::UnexpectedResponseError => error
31
+ $stderr.puts "Build Failed!".red
32
+ end
33
+ end
34
+
35
+ def run (options)
36
+ self.build options
37
+ end
38
+
39
+ end
@@ -0,0 +1,46 @@
1
+ require 'docker'
2
+ require 'json'
3
+ require 'colorize'
4
+
5
+ require_relative '../dsl/eval'
6
+ require_relative 'base'
7
+
8
+ class Deploy < CommandBase
9
+
10
+ def build (options)
11
+ $stdout.print "Building new image\n"
12
+ file = File.expand_path(options[:fileName], Dir.getwd)
13
+ shuttlConfig = ShuttlDSL.load file, options[:stage]
14
+ # tar = shuttlConfig.makeImage options[:stage], @cwd
15
+ begin
16
+ step = 1
17
+ @image = shuttlConfig.build options[:stage], @cwd, true do |v|
18
+ if (log = JSON.parse(v)) && log.has_key?("stream")
19
+ $stdout.puts log['stream']
20
+ if log['stream'].include? 'Step'
21
+ step += 1
22
+ end
23
+ end
24
+ end
25
+ if !shuttlConfig.getName().nil?
26
+ @image.tag('repo' => shuttlConfig.getName())
27
+ end
28
+ @info['images'][@cwd] = {:image_id => @image.id, :volumes => shuttlConfig.gatherVolume(options[:stage]), :built => Time.now.to_i, :stage => options[:stage]}
29
+ rescue Docker::Error::UnexpectedResponseError => error
30
+ $stderr.puts "Build Failed!".red
31
+ end
32
+ end
33
+
34
+ def run (options)
35
+ Docker.authenticate!
36
+ self.build options
37
+ `$(aws ecr get-login --no-include-email --region us-east-2)`
38
+ @image.tag('repo' => 'shuttl/django', 'tag' => options[:stage])
39
+ @image.tag('repo' => options[:tag], 'tag' => 'latest', force: true)
40
+ @image.push(nil, repo_tag: "#{options[:tag]}") do |stream, chunk|
41
+ $stdout.puts "#{stream}: #{chunk}"
42
+ end
43
+ $stdout.puts "Deploy done!".green
44
+ end
45
+
46
+ end
@@ -0,0 +1,50 @@
1
+ require 'docker'
2
+ require 'colorize'
3
+ require 'json'
4
+
5
+ require_relative '../dsl/eval'
6
+ require_relative 'base'
7
+
8
+ class Info < CommandBase
9
+
10
+ def build (options)
11
+ file = File.expand_path(options[:fileName], Dir.getwd)
12
+ shuttlConfig = ShuttlDSL.load file, options[:stage]
13
+ # tar = shuttlConfig.makeImage options[:stage], @cwd
14
+ $stdout.puts shuttlConfig.makeDockerFile options[:stage]
15
+ end
16
+
17
+ def run (options)
18
+ if options[:showDocker]
19
+ self.build options
20
+ end
21
+ if options[:showIP]
22
+ if isRunning?
23
+ $stdout.puts "IP Address: #{@info['containers'][@cwd]["json"]['NetworkSettings']['IPAddress']}".green
24
+ else
25
+ $stderr.puts "No container running for this dir!".red
26
+ end
27
+ end
28
+ if options[:container]
29
+ if isRunning?
30
+ puts JSON.pretty_generate(@container.json)
31
+ else
32
+ $stderr.puts "No container running for this dir!".red
33
+ end
34
+ end
35
+ if options[:status]
36
+ if isRunning?
37
+ if @container.json["State"]['Running']
38
+ $stdout.puts "Shuttl is up and running!".green
39
+ else
40
+ @container.json["State"].each do |name, value|
41
+ puts "#{name}: #{value}"
42
+ end
43
+ end
44
+ else
45
+ $stderr.puts "No container running for this dir!".red
46
+ end
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,38 @@
1
+ require 'docker'
2
+ require 'json'
3
+ require 'colorize'
4
+ require 'net/http'
5
+ require 'zip'
6
+
7
+ require_relative '../dsl/eval'
8
+ require_relative 'base'
9
+
10
+ class Install < CommandBase
11
+
12
+ def run (options)
13
+ tempFile = Tempfile.new('shuttl.zip')
14
+ Net::HTTP.start("s3.us-east-2.amazonaws.com") do |http|
15
+ resp = http.get("/shuttl-cli/shuttlinfo.zip")
16
+ tempFile.write(resp.body)
17
+ end
18
+ tempFile.close
19
+ Zip::File.open(tempFile.path) do |zip_file|
20
+ # Handle entries one by one
21
+ zip_file.each do |entry|
22
+ # Extract to file/directory/symlink
23
+ entry.extract(File.join(options[:installPath], entry.name.gsub('.shuttl/', ''))) {true}
24
+ end
25
+ end
26
+ tempFile.unlink
27
+
28
+ base = {
29
+ :images => {},
30
+ :containers => {},
31
+ }
32
+
33
+ File.open(File.join(Dir.home, '.shuttl/info'), 'w') do |fi|
34
+ fi.write base.to_json
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,21 @@
1
+ require 'docker'
2
+ require 'colorize'
3
+
4
+ require_relative 'base'
5
+
6
+ class Run < CommandBase
7
+
8
+ def run (options)
9
+ if !isRunning?
10
+ $stdout.puts "Shuttl not running! run shuttl start".red
11
+ return
12
+ end
13
+ fd = IO.sysopen "/dev/tty", "w"
14
+ ios = IO.new(fd, "w")
15
+ output = @container.exec(@args, tty: true) do |stream|
16
+ ios.print stream
17
+ end
18
+ ios.close
19
+ end
20
+
21
+ end
@@ -0,0 +1,29 @@
1
+ require 'docker'
2
+ require 'colorize'
3
+
4
+ require_relative 'base'
5
+
6
+ class SSH < CommandBase
7
+
8
+ def run (options)
9
+ if !isRunning?
10
+ $stderr.puts "Shuttl not running! run shuttl start".red
11
+ return
12
+ end
13
+ fd = IO.sysopen "/dev/tty", "w"
14
+ ios = IO.new(fd, "w")
15
+ ios.raw!
16
+ begin
17
+ @container.exec(['/bin/bash'], stdin: $stdin, tty: true, stdout: true, stderr: true, stream: true) do |stream|
18
+ ios.print stream
19
+ end
20
+ rescue Interrupt => e
21
+ rescue Exception => e
22
+ $stderr.puts "Error: #{e}".red
23
+ end
24
+ ios.close
25
+ ## The space is put before the command so that it won't show up in bash history
26
+ ` stty sane`
27
+ end
28
+
29
+ end
@@ -0,0 +1,77 @@
1
+ require 'docker'
2
+ require 'colorize'
3
+
4
+ require_relative '../dsl/eval'
5
+ require_relative 'base'
6
+
7
+ class Booted < StandardError
8
+ end
9
+
10
+ class Start < CommandBase
11
+
12
+ def build (options)
13
+ $stdout.print "Building new image\n"
14
+ file = File.expand_path(options[:fileName], Dir.getwd)
15
+ shuttlConfig = ShuttlDSL.load file, options[:stage]
16
+ begin
17
+ step = 1
18
+ @image = shuttlConfig.build options[:stage], @cwd do |v|
19
+ if (log = JSON.parse(v)) && log.has_key?("stream")
20
+ $stdout.puts log['stream']
21
+ if log['stream'].include? 'Step'
22
+ step += 1
23
+ end
24
+ end
25
+ end
26
+ if !shuttlConfig.getName().nil?
27
+ @image.tag('repo' => shuttlConfig.getName())
28
+ end
29
+ @info['images'][@cwd] = {:image_id => @image.id, :volumes => shuttlConfig.gatherVolume(options[:stage]), :built => Time.now.to_i}
30
+ rescue Docker::Error::UnexpectedResponseError => error
31
+ $stderr.puts error
32
+ end
33
+ end
34
+
35
+ def startContainer (options)
36
+ $stdout.puts "Starting image"
37
+ @container = Docker::Container.create(
38
+ 'Image' => @image.id,
39
+ 'HostConfig' => {
40
+ "Binds" => @current_image_info['volumes'].map {|mountPoint, hostDir| "#{hostDir}:#{mountPoint}"}
41
+ }
42
+ ).start
43
+ begin
44
+ catch (Booted) do
45
+ @container.tap(&:start).attach do |stream, chunk|
46
+ if chunk.include? 'SHUTTL IMAGE BOOTED'
47
+ throw Booted
48
+ end
49
+ $stdout.puts "#{stream}: #{chunk}"
50
+ end
51
+ end
52
+ $stdout.puts "Container is fully booted and ready for use!".green
53
+ $stdout.puts "Container's id is #{@container.id} and the IP is: #{@container.json['NetworkSettings']['IPAddress']}".green
54
+ rescue Exception => e
55
+ $std.puts "error: #{e}"
56
+ end
57
+ @info['containers'][@cwd] = {:container_id => @container.id, :status => 'running', :json => @container.json }
58
+ end
59
+
60
+ def shouldRebuild?(mtime)
61
+ @current_image_info["built"] < mtime.to_i
62
+ end
63
+
64
+
65
+ def run (options)
66
+ if self.isRunning?
67
+ $stdout.puts "Already running!"
68
+ return
69
+ end
70
+ if self.hasImage? && !shouldRebuild?(File.mtime(File.expand_path(options[:fileName], Dir.getwd)))
71
+ return self.startContainer options
72
+ end
73
+ self.build options
74
+ self.startContainer options
75
+ end
76
+
77
+ end
@@ -0,0 +1,18 @@
1
+ require 'docker'
2
+
3
+ require_relative '../dsl/eval'
4
+ require_relative 'base'
5
+
6
+ class Stop < CommandBase
7
+
8
+ def run (options)
9
+ if self.isRunning?
10
+ $stdout.puts "Stopping Shuttl instance"
11
+ @container.kill
12
+ else
13
+ $stdout.puts "Not running?"
14
+ end
15
+
16
+ end
17
+
18
+ end
data/src/dsl/Shuttl.rb ADDED
@@ -0,0 +1,100 @@
1
+ ## Base class for the DSL definition
2
+ require 'uri'
3
+ require 'docker'
4
+
5
+ class Shuttl
6
+
7
+ attr_accessor :name, :stage
8
+
9
+ def initialize (builder)
10
+ @builder = builder
11
+ end
12
+
13
+ def DEFINES (name)
14
+ @builder.setName(name)
15
+ end
16
+
17
+ def add (command)
18
+ @builder.add command
19
+ end
20
+
21
+ def fileAdd (source)
22
+ @builder.fileAdd source
23
+ end
24
+
25
+ def FROM (name)
26
+ self.add "FROM #{name}"
27
+ end
28
+
29
+ def RUN (name)
30
+ self.add "RUN #{name}"
31
+ end
32
+
33
+ def ADD (source, destination)
34
+ if source !=~ /\A#{URI::regexp}\z/
35
+ @builder.fileAdd(source, destination)
36
+ else
37
+ self.add "ADD #{source} #{destination}"
38
+ end
39
+ end
40
+
41
+ def COPY(source, destination)
42
+ self.add "COPY #{source} #{destination}"
43
+ end
44
+
45
+ def EXPOSE (port)
46
+ self.add "EXPOSE #{port}"
47
+ end
48
+
49
+ def ENTRYPOINT (entrypoint)
50
+ @builder.entrypoint = entrypoint
51
+ end
52
+
53
+ def ONSTART (cmd)
54
+ self.add "RUN echo \"#{cmd}\" >> /.shuttl/run"
55
+ end
56
+
57
+ def ONRUN (cmd)
58
+ self.add "RUN echo \"#{cmd}\" >> /.shuttl/start"
59
+ end
60
+
61
+ def EXTENDS (name)
62
+ require_relative '../Shuttl/Loader'
63
+ loader = Loader.new
64
+ @builder.merge loader.find name, @builder.buildStage
65
+ end
66
+
67
+ def USE (name)
68
+ EXTENDS name
69
+ end
70
+
71
+ def SET (setting, value)
72
+ @builder.set setting, value
73
+ end
74
+
75
+ def ON (name)
76
+ @builder.on name do
77
+ yield
78
+ end
79
+ end
80
+
81
+ def VOLUME (volume)
82
+ self.add "VOLUME #{volume}"
83
+ end
84
+
85
+ def ATTACH (localDir, volume)
86
+ @builder.attach(localDir, volume)
87
+ end
88
+
89
+ def ENVIRONMENT (tester)
90
+ tester.call(@builder.buildSettings[:settings]['ENVIRONMENT'])
91
+ end
92
+
93
+ def IS (name)
94
+ proc {|value| value == name }
95
+ end
96
+
97
+ def CMD (cmd)
98
+ @builder.cmd cmd
99
+ end
100
+ end
@@ -0,0 +1,209 @@
1
+ class Builder
2
+
3
+ attr_accessor :stage, :buildStage, :buildSettings, :volumes, :entrypoint
4
+ def initialize (fileLocation, stage=nil, name=nil)
5
+ @name = name
6
+ @buildStage = stage
7
+ @buildSettings = { :settings => {}, :docker => [], :files => {}, :volumes => {} }
8
+ @entrypoint = nil
9
+ @volumes = {}
10
+ @cwd = Dir.getwd
11
+ @fileLocation = fileLocation
12
+ @output = Tempfile.new('shuttlBuild')
13
+ @tar = Gem::Package::TarWriter.new(@output)
14
+ @dirs = [fileLocation, Dir.getwd, File.join(Dir.home, '.shuttl/definitions')]
15
+ if ENV.key('SHUTTL_PATH')
16
+ @dirs << ENV['SHUTTL_PATH'].split(":")
17
+ end
18
+ end
19
+
20
+ def findFile(name)
21
+ found = nil
22
+ @dirs.each do | dir |
23
+ potentialFileName = File.join(dir, name)
24
+ if File.exist? potentialFileName
25
+ if File.directory? potentialFileName
26
+ raise Errno::EISDIR
27
+ end
28
+ found = File.open(potentialFileName)
29
+ break
30
+ end
31
+ end
32
+ if found.nil?
33
+ throw "File not found: #{name}"
34
+ end
35
+ found
36
+ end
37
+
38
+ def setName(name)
39
+ @name = name
40
+ end
41
+
42
+ def getName
43
+ @name
44
+ end
45
+
46
+ def addFile(localName, nameInDocker, rootName=nil)
47
+ if rootName.nil?
48
+ rootName = nameInDocker
49
+ end
50
+ begin
51
+ file = findFile(localName)
52
+ rescue Errno::EISDIR
53
+ @tar.mkdir(nameInDocker, 0640)
54
+ Dir["#{localName}/**/*"].each do |file|
55
+ pathInDocker = File.join(rootName, file)
56
+ addFile(file, pathInDocker, rootName)
57
+ end
58
+ return
59
+ end
60
+
61
+ permissions = file.is_a?(Hash) ? file[:permissions] : 0640
62
+ @tar.add_file(nameInDocker, permissions) do | tarFile |
63
+ while buffer = file.read(1024 * 1000)
64
+ tarFile.write(buffer)
65
+ end
66
+ end
67
+ @tar.flush
68
+ end
69
+
70
+ def create_tar(hash = {}, tap=true)
71
+ output = StringIO.new
72
+ Gem::Package::TarWriter.new(output) do |tar|
73
+ hash.each do |file_name, file_details|
74
+ permissions = file_details.is_a?(Hash) ? file_details[:permissions] : 0640
75
+ tar.add_file(file_name, permissions) do |tar_file|
76
+ content = file_details.is_a?(Hash) ? file_details[:content] : file_details
77
+ tar_file.write(content)
78
+ end
79
+ end
80
+ end
81
+ return output.tap(&:rewind)
82
+ end
83
+
84
+ def gatherSettings (stage)
85
+ settings = @buildSettings[:settings]
86
+ end
87
+
88
+ def makeDockerFile (stage)
89
+ settings = gatherSettings(stage)
90
+ settingsArr = []
91
+ settings.each do |key, value|
92
+ settingsArr << "ARG #{key}=#{value}"
93
+ settingsArr << "ENV #{key}=${#{key}}"
94
+ end
95
+ definition = @buildSettings[:docker]
96
+ definition = [definition[0], ] + settingsArr + definition[1..definition.count]
97
+ if @entrypoint
98
+ definition << "ENTRYPOINT #{@entrypoint}"
99
+ end
100
+ volumes = gatherVolume stage
101
+ definition << "RUN echo 'echo SHUTTL IMAGE BOOTED' >> /.shuttl/run"
102
+ definition << "RUN echo 'bash /.shuttl/start' >> /.shuttl/run"
103
+ if volumes.keys.count > 0
104
+ definition << "VOLUME #{volumes.keys}"
105
+ end
106
+ definition.join("\n")
107
+ end
108
+
109
+
110
+ def gatherFiles (stage, cwd)
111
+ files = @buildSettings[:files]
112
+ files
113
+ end
114
+
115
+ def makeImage (stage, cwd)
116
+ dockerfile = self.makeDockerFile stage
117
+ files = self.gatherFiles stage, cwd
118
+ @tar.add_file("Dockerfile", 0640) do |tar_file|
119
+ tar_file.write(dockerfile)
120
+ end
121
+ files.each do |key, val|
122
+ addFile(key, val)
123
+ end
124
+ end
125
+
126
+ def build (stage, cwd, clean )
127
+ begin
128
+ makeImage stage, cwd
129
+ query = {}
130
+ query[:buildargs] = {}
131
+ @buildSettings[:settings].each do |key, value|
132
+ query[:buildargs][key] = value.to_s
133
+ end
134
+ query[:buildargs] = query[:buildargs].merge @env || Hash[]
135
+ puts query[:buildargs]
136
+ query[:buildargs] = query[:buildargs].to_json
137
+ if clean
138
+ query[:nocache] = true
139
+ end
140
+ puts query
141
+ Docker::Image.build_from_tar @output.tap(&:rewind), query do |v|
142
+ yield v
143
+ end
144
+ ensure
145
+ @output.close
146
+ @output.unlink
147
+ end
148
+ end
149
+
150
+ def add (command)
151
+ @buildSettings[:docker] << command
152
+ end
153
+
154
+ def fileAdd (source, destination)
155
+ @buildSettings[:files][source] = destination
156
+ add "ADD #{destination} #{destination}"
157
+ end
158
+
159
+ def set (setting, value)
160
+ @buildSettings[:settings][setting] = value
161
+ end
162
+
163
+ def on (name)
164
+ if name != @buildStage
165
+ return
166
+ end
167
+ yield
168
+ end
169
+
170
+ def merge (other)
171
+ newInfo = @buildSettings.merge(other.buildSettings) do |key, old, newVal|
172
+ if key == :settings
173
+ old.merge newVal
174
+ elsif key == :files
175
+ old.merge newVal
176
+ elsif key == :docker
177
+ old.concat newVal
178
+ end
179
+ end
180
+ @buildSettings = newInfo
181
+ @volumes = other.volumes.merge @volumes
182
+
183
+ end
184
+
185
+ def attach(localDir, volume)
186
+ if localDir == 'pwd'
187
+ localDir = @cwd
188
+ end
189
+ @volumes[volume] = localDir
190
+ end
191
+
192
+ def gatherVolume(stage)
193
+ @volumes
194
+ end
195
+
196
+ def setEnvFile(env)
197
+ if env.nil?
198
+ @env = Hash[]
199
+ return
200
+ end
201
+ File.open(env, 'r') do |fi|
202
+ @env = JSON.parse(fi.read)
203
+ end
204
+ end
205
+
206
+ def cmd (cmd)
207
+ add "CMD #{cmd}"
208
+ end
209
+ end
data/src/dsl/eval.rb ADDED
@@ -0,0 +1,14 @@
1
+ require_relative 'Shuttl'
2
+ require_relative 'buildContext'
3
+
4
+ class ShuttlDSL
5
+
6
+ def self.load(filename, stage)
7
+ builder = Builder.new File.dirname(filename), stage
8
+ dsl = Shuttl.new builder
9
+ dsl.instance_eval(File.read(filename), filename)
10
+ return builder
11
+ end
12
+
13
+ end
14
+
data/src/main.rb ADDED
File without changes
data/src/settings.rb ADDED
@@ -0,0 +1,4 @@
1
+ module ShuttlSettings
2
+ VERSION = "0.3.1"
3
+ BUILD = 1
4
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shuttl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
5
+ platform: ruby
6
+ authors:
7
+ - Yoseph Radding
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2010-04-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: docker-api
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.34.0
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.34.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 1.34.0
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.34.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: colorize
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 0.8.1
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 0.8.1
47
+ - !ruby/object:Gem::Dependency
48
+ name: rubyzip
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 1.2.0
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 1.2.0
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: 1.2.0
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 1.2.0
67
+ description: Shuttl builds doccker images easily
68
+ email: yoseph@shuttl.io
69
+ executables:
70
+ - shuttl
71
+ extensions: []
72
+ extra_rdoc_files: []
73
+ files:
74
+ - bin/shuttl
75
+ - src/Shuttl/Loader.rb
76
+ - src/Shuttl/Shuttl.rb
77
+ - src/commands/base.rb
78
+ - src/commands/build.rb
79
+ - src/commands/deploy.rb
80
+ - src/commands/info.rb
81
+ - src/commands/install.rb
82
+ - src/commands/run.rb
83
+ - src/commands/ssh.rb
84
+ - src/commands/start.rb
85
+ - src/commands/stop.rb
86
+ - src/dsl/Shuttl.rb
87
+ - src/dsl/buildContext.rb
88
+ - src/dsl/eval.rb
89
+ - src/main.rb
90
+ - src/settings.rb
91
+ homepage: https://github.com/shuttl-io/shuttl-cli
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message: Thanks for installing! Run shuttl install to complete the install
96
+ rdoc_options: []
97
+ require_paths:
98
+ - src
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.5.1
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Shuttl!
115
+ test_files: []