php_fpm_docker 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.rubocop.yml +20 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +79 -0
- data/LICENSE.txt +22 -0
- data/README.md +68 -0
- data/Rakefile +21 -0
- data/bin/php_fpm_docker +4 -0
- data/doc/graphs/structure/structure.gliffy +1 -0
- data/doc/graphs/structure/structure.png +0 -0
- data/doc/graphs/structure/structure.svg +1 -0
- data/lib/php_fpm_docker.rb +6 -0
- data/lib/php_fpm_docker/application.rb +314 -0
- data/lib/php_fpm_docker/launcher.rb +309 -0
- data/lib/php_fpm_docker/pool.rb +169 -0
- data/lib/php_fpm_docker/version.rb +5 -0
- data/php_fpm_docker.gemspec +30 -0
- data/spec/helper.rb +19 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/unit/application_spec.rb +197 -0
- data/spec/unit/launcher_spec.rb +339 -0
- data/spec/unit/pool_spec.rb +277 -0
- metadata +172 -0
@@ -0,0 +1,277 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'php_fpm_docker/pool'
|
3
|
+
|
4
|
+
LAUNCHER_WEB_PATH = '/var/webpath'
|
5
|
+
LAUNCHER_BIND_MOUNTS = ['/mnt/bind']
|
6
|
+
LAUNCHER_SPAWN_FCGI = '/usr/bin/fcgi-bin'
|
7
|
+
LAUNCHER_PHP = '/usr/bin/php'
|
8
|
+
|
9
|
+
describe PhpFpmDocker::Pool do
|
10
|
+
|
11
|
+
# Default config
|
12
|
+
let(:default_config) {
|
13
|
+
{
|
14
|
+
'listen' => '/tmp/name2.sock',
|
15
|
+
'listen.owner' => 'user1',
|
16
|
+
'listen.group' => 'group1',
|
17
|
+
'listen.mode' => '0660',
|
18
|
+
'user' => 'user2',
|
19
|
+
'group' => 'group2',
|
20
|
+
'php_admin_value[open_basedir]' => [
|
21
|
+
'/mnt/invalid/not_me_test',
|
22
|
+
File.join(LAUNCHER_WEB_PATH,'test123','web'),
|
23
|
+
File.join(LAUNCHER_WEB_PATH,'clients','client123','web2','web'),
|
24
|
+
].join(':'),
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
# Mock Etc (getuid and getpwdid)
|
29
|
+
before (:example) {
|
30
|
+
# mock users
|
31
|
+
user1 = double
|
32
|
+
allow(user1).to receive(:uid).and_return(1001)
|
33
|
+
user2 = double
|
34
|
+
allow(user2).to receive(:uid).and_return(1002)
|
35
|
+
allow(Etc).to receive(:getpwnam).with('user1').and_return(user1)
|
36
|
+
allow(Etc).to receive(:getpwnam).with('user2').and_return(user2)
|
37
|
+
# mock groups
|
38
|
+
group1 = double
|
39
|
+
allow(group1).to receive(:gid).and_return(1001)
|
40
|
+
group2 = double
|
41
|
+
allow(group2).to receive(:gid).and_return(1002)
|
42
|
+
allow(Etc).to receive(:getgrnam).with('group1').and_return(group1)
|
43
|
+
allow(Etc).to receive(:getgrnam).with('group2').and_return(group2)
|
44
|
+
}
|
45
|
+
|
46
|
+
# Mock Docker
|
47
|
+
before (:example){
|
48
|
+
@dbl_docker = class_double('Docker').as_stubbed_const()
|
49
|
+
@dbl_docker_container = class_double('Docker::Container').as_stubbed_const()
|
50
|
+
}
|
51
|
+
|
52
|
+
# Mock launcher
|
53
|
+
let(:launcher) {
|
54
|
+
l = double
|
55
|
+
|
56
|
+
# respond with global bind mounts
|
57
|
+
allow(l).to receive(:bind_mounts).and_return(LAUNCHER_BIND_MOUNTS)
|
58
|
+
|
59
|
+
# respond with webpath
|
60
|
+
allow(l).to receive(:web_path).and_return(LAUNCHER_WEB_PATH)
|
61
|
+
|
62
|
+
## Mock docker image
|
63
|
+
i = double
|
64
|
+
allow(i).to receive(:id).and_return('deadbeef')
|
65
|
+
# respond with image
|
66
|
+
allow(l).to receive(:docker_image).and_return(i)
|
67
|
+
|
68
|
+
# respond spwan fcgi path
|
69
|
+
allow(l).to receive(:spawn_cmd_path).and_return(LAUNCHER_SPAWN_FCGI)
|
70
|
+
|
71
|
+
# respond php path
|
72
|
+
allow(l).to receive(:php_cmd_path).and_return(LAUNCHER_PHP)
|
73
|
+
|
74
|
+
l
|
75
|
+
}
|
76
|
+
|
77
|
+
describe 'pool1 with default values' do
|
78
|
+
let (:p) {
|
79
|
+
p = described_class.new({
|
80
|
+
:name => 'pool1',
|
81
|
+
:config => default_config,
|
82
|
+
:launcher => launcher,
|
83
|
+
})
|
84
|
+
}
|
85
|
+
it "parse correct uid" do
|
86
|
+
expect(p.uid).to eq(1002)
|
87
|
+
end
|
88
|
+
it "parse correct gid" do
|
89
|
+
expect(p.gid).to eq(1002)
|
90
|
+
end
|
91
|
+
it "parse correct listen_uid" do
|
92
|
+
expect(p.listen_uid).to eq(1001)
|
93
|
+
end
|
94
|
+
it "parse correct listen_gid" do
|
95
|
+
expect(p.listen_gid).to eq(1001)
|
96
|
+
end
|
97
|
+
describe "#spawn_command" do
|
98
|
+
before (:example) {
|
99
|
+
@list = p.spawn_command
|
100
|
+
}
|
101
|
+
it 'list include spawn_fcgi' do
|
102
|
+
expect(@list).to include(LAUNCHER_SPAWN_FCGI)
|
103
|
+
end
|
104
|
+
it 'list include socket path' do
|
105
|
+
expect(@list).to include(default_config['listen'])
|
106
|
+
end
|
107
|
+
it 'list is flat' do
|
108
|
+
expect(@list.flatten).to eq(@list)
|
109
|
+
end
|
110
|
+
it 'list is string only' do
|
111
|
+
@list.each do |elem|
|
112
|
+
expect(elem).to be_a(String)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
describe "#bind_mounts" do
|
117
|
+
let (:bind_mounts) {
|
118
|
+
p.bind_mounts
|
119
|
+
}
|
120
|
+
it "not raise error" do
|
121
|
+
expect{bind_mounts}.not_to raise_error
|
122
|
+
end
|
123
|
+
it "include socket directory" do
|
124
|
+
expect(bind_mounts).to include(File.dirname(default_config['listen']))
|
125
|
+
end
|
126
|
+
it "include web root directories" do
|
127
|
+
# get valid open_base_dirs
|
128
|
+
default_config['php_admin_value[open_basedir]'].split(':')[1..-1].each do |dir|
|
129
|
+
expect(bind_mounts).to include(File.dirname(dir))
|
130
|
+
end
|
131
|
+
end
|
132
|
+
it "not include fake directories" do
|
133
|
+
dir = default_config['php_admin_value[open_basedir]'].split(':').first
|
134
|
+
expect(bind_mounts).not_to include(dir)
|
135
|
+
expect(bind_mounts).not_to include(File.dirname(dir))
|
136
|
+
end
|
137
|
+
end
|
138
|
+
describe "#php_command" do
|
139
|
+
before (:example) {
|
140
|
+
@list = p.php_command
|
141
|
+
}
|
142
|
+
it 'list include php cmd' do
|
143
|
+
expect(@list).to include(LAUNCHER_PHP)
|
144
|
+
end
|
145
|
+
it 'list is flat' do
|
146
|
+
expect(@list.flatten).to eq(@list)
|
147
|
+
end
|
148
|
+
it 'list is string only' do
|
149
|
+
@list.each do |elem|
|
150
|
+
expect(elem).to be_a(String)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
describe "#docker_create_opts" do
|
155
|
+
let (:docker_create_opts) {
|
156
|
+
p.docker_create_opts
|
157
|
+
}
|
158
|
+
it "not raise error" do
|
159
|
+
expect{docker_create_opts}.not_to raise_error
|
160
|
+
end
|
161
|
+
it "include php" do
|
162
|
+
expect(docker_create_opts['Image']).to eq('deadbeef')
|
163
|
+
end
|
164
|
+
end
|
165
|
+
describe "#start" do
|
166
|
+
let (:method) {
|
167
|
+
p.start
|
168
|
+
}
|
169
|
+
before (:example) {
|
170
|
+
# catch upstream calls
|
171
|
+
@spawn_list = ['spawn','spawn_args']
|
172
|
+
@php_list = ['php','php_args']
|
173
|
+
allow(p).to receive(:spawn_command).and_return(@spawn_list)
|
174
|
+
allow(p).to receive(:php_command).and_return(@php_list)
|
175
|
+
allow(p).to receive(:docker_create_opts).and_return({})
|
176
|
+
allow(p).to receive(:docker_start_opts).and_return({})
|
177
|
+
}
|
178
|
+
it "starts container" do
|
179
|
+
# container instance
|
180
|
+
dbl_c_inst = double
|
181
|
+
expect(dbl_c_inst).to receive(:start).with({})
|
182
|
+
expect(@dbl_docker_container).to receive(:create).and_return(dbl_c_inst)
|
183
|
+
method
|
184
|
+
end
|
185
|
+
it "build cmd correctly" do
|
186
|
+
# container instance
|
187
|
+
dbl_c_inst = double
|
188
|
+
allow(dbl_c_inst).to receive(:start).with({})
|
189
|
+
hash = hash_including('Cmd' => (@spawn_list + ['--'] + @php_list))
|
190
|
+
expect(@dbl_docker_container).to receive(:create).with(hash).and_return(dbl_c_inst)
|
191
|
+
method
|
192
|
+
end
|
193
|
+
it 'enables the pool' do
|
194
|
+
# container instance
|
195
|
+
dbl_c_inst = double
|
196
|
+
expect(dbl_c_inst).to receive(:start).with({})
|
197
|
+
expect(@dbl_docker_container).to receive(:create).and_return(dbl_c_inst)
|
198
|
+
method
|
199
|
+
# container class
|
200
|
+
expect(p.enabled).to eq(true)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
describe "#stop" do
|
204
|
+
it "stops existing container" do
|
205
|
+
# container instance
|
206
|
+
dbl_c_inst = double
|
207
|
+
allow(dbl_c_inst).to receive(:delete).with(hash_including(force: true))
|
208
|
+
p.instance_variable_set(:@container, dbl_c_inst)
|
209
|
+
p.stop
|
210
|
+
expect(p.enabled).to eq(false)
|
211
|
+
end
|
212
|
+
it "doesn't stop non-existing container" do
|
213
|
+
p.instance_variable_set(:@container, nil)
|
214
|
+
p.stop
|
215
|
+
expect(p.enabled).to eq(false)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
describe "#container_name" do
|
219
|
+
let(:method) {
|
220
|
+
p.instance_variable_set(:@name, 'myname')
|
221
|
+
p.container_name
|
222
|
+
}
|
223
|
+
it '@container_name not set' do
|
224
|
+
p.instance_variable_set(:@container_name, nil)
|
225
|
+
name = method
|
226
|
+
expect(name).to a_string_matching(/^myname_[a-f0-9]+$/)
|
227
|
+
expect(p.instance_variable_get(:@container_name)).to eq (name)
|
228
|
+
end
|
229
|
+
it '@container_name set' do
|
230
|
+
p.instance_variable_set(:@container_name, 'myname_cafe')
|
231
|
+
name = method
|
232
|
+
expect(name).to eq ('myname_cafe')
|
233
|
+
expect(p.instance_variable_get(:@container_name)).to eq (name)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
describe "#container_running?" do
|
237
|
+
it "container is nil" do
|
238
|
+
expect(p.container_running?).to eq(false)
|
239
|
+
end
|
240
|
+
it "container is running" do
|
241
|
+
dbl_c_inst = double
|
242
|
+
allow(dbl_c_inst).to receive(:info).and_return({'State' => { 'Running' => true }})
|
243
|
+
p.instance_variable_set(:@container, dbl_c_inst)
|
244
|
+
expect(p.container_running?).to eq(true)
|
245
|
+
end
|
246
|
+
it "container is not running" do
|
247
|
+
dbl_c_inst = double
|
248
|
+
allow(dbl_c_inst).to receive(:info).and_return({'State' => { 'Running' => false }})
|
249
|
+
p.instance_variable_set(:@container, dbl_c_inst)
|
250
|
+
expect(p.container_running?).to eq(false)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
describe "#check" do
|
254
|
+
[ # enabled, running, restart
|
255
|
+
[true,true,false],
|
256
|
+
[false,true,false],
|
257
|
+
[false,false,false],
|
258
|
+
[true,false,true],
|
259
|
+
].each do |enabled,running,restart|
|
260
|
+
it "enabled=#{enabled} and running=#{running} -> restart=#{restart}" do
|
261
|
+
allow(p).to receive(:'container_running?').and_return(running)
|
262
|
+
p.instance_variable_set(:@enabled, enabled)
|
263
|
+
|
264
|
+
# container
|
265
|
+
dbl_c_inst = double
|
266
|
+
p.instance_variable_set(:@container, dbl_c_inst)
|
267
|
+
|
268
|
+
if restart
|
269
|
+
expect(p).to receive(:stop)
|
270
|
+
expect(p).to receive(:start)
|
271
|
+
end
|
272
|
+
p.check
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
metadata
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: php_fpm_docker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christian Simon
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-25 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: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: inifile
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: coveralls
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: Use docker containers for PHP from FPM config
|
112
|
+
email:
|
113
|
+
- simon@swine.de
|
114
|
+
executables:
|
115
|
+
- php_fpm_docker
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- ".gitignore"
|
120
|
+
- ".rspec"
|
121
|
+
- ".rubocop.yml"
|
122
|
+
- ".travis.yml"
|
123
|
+
- Gemfile
|
124
|
+
- Gemfile.lock
|
125
|
+
- LICENSE.txt
|
126
|
+
- README.md
|
127
|
+
- Rakefile
|
128
|
+
- bin/php_fpm_docker
|
129
|
+
- doc/graphs/structure/structure.gliffy
|
130
|
+
- doc/graphs/structure/structure.png
|
131
|
+
- doc/graphs/structure/structure.svg
|
132
|
+
- lib/php_fpm_docker.rb
|
133
|
+
- lib/php_fpm_docker/application.rb
|
134
|
+
- lib/php_fpm_docker/launcher.rb
|
135
|
+
- lib/php_fpm_docker/pool.rb
|
136
|
+
- lib/php_fpm_docker/version.rb
|
137
|
+
- php_fpm_docker.gemspec
|
138
|
+
- spec/helper.rb
|
139
|
+
- spec/spec_helper.rb
|
140
|
+
- spec/unit/application_spec.rb
|
141
|
+
- spec/unit/launcher_spec.rb
|
142
|
+
- spec/unit/pool_spec.rb
|
143
|
+
homepage: https://github.com/simonswine/php_fpm_docker
|
144
|
+
licenses:
|
145
|
+
- GPLv3
|
146
|
+
metadata: {}
|
147
|
+
post_install_message:
|
148
|
+
rdoc_options: []
|
149
|
+
require_paths:
|
150
|
+
- lib
|
151
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
152
|
+
requirements:
|
153
|
+
- - ">="
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: '0'
|
156
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
requirements: []
|
162
|
+
rubyforge_project:
|
163
|
+
rubygems_version: 2.4.3
|
164
|
+
signing_key:
|
165
|
+
specification_version: 4
|
166
|
+
summary: Use docker containers for PHP from FPM config
|
167
|
+
test_files:
|
168
|
+
- spec/helper.rb
|
169
|
+
- spec/spec_helper.rb
|
170
|
+
- spec/unit/application_spec.rb
|
171
|
+
- spec/unit/launcher_spec.rb
|
172
|
+
- spec/unit/pool_spec.rb
|