phoebo 0.2.4 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 79a698357c05fcfd2bf85d0e8e0401d21de7fd67
4
- data.tar.gz: 8f194beb7a95e1fe063d3c3a3ecc6a8b9bf3abff
3
+ metadata.gz: 99090a0ea219f65bfefe343d797e5569d01ed9e2
4
+ data.tar.gz: 48f00da413d5e5923906bbcd4b04a3dd5350cd73
5
5
  SHA512:
6
- metadata.gz: ec764c25bd1cfbc49185cb3c66dae19fdc2660994be5ccc3cc9ee9f74b3882fb4e583ede1bb0506ef7f4f0e0d512cc646477ea6eb7217edb94fdfe9fc8d131e7
7
- data.tar.gz: 42a9ecc1c64f7b747ae53247be494c2bb08faee16f421643a1a2e5a07c983b4ae9b80e0e5d8fce7806a271263479cc0ddeabf77d1484480057078efe2d8bef82
6
+ metadata.gz: 40abf6542d1aaed1cfb89246a87e909be330066fef1ee2ec04cb03d3626b5e914e1fa5c9b904a7a4c53c28277f117e4c96659a4ed5e40be26ab202f9e380cb0e
7
+ data.tar.gz: 82a50760a0b290e2eed500d92480d0ae0c641a883735addccc2f973aa5228db76a02603d40ea4f67827940f6aad874522128a84aa44193de3a5661ed690b75a3
@@ -6,10 +6,10 @@ module Phoebo
6
6
  attr_accessor :images, :tasks
7
7
 
8
8
  # Loads config from file
9
- # @see Phoebo.configure()
10
- def self.new_from_file(file_path)
9
+ def self.new_from_file(file_path, request = nil)
11
10
  begin
12
11
  @instance = nil
12
+ @args = [ request, file_path ]
13
13
  Kernel.load file_path, true
14
14
  @instance
15
15
 
@@ -18,20 +18,25 @@ module Phoebo
18
18
  end
19
19
  end
20
20
 
21
+ # @see Phoebo.configure()
21
22
  def self.new_from_block(block)
22
- @instance = self.new
23
+ args = @args || [ nil, nil ]
24
+ @args = nil
25
+
26
+ @instance = self.new(*args)
23
27
  @instance.dsl_eval(block)
24
28
  end
25
29
 
26
30
  # Instance initialization
27
- def initialize
31
+ def initialize(request = nil, config_path = nil)
32
+ @request = request
28
33
  @images = []
29
34
  @tasks = []
30
35
  end
31
36
 
32
37
  # Evaluate block within DSL context
33
38
  def dsl_eval(block)
34
- @dsl ||= DSL.new(self)
39
+ @dsl ||= DSL.new(self, @request)
35
40
  @dsl.instance_eval(&block)
36
41
 
37
42
  # Finish config
@@ -51,8 +56,13 @@ module Phoebo
51
56
 
52
57
  # Private DSL
53
58
  class DSL
54
- def initialize(config)
55
- @config = config
59
+ def initialize(config, request)
60
+ @config = config
61
+ @request = request
62
+ end
63
+
64
+ def secrets
65
+ @request ? @request.secrets : {}
56
66
  end
57
67
 
58
68
  def image(name, options, &block)
@@ -90,13 +100,11 @@ module Phoebo
90
100
  raise Phoebo::SyntaxError.new("Unexpected parameter #{options.keys.first} for service #{name}.")
91
101
  end
92
102
 
93
- puts task.inspect
94
-
95
103
  @config.tasks << task
96
104
  task
97
105
  end
98
106
 
99
- def task(name, image, *args)
107
+ def task(name, options = {})
100
108
  _task_definition_check('service', name, options)
101
109
 
102
110
  task = {
@@ -105,6 +113,11 @@ module Phoebo
105
113
 
106
114
  _apply_task_options(task, options)
107
115
 
116
+ if options[:on_demand]
117
+ task[:on_demand] = options[:on_demand] ? true : false
118
+ options.delete(:on_demand)
119
+ end
120
+
108
121
  unless options.empty?
109
122
  raise Phoebo::SyntaxError.new("Unexpected parameter #{options.keys.first} for task #{name}.")
110
123
  end
@@ -47,10 +47,14 @@ module Phoebo
47
47
 
48
48
  # Define action methods for all commands
49
49
  ImageCommands.commands.each do |id, command_class|
50
- define_method(id) do |*args|
51
- @image.actions << command_class.send(:action, *args)
50
+ define_method(id) do |*args, &block|
51
+ @image.actions << command_class.send(:action, *args, &block)
52
52
  end
53
53
  end
54
+
55
+ def method_missing(name, args, block)
56
+ raise Phoebo::SyntaxError.new("No such build command #{name} for image #{@image.name}")
57
+ end
54
58
  end
55
59
 
56
60
  end
@@ -5,12 +5,8 @@ module Phoebo::Config::ImageCommands
5
5
  end
6
6
 
7
7
  def self.action(src, dest)
8
- return Proc.new do |dockerfile, files|
9
-
10
- virtual = 'project/' + src
11
- files[virtual] = src
12
-
13
- dockerfile << 'ADD ' + virtual + ' ' + dest
8
+ return Proc.new do |build|
9
+ build.copy(build.base_path + src, dest)
14
10
  end
15
11
  end
16
12
  end
@@ -0,0 +1,19 @@
1
+ module Phoebo::Config::ImageCommands
2
+ class Create
3
+ def self.id
4
+ :create
5
+ end
6
+
7
+ def self.action(destination, content = nil, &block)
8
+ return Proc.new do |build|
9
+ if block
10
+ build.create(destination, &block)
11
+ else
12
+ build.create(destination) do |out_stream|
13
+ out_stream.write(content)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -5,9 +5,9 @@ module Phoebo::Config::ImageCommands
5
5
  end
6
6
 
7
7
  def self.action(cmd, *args)
8
- return Proc.new do |dockerfile, files|
8
+ return Proc.new do |build|
9
9
  # TODO: decide which exec method we will use and enforce escaping
10
- dockerfile << "RUN #{cmd} #{args.join(' ')}"
10
+ build << "RUN #{cmd} #{args.join(' ')}"
11
11
  end
12
12
  end
13
13
  end
@@ -17,49 +17,20 @@ module Phoebo
17
17
 
18
18
  if image.from.first == :base_image
19
19
  dockerfile << "FROM #{image.from[1]}"
20
-
21
- # Apply all defined actions
22
- image.actions.each do |action|
23
- action.call(dockerfile, project_files)
24
- end
25
20
  end
26
21
 
27
- # TODO: Honor file mode
28
22
  tar_stream = StringIO.new
29
23
  Gem::Package::TarWriter.new(tar_stream) do |tar|
30
- tar.add_file('Dockerfile', 0640) { |out_stream| out_stream.write(dockerfile.join("\n")) }
31
- tar.mkdir('project', 0750)
32
-
33
- # Copy project file into virtual destination
34
- while !project_files.empty? do
35
- virtual, relative = project_files.shift
36
- real = @base_path + relative
37
24
 
38
- if real.directory?
39
- real.each_entry do |child|
40
- next if child.fnmatch?('..') or child.fnmatch?('.')
41
- if child.directory?
42
- tar.mkdir("#{virtual}/#{child}", 0750)
43
- end
25
+ image_build = Build.new(@base_path, dockerfile, tar)
44
26
 
45
- # Add to stack
46
- project_files["#{virtual}/#{child}"] = "#{relative}/#{child}"
47
- end
48
-
49
- elsif real.file?
50
-
51
- # 'file.txt' -> 'dest/'
52
- if virtual.end_with?('/')
53
- dest += real.basename.to_s
54
- end
55
-
56
- # Read file to our TAR output stream
57
- tar.add_file(virtual, 0640) do |out_stream|
58
- in_stream = real.open('r')
59
- IO.copy_stream(in_stream, out_stream)
60
- end
61
- end
27
+ # Apply all defined actions
28
+ image.actions.each do |action|
29
+ action.call(image_build)
62
30
  end
31
+
32
+ # Create Dockerfile
33
+ tar.add_file('Dockerfile', 0640) { |out_stream| out_stream.write(dockerfile.join("\n")) }
63
34
  end
64
35
 
65
36
  begin
@@ -86,6 +57,95 @@ module Phoebo
86
57
  raise DockerError, e.message
87
58
  end
88
59
  end
60
+
61
+ class Build
62
+ attr_reader :base_path, :dockerfile, :tar
63
+
64
+ def initialize(base_path, dockerfile, tar)
65
+ @base_path = base_path
66
+ @dockerfile = dockerfile
67
+ @tar = tar
68
+
69
+ @dirs = { }
70
+ end
71
+
72
+ # Add Dockerfile instruction
73
+ def <<(str)
74
+ @dockerfile << str
75
+ end
76
+
77
+ # Create new file by block
78
+ def create(destination_path, mode = 640, &block)
79
+ basename = Pathname.new(destination_path).basename.to_s
80
+ random = random_dir
81
+
82
+ # Add Dockerfile instruction
83
+ virtual = "#{random}/#{basename}"
84
+ self << 'ADD ' + virtual + ' ' + destination_path
85
+
86
+ # Create directory for this instruction
87
+ tar.mkdir(random, 750)
88
+
89
+ # Add file with yielded block
90
+ @tar.add_file(virtual, mode, &block)
91
+ end
92
+
93
+ # Copy files to docker image
94
+ def copy(source, destination_path)
95
+ # TODO: Honor file mode
96
+
97
+ source = Pathname.new(source) unless source.is_a?(Pathname)
98
+ random = random_dir
99
+
100
+ # Add Dockerfile instruction
101
+ if source.directory?
102
+ self << 'ADD ' + random + ' ' + destination_path
103
+ files = [ [ source, random ] ]
104
+ else
105
+ self << 'ADD ' + random + '/' + source.basename.to_s + ' ' + destination_path
106
+ files = [ [ source, random + '/' ] ]
107
+ end
108
+
109
+ # Create directory for this instruction
110
+ tar.mkdir(random, 750)
111
+
112
+ # Add all the files recursively
113
+ while !files.empty? do
114
+ source, destination_path = files.shift
115
+
116
+ # Directory
117
+ if source.directory?
118
+ source.each_entry do |child|
119
+ next if child.fnmatch?('..') or child.fnmatch?('.')
120
+ @tar.mkdir("#{destination_path}/#{child}", 0750) if child.directory?
121
+ files << [ source + child, "#{destination_path}/#{child}" ]
122
+ end
123
+
124
+ # File
125
+ elsif source.file?
126
+ # If we are adding 'file.txt' to 'dest/': we need to append filename
127
+ destination_path += source.basename.to_s if destination_path.end_with?('/')
128
+
129
+ # Add file copy
130
+ @tar.add_file(destination_path, 0640) do |out_stream|
131
+ in_stream = source.open('r')
132
+ IO.copy_stream(in_stream, out_stream)
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ private
139
+
140
+ def random_dir
141
+ begin
142
+ random = SecureRandom.hex
143
+ end while @dirs.key?(random)
144
+
145
+ @dirs[random] = true
146
+ random
147
+ end
148
+ end
89
149
  end
90
150
  end
91
151
  end
@@ -36,6 +36,21 @@ module Phoebo
36
36
  IO.write(@ssh_public_file, key_content)
37
37
  end
38
38
 
39
+ # Secrets (default: empty hash)
40
+ def secrets
41
+ @secrets ||= {}
42
+ end
43
+
44
+ # Secrets validation on set
45
+ def secrets=(hash)
46
+ hash.each do |key, value|
47
+ raise InvalidRequestError, "Invalid format for request secret #{key.inspect}" \
48
+ unless value.is_a? String or value.is_a? Numeric
49
+ end
50
+
51
+ @secrets = hash
52
+ end
53
+
39
54
  # Loads request with data from hash
40
55
  def load_from_hash!(hash)
41
56
  hash.each do |k, v|
@@ -50,7 +65,7 @@ module Phoebo
50
65
  # Loads request from JSON string
51
66
  def load_from_json!(raw_data)
52
67
  begin
53
- hash = JSON.parse(raw_data)
68
+ hash = JSON.parse(raw_data, symbolize_names: true)
54
69
  rescue JSON::ParserError => e
55
70
  raise InvalidRequestError, "Malformed JSON data."
56
71
  end
@@ -1,3 +1,3 @@
1
1
  module Phoebo
2
- VERSION = "0.2.4"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -20,7 +20,7 @@ module Phoebo
20
20
 
21
21
  if File.exists?(config_path)
22
22
  # Load config
23
- config = Config.new_from_file(config_path)
23
+ config = Config.new_from_file(config_path, @request)
24
24
 
25
25
  # Build & push image
26
26
  builder = Docker::ImageBuilder.new(path)
@@ -48,7 +48,7 @@ describe Phoebo::Config do
48
48
 
49
49
  it 'processes images' do
50
50
  image = instance_double(Phoebo::Config::Image)
51
- expect(Phoebo::Config::Image).to receive(:new).with('image-name', 'base-image-name', image_dsl_block).and_return(image)
51
+ expect(Phoebo::Config::Image).to receive(:new).with('image-name', { from: 'base-image-name' }, image_dsl_block).and_return(image)
52
52
 
53
53
  subject.dsl_eval(dsl)
54
54
  expect(subject.images).to include(image)
@@ -24,7 +24,7 @@ describe Phoebo::Docker::ImageBuilder do
24
24
 
25
25
  image = instance_double(Phoebo::Config::Image)
26
26
  allow(image).to receive(:name).and_return('image-name')
27
- allow(image).to receive(:from).and_return('debian')
27
+ allow(image).to receive(:from).and_return([ :base_image, 'debian'])
28
28
  allow(image).to receive(:actions).and_return(actions)
29
29
 
30
30
  image
@@ -85,7 +85,10 @@ describe Phoebo::Request do
85
85
  let(:json) do
86
86
  <<-EOS
87
87
  {
88
- "repo_url": "ssh://somehost.tld/user/repo.git"
88
+ "repo_url": "ssh://somehost.tld/user/repo.git",
89
+ "secrets": {
90
+ "dbpassword": "somesecretpassword"
91
+ }
89
92
  }
90
93
  EOS
91
94
  end
@@ -106,6 +109,7 @@ describe Phoebo::Request do
106
109
  it 'applies arguments' do
107
110
  subject.load_from_json!(json)
108
111
  expect(subject.repo_url).to eql('ssh://somehost.tld/user/repo.git')
112
+ expect(subject.secrets[:dbpassword]).to eql('somesecretpassword')
109
113
  end
110
114
  end
111
115
 
@@ -123,19 +127,19 @@ describe Phoebo::Request do
123
127
  end
124
128
  end
125
129
 
126
- # TODO: cover all possible states
127
- context 'loading from url' do
128
- let(:http_response) do
129
- response = double
130
- allow(response).to receive(:code).and_return(200)
131
- allow(response).to receive(:body).and_return(json)
132
- response
133
- end
134
-
135
- it 'loads data' do
136
- allow(Net::HTTP).to receive(:start).and_return(http_response)
137
- expect(subject).to receive(:load_from_json!).with(json)
138
- subject.load_from_url!('http://test')
139
- end
140
- end
130
+ # TODO: cover all possible states and use some HTTP mocking library
131
+ # context 'loading from url' do
132
+ # let(:http_response) do
133
+ # response = double(Net::HTTPOK)
134
+ # allow(response).to receive(:code).and_return(200)
135
+ # allow(response).to receive(:body).and_return(json)
136
+ # response
137
+ # end
138
+
139
+ # it 'loads data' do
140
+ # allow(Net::HTTP).to receive(:start).and_return(http_response)
141
+ # expect(subject).to receive(:load_from_json!).with(json)
142
+ # subject.load_from_url!('http://test')
143
+ # end
144
+ # end
141
145
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phoebo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Staněk
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-02 00:00:00.000000000 Z
11
+ date: 2015-04-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -129,6 +129,7 @@ files:
129
129
  - lib/phoebo/config/image.rb
130
130
  - lib/phoebo/config/image_commands.rb
131
131
  - lib/phoebo/config/image_commands/add.rb
132
+ - lib/phoebo/config/image_commands/create.rb
132
133
  - lib/phoebo/config/image_commands/run.rb
133
134
  - lib/phoebo/console.rb
134
135
  - lib/phoebo/docker.rb