fulmar 1.10.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.travis.yml +11 -4
- data/README.md +87 -102
- data/fulmar.gemspec +2 -2
- data/lib/fulmar/domain/{service/application_service.rb → model/application.rb} +16 -2
- data/lib/fulmar/domain/model/configuration.rb +185 -0
- data/lib/fulmar/domain/service/config_test_service.rb +32 -114
- data/lib/fulmar/domain/service/config_tests/hosts.rb +23 -0
- data/lib/fulmar/domain/service/config_tests/mariadb.rb +19 -0
- data/lib/fulmar/domain/service/config_tests/paths.rb +15 -0
- data/lib/fulmar/domain/service/config_tests/project.rb +5 -0
- data/lib/fulmar/domain/service/configuration_service.rb +13 -131
- data/lib/fulmar/domain/service/file_sync_service.rb +7 -7
- data/lib/fulmar/domain/service/helper/common_helper.rb +18 -32
- data/lib/fulmar/domain/service/plugin_service.rb +60 -0
- data/lib/fulmar/domain/service/{config_rendering_service.rb → template_rendering_service.rb} +3 -3
- data/lib/fulmar/domain/task/configuration.rake +9 -7
- data/lib/fulmar/domain/task/console.rake +3 -3
- data/lib/fulmar/domain/task/environment.rake +5 -12
- data/lib/fulmar/domain/task/initialization/base.rake +3 -9
- data/lib/fulmar/domain/task/versions.rake +8 -12
- data/lib/fulmar/infrastructure/default_files/Fulmar/project.config.yml +26 -0
- data/lib/fulmar/infrastructure/default_files/Fulmarfile +22 -0
- data/lib/fulmar/infrastructure/{service → model}/transfer/base.rb +4 -4
- data/lib/fulmar/infrastructure/{service → model}/transfer/rsync.rb +4 -2
- data/lib/fulmar/infrastructure/{service → model}/transfer/rsync_with_versions.rb +18 -20
- data/lib/fulmar/infrastructure/{service → model}/transfer/tar.rb +2 -2
- data/lib/fulmar/infrastructure/service/copy_service.rb +2 -2
- data/lib/fulmar/infrastructure/service/ssh_config_service.rb +8 -21
- data/lib/fulmar/service/bootstrap_service.rb +1 -5
- data/lib/fulmar/task_manager.rb +2 -8
- data/lib/fulmar/version.rb +1 -1
- data/spec/lib/fulmar/model/configuration_spec.rb +193 -0
- data/spec/lib/fulmar/service/plugin_service_spec.rb +29 -0
- metadata +29 -34
- data/lib/fulmar/domain/service/helper/database_helper.rb +0 -14
- data/lib/fulmar/domain/service/helper/flow_helper.rb +0 -16
- data/lib/fulmar/domain/service/helper/vhost_helper.rb +0 -21
- data/lib/fulmar/domain/task/database_sync.rake +0 -63
- data/lib/fulmar/infrastructure/service/composer_service.rb +0 -22
- data/lib/fulmar/infrastructure/service/database/database_service.rb +0 -118
- data/lib/fulmar/infrastructure/service/flow_service.rb +0 -47
- data/lib/fulmar/infrastructure/service/shell_service.rb +0 -10
- data/lib/fulmar/service/logger_service.rb +0 -7
- data/test/rsync_with_versions.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8d6270f2a2b407f32d9419cd4bb55c228138b8e
|
4
|
+
data.tar.gz: 2c820de490c174e1c7a0df5db92434fe1a3f3302
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 308c77aa59ef61e622a7988c5bbafb86b76694408d5d6b112e311813a9175c34260aff4954449f358e3722de481ff3570ac79c3e21d6c3ef904be72fbab9ada9
|
7
|
+
data.tar.gz: ae114e84ce4907f2feefe402f1409cbbfe76497c3e47dc15dbe676964df877150bf22f14dd7c763963fe4be954e9997e571c32979a7457c7d067a0ceb7255966
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,11 +1,18 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 2.
|
4
|
-
- 2.1
|
5
|
-
- 2.
|
3
|
+
- 2.2.2
|
4
|
+
- 2.3.1
|
5
|
+
- 2.4.0
|
6
6
|
- ruby-head
|
7
|
-
before_install: gem install bundler -v 1.10.6
|
8
7
|
matrix:
|
9
8
|
fast_finish: true
|
10
9
|
allow_failures:
|
11
10
|
- rvm: ruby-head
|
11
|
+
|
12
|
+
before_script:
|
13
|
+
- bundle update
|
14
|
+
|
15
|
+
script:
|
16
|
+
- git config --global user.email "you@example.com"
|
17
|
+
- git config --global user.name "Whatever Name"
|
18
|
+
- bundle exec rspec spec
|
data/README.md
CHANGED
@@ -12,28 +12,12 @@ the cache and the publish via a symlink. This avoids an inconsistent state on th
|
|
12
12
|
machine and allows a quick revert to the old version, as long as other dependencies are
|
13
13
|
compatible (i.e. database).
|
14
14
|
|
15
|
-
It has (yet limited) support for MySQL / MariaDB and git.
|
16
|
-
through an ssh tunnel.
|
17
|
-
|
18
|
-
## Warning
|
19
|
-
|
20
|
-
Version 1.10.0 of Fulmar removes some features that we (CORE4) rarely used and which
|
21
|
-
caused the deployments to slow down the gems needed to be compiled against the
|
22
|
-
installed libraries. If you need support to query databases (more than just dumps)
|
23
|
-
or use dependencies, you will need to explicitly specify version `~> 1.9.0`.
|
24
|
-
Fulmar 2.0 will support these features via plugins.
|
15
|
+
It has (yet limited) support for MySQL / MariaDB and git.
|
25
16
|
|
26
17
|
## Prerequisites
|
27
18
|
|
28
|
-
Fulmar
|
29
|
-
|
30
|
-
libmariadbclient-dev/libmysqlclient-dev or similar.
|
31
|
-
|
32
|
-
You also need cmake and pkg-config to build the dependencies.
|
33
|
-
|
34
|
-
- OSX: brew install mariadb cmake pkg-config
|
35
|
-
- Ubuntu: apt-get install libmariadb-client-lgpl-dev build-essential cmake
|
36
|
-
- Ubuntu (pre-xenial): apt-get install libmariadbclient-dev build-essential cmake
|
19
|
+
Fulmar 2.0 runs with Ruby >= 2.2.2. Plugins like [mariadb](https://github.com/CORE4/fulmar-plugin-mariadb) might have
|
20
|
+
additional requirements.
|
37
21
|
|
38
22
|
## Installation
|
39
23
|
|
@@ -57,20 +41,24 @@ Fulmar works like Rake. In fact it is just a collection of services on top of Ra
|
|
57
41
|
|
58
42
|
## A simple start
|
59
43
|
|
60
|
-
Start by creating a file named "Fulmarfile" in your project root. Then add a Folder "Fulmar" with a project.config.yml
|
44
|
+
Start by creating a file named "Fulmarfile" in your project root. Then add a Folder "Fulmar" with a project.config.yml
|
45
|
+
in it. The name of the config file doesn't matter, it just needs to end with .config.yml. All \*.config.yml in the
|
46
|
+
Fulmar directory are merged so you can split you configuration into
|
61
47
|
several files.
|
62
48
|
|
63
49
|
$ touch Fulmarfile
|
64
50
|
$ mkdir Fulmar
|
65
51
|
$ touch Fulmar/project.config.yml
|
66
52
|
|
67
|
-
You can now test your configuration.
|
53
|
+
You can now now test your configuration.
|
68
54
|
|
69
55
|
$ fulmar test:config
|
70
56
|
|
71
|
-
Everything should be fine. With `fulmar -T` you can get a quick overview on the available tasks. That is probably just
|
57
|
+
Everything should be fine. With `fulmar -T` you can get a quick overview on the available tasks. That is probably just
|
58
|
+
one, the fulmar console. We'll come back to this later.
|
72
59
|
|
73
|
-
Now let's start with a first configuration. You might want to have a server ready which you can access via ssh and
|
60
|
+
Now let's start with a first configuration. You might want to have a server ready which you can access via ssh and
|
61
|
+
public key authentication. Add this to you configuration file (Fulmar/project.config.yml):
|
74
62
|
|
75
63
|
```yaml
|
76
64
|
environments:
|
@@ -85,15 +73,25 @@ environments:
|
|
85
73
|
delete: false
|
86
74
|
```
|
87
75
|
|
88
|
-
Now you can run a second test: `fulmar test:hosts`. This will test the ssh connection to your remote machine. You can
|
76
|
+
Now you can run a second test: `fulmar test:hosts`. This will test the ssh connection to your remote machine. You can
|
77
|
+
add a username to the ssh configuration in the files section (below the hostname). However, if you need a finer
|
78
|
+
grained host configuration, you should use your
|
79
|
+
[ssh config file](http://www.cyberciti.biz/faq/create-ssh-config-file-on-linux-unix/).
|
89
80
|
|
90
|
-
Most fulmar tasks need to run within a configured environment. Within the configuration, environments are split into
|
81
|
+
Most fulmar tasks need to run within a configured environment. Within the configuration, environments are split into
|
82
|
+
targets. So actually, a task works within a target. In our example, we set up an environment named 'my_server' with
|
83
|
+
a target named 'files'. You might want to use one environment for your development server, one for a preview server
|
84
|
+
and one for the production machine. Within each environment you might have different hosts for the file and the
|
85
|
+
database, so each environment can be split up into multiple targets (e.g. 'files' and 'database').
|
91
86
|
|
92
|
-
There is one special environment 'all' which contains settings you want to use in all targets of all environements.
|
87
|
+
There is one special environment 'all' which contains settings you want to use in all targets of all environements.
|
88
|
+
These globally configured settings are overwritten with the specific ones from a target if they exist. Useful examples
|
89
|
+
for this are 'local_path' and 'debug'.
|
93
90
|
|
94
91
|
### Tasks
|
95
92
|
|
96
|
-
As you can see (`fulmar -T`), you cannot do anything more useful now. So it is time to write a first task. If you are
|
93
|
+
As you can see (`fulmar -T`), you cannot do anything more useful now. So it is time to write a first task. If you are
|
94
|
+
familiar with rake, this should not be a problem.
|
97
95
|
|
98
96
|
```ruby
|
99
97
|
namespace :deploy do
|
@@ -105,14 +103,17 @@ namespace :deploy do
|
|
105
103
|
end
|
106
104
|
```
|
107
105
|
|
108
|
-
This creates a task 'test' within the namespace 'deploy', which means you can access the task by running
|
106
|
+
This creates a task 'test' within the namespace 'deploy', which means you can access the task by running
|
107
|
+
`fulmar deploy:test`. The task will load the configuration for 'myserver/files' and start a file sync (via rsync) to
|
108
|
+
your remote host.
|
109
109
|
|
110
110
|
If you log in to your remote machine, you should now see the Fulmar directory and the Fulmarfile in /tmp.
|
111
111
|
```bash
|
112
112
|
ssh my-ssh-server "ls -l /tmp | grep Fulmar"
|
113
113
|
```
|
114
114
|
|
115
|
-
This task can be even shorter. Fulmar creates some helper tasks automatically from your configuration. Just like in
|
115
|
+
This task can be even shorter. Fulmar creates some helper tasks automatically from your configuration. Just like in
|
116
|
+
rake, you can list them via `fulmar -T -A`:
|
116
117
|
```
|
117
118
|
fulmar console # Open an interactive terminal within fulmar
|
118
119
|
fulmar deploy:test # Deploy to test system
|
@@ -122,7 +123,8 @@ fulmar test:config #
|
|
122
123
|
fulmar test:hosts #
|
123
124
|
```
|
124
125
|
|
125
|
-
Fulmar creates task for every environment/target with this scheme. So you can just set a dependency to these tasks to
|
126
|
+
Fulmar creates task for every environment/target with this scheme. So you can just set a dependency to these tasks to
|
127
|
+
load the configuration. Your task now only needs one line:
|
126
128
|
|
127
129
|
```ruby
|
128
130
|
namespace :deploy do
|
@@ -139,11 +141,15 @@ task :test => ['environment:my_server:files', 'prepare_files', 'do_some_more'] d
|
|
139
141
|
end
|
140
142
|
```
|
141
143
|
|
142
|
-
If you call one task from another, your configuration will be given to the subtask. If you change the target in the
|
143
|
-
|
144
|
+
If you call one task from another, your configuration will be given to the subtask. If you change the target in the
|
145
|
+
subtask it will be reverted when returning to your main task unless you did not set any configuration in the main task.
|
146
|
+
This means the following:
|
147
|
+
- You can use the dependency to a 'environment:' task to set the target initially (as it will be the first subtask to
|
148
|
+
run)
|
144
149
|
- You can call a subtask and and you don't have to worry it might change your target.
|
145
150
|
|
146
|
-
*The configuration object is a singleton. So if you change the actual configuration of a target, it will affect other
|
151
|
+
*The configuration object is a singleton. So if you change the actual configuration of a target, it will affect other
|
152
|
+
tasks*
|
147
153
|
|
148
154
|
## Features
|
149
155
|
|
@@ -151,7 +157,8 @@ So now you can deploy the Fulmarfiles itself which is nice. So what else can you
|
|
151
157
|
|
152
158
|
### Shell
|
153
159
|
|
154
|
-
Very often you need to call other programs or script from a shell to compile your assets or clear the cache. Within a
|
160
|
+
Very often you need to call other programs or script from a shell to compile your assets or clear the cache. Within a
|
161
|
+
task, you can access the local and remote shell:
|
155
162
|
|
156
163
|
```ruby
|
157
164
|
task :tutorial => 'environment:my_server:files' do
|
@@ -161,7 +168,8 @@ task :tutorial => 'environment:my_server:files' do
|
|
161
168
|
end
|
162
169
|
```
|
163
170
|
|
164
|
-
You can run multiple commands. If one fails, everything stops immediately. This is intended to stop the deployment of
|
171
|
+
You can run multiple commands. If one fails, everything stops immediately. This is intended to stop the deployment of
|
172
|
+
broken files.
|
165
173
|
|
166
174
|
```ruby
|
167
175
|
task :tutorial => 'environment:my_server:files' do
|
@@ -188,7 +196,8 @@ end
|
|
188
196
|
|
189
197
|
### File synchronisation
|
190
198
|
|
191
|
-
At the moment, there are two ways to synchronize your files to a remote host. The basic rsync and "rsync with versions".
|
199
|
+
At the moment, there are two ways to synchronize your files to a remote host. The basic rsync and "rsync with versions".
|
200
|
+
You already know how to set up the simple file sync. You can add a few options, if you like:
|
192
201
|
|
193
202
|
```yaml
|
194
203
|
environments:
|
@@ -210,16 +219,20 @@ environments:
|
|
210
219
|
```
|
211
220
|
|
212
221
|
These are:
|
213
|
-
- **delete**: true/false, Should additional files be deleted from the remote host? The default is "true", as you don't
|
222
|
+
- **delete**: true/false, Should additional files be deleted from the remote host? The default is "true", as you don't
|
223
|
+
want to have legacy files opening security holes on your server.
|
214
224
|
- **exclude**: regexp, which works with "--exclude" from rsync. Usually, you should prefer the next option
|
215
|
-
- **exclude_file**: Filename of the exclude file relative to "local_path". If you have a file called .rsyncignore in
|
225
|
+
- **exclude_file**: Filename of the exclude file relative to "local_path". If you have a file called .rsyncignore in
|
226
|
+
"local_path" this will be used automatically.
|
216
227
|
- **chown**: chown files on remote machine to this user
|
217
228
|
- **chmod**: change mode of files on remote machines
|
218
|
-
- **direction**: up/down, Should the files be uploaded or downloaded? The latter is useful if you want to download
|
229
|
+
- **direction**: up/down, Should the files be uploaded or downloaded? The latter is useful if you want to download
|
230
|
+
images created by a cms on the remote machine.
|
219
231
|
|
220
232
|
### rsync with versions
|
221
233
|
|
222
|
-
When deploying to a production machine, you want your minimize the downtime for the website. Fulmar can help by syncing
|
234
|
+
When deploying to a production machine, you want your minimize the downtime for the website. Fulmar can help by syncing
|
235
|
+
different versions which are symlinked after they are set up.
|
223
236
|
|
224
237
|
```yaml
|
225
238
|
environments:
|
@@ -238,13 +251,20 @@ environments:
|
|
238
251
|
#shared_dir: shared
|
239
252
|
```
|
240
253
|
|
241
|
-
You do no necessarily need to set "limit_releases" or "shared". "temp_dir", "releases_dir" and "shared_dir" are
|
254
|
+
You do no necessarily need to set "limit_releases" or "shared". "temp_dir", "releases_dir" and "shared_dir" are
|
255
|
+
relative to the remote_path and usually do not need to be changed.
|
242
256
|
|
243
257
|
So what does this all mean?
|
244
258
|
|
245
|
-
If you still have your deploy:test task you can now deploy a first version of your files. Probably create the "data"
|
259
|
+
If you still have your deploy:test task you can now deploy a first version of your files. Probably create the "data"
|
260
|
+
dir first and put a file in to see what happens. Fulmar will sync you project into a temporary directory ("temp_dir")
|
261
|
+
and then copy it the the releases directory with the current timestamp as a directory name. So in this case, a directory
|
262
|
+
like "/tmp/releases/2015-04-27_123456" will be created. Fulmar will then look of shared/data exists and if not, copy it
|
263
|
+
from the releases directory. Shared directories in releases will be deleted an symlinked to shared. So you want to
|
264
|
+
list all directories which are filled by you web application (images uploaded from the cms, etc).
|
246
265
|
|
247
|
-
Then you can build up the cache or whatever needs to be done within that directory before putting it live. This last
|
266
|
+
Then you can build up the cache or whatever needs to be done within that directory before putting it live. This last
|
267
|
+
step is not yet covered in our little deployment task. So:
|
248
268
|
|
249
269
|
```ruby
|
250
270
|
namespace :deploy do
|
@@ -260,75 +280,26 @@ namespace :deploy do
|
|
260
280
|
end
|
261
281
|
```
|
262
282
|
|
263
|
-
The transfer() method of the file_sync service returns the created release dir and you can do what needs to be done
|
264
|
-
|
265
|
-
As you probably figured out, your webserver uses the "current" symlink to get the base directory of your web application. Here, you would point it to /tmp/current/public or something like that.
|
266
|
-
|
267
|
-
### Database access
|
268
|
-
|
269
|
-
Within a task you can access and update databases (mariadb/mysql at the time of writing). Remote databases do not need to be accessible directly since fulmar uses an ssh tunnel to connect to the host first. So the database host is often just "localhost" (the default value). You can specify a different host if you database server is on another host, which is the case on most simple web spaces.
|
270
|
-
|
271
|
-
The field "maria:database" is required for type "maria". The other fields are optional, though you probably need the fields "user" and "password". Below you can see the default values for the optional fields.
|
272
|
-
|
273
|
-
```yaml
|
274
|
-
environments:
|
275
|
-
staging:
|
276
|
-
database:
|
277
|
-
host: project-staging
|
278
|
-
type: maria
|
279
|
-
maria:
|
280
|
-
database: db_name
|
281
|
-
user: root
|
282
|
-
password:
|
283
|
-
port: 3306
|
284
|
-
host: localhost
|
285
|
-
encoding: utf-8
|
286
|
-
```
|
287
|
-
|
288
|
-
```ruby
|
289
|
-
require 'pp'
|
290
|
-
|
291
|
-
task :database => 'environment:staging:database' do
|
292
|
-
database.create 'test_db'
|
293
|
-
database.clear # deletes all tables within the database
|
294
|
-
remote_file_name = database.dump # dumps the database to the returned file
|
295
|
-
database.load_dump(remote_file_name) # loads an sql dump
|
296
|
-
local_filename = database.download_dump # downloads an sql dump to your machine
|
297
|
-
end
|
298
|
-
```
|
283
|
+
The transfer() method of the file_sync service returns the created release dir and you can do what needs to be done
|
284
|
+
before calling file_sync.publish to create the symlink (current => releases/2015-04-27_123456).
|
299
285
|
|
300
|
-
|
301
|
-
|
302
|
-
```ruby
|
303
|
-
results = database.query 'SELECT name, email FROM users'
|
304
|
-
results.each do |row|
|
305
|
-
puts "#{row['name']} <#{row['email']}>"
|
306
|
-
end
|
307
|
-
```
|
308
|
-
|
309
|
-
You can use all features of the mysql2 gem via `database.client`.
|
310
|
-
|
311
|
-
If you configured more than one database on different environments, fulmar will
|
312
|
-
create task to sync these databases via mysql_dump. This allows you to update a
|
313
|
-
staging or preview database with the data from the production system.
|
314
|
-
|
315
|
-
```
|
316
|
-
fulmar database:update:preview:from_live
|
317
|
-
```
|
318
|
-
|
319
|
-
The task to copy data *to* the live database is hidden (it has no description).
|
286
|
+
As you probably figured out, your webserver uses the "current" symlink to get the base directory of your web
|
287
|
+
application. Here, you would point it to /tmp/current/public or something like that.
|
320
288
|
|
321
289
|
### Configuration templates
|
322
290
|
|
323
|
-
Sometimes you need different versions of files on your different environments. An exmaple might be the .htaccess file.
|
291
|
+
Sometimes you need different versions of files on your different environments. An exmaple might be the .htaccess file.
|
292
|
+
You can use the [Ruby ERB templating engine](http://www.stuartellis.eu/articles/erb/) to generate the different
|
293
|
+
versions. The configuration of your environment are available via the `@config` variable.
|
324
294
|
|
325
|
-
Templates need to be named with the schema `<original_filename>.erb`. All files you want to render need to be listed in
|
295
|
+
Templates need to be named with the schema `<original_filename>.erb`. All files you want to render need to be listed in
|
296
|
+
the configuration:
|
326
297
|
|
327
298
|
```yaml
|
328
299
|
environments:
|
329
300
|
staging:
|
330
301
|
files:
|
331
|
-
|
302
|
+
templates:
|
332
303
|
- .htaccess.erb
|
333
304
|
application_environment: Production/Live
|
334
305
|
```
|
@@ -338,3 +309,17 @@ Then add the variable to your template:
|
|
338
309
|
```
|
339
310
|
SetEnv APPLICATION_ENVIRONMENT "<%= @config['application_environment'] %>"
|
340
311
|
```
|
312
|
+
|
313
|
+
## Plugins
|
314
|
+
|
315
|
+
Fulmar can be extended with plugins.
|
316
|
+
|
317
|
+
```yaml
|
318
|
+
plugins:
|
319
|
+
mariadb:
|
320
|
+
# Put mariadb plugin configuration here
|
321
|
+
git:
|
322
|
+
# Put git plugin configuration here
|
323
|
+
```
|
324
|
+
|
325
|
+
If you want to add your own plugin, have a look at the [example plugin](https://github.com/CORE4/fulmar-plugin-example).
|
data/fulmar.gemspec
CHANGED
@@ -20,8 +20,8 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_development_dependency 'bundler', '~> 1.7'
|
22
22
|
|
23
|
-
spec.add_runtime_dependency 'rake', '~>
|
23
|
+
spec.add_runtime_dependency 'rake', '~>11'
|
24
24
|
spec.add_runtime_dependency 'fulmar-shell', '~>1', '>=1.7.0'
|
25
|
-
spec.add_runtime_dependency 'ruby_wings', '~>0.1', '>=0.1.1'
|
26
25
|
spec.add_runtime_dependency 'colorize', '~>0'
|
26
|
+
spec.add_runtime_dependency 'activesupport', '~> 5.0'
|
27
27
|
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'rake'
|
2
|
+
require 'fulmar/domain/service/plugin_service'
|
2
3
|
|
3
4
|
module Fulmar
|
4
5
|
module Domain
|
5
6
|
module Service
|
6
7
|
# The main application which extends rake
|
7
|
-
class
|
8
|
+
class Application < Rake::Application
|
8
9
|
def initialize
|
9
10
|
super
|
10
11
|
@rakefiles = %w(fulmarfile Fulmarfile fulmarfile.rb Fulmarfile.rb)
|
@@ -33,6 +34,9 @@ module Fulmar
|
|
33
34
|
glob("#{fulmar_task_dir}/initialization/*.rake") do |name|
|
34
35
|
Rake.load_rakefile name
|
35
36
|
end
|
37
|
+
Fulmar::Domain::Service::PluginService.instance.rake_files.each do |name|
|
38
|
+
Rake.load_rakefile name
|
39
|
+
end
|
36
40
|
super
|
37
41
|
end
|
38
42
|
|
@@ -62,10 +66,20 @@ module Fulmar
|
|
62
66
|
'-V',
|
63
67
|
'Display the program version.',
|
64
68
|
lambda do |_value|
|
65
|
-
puts "fulmar #{Fulmar::VERSION} (using rake, version #{
|
69
|
+
puts "fulmar #{Fulmar::VERSION} (using rake, version #{Rake::VERSION})"
|
66
70
|
exit
|
67
71
|
end
|
68
72
|
]
|
73
|
+
options << [
|
74
|
+
'--debug',
|
75
|
+
nil,
|
76
|
+
'Run in debug mode.',
|
77
|
+
lambda do |_value|
|
78
|
+
configuration = Fulmar::Domain::Service::ConfigurationService.instance
|
79
|
+
configuration.debug = true
|
80
|
+
end
|
81
|
+
]
|
82
|
+
options
|
69
83
|
end
|
70
84
|
end
|
71
85
|
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'fulmar/domain/model/project'
|
3
|
+
require 'active_support/core_ext/object/blank'
|
4
|
+
require 'active_support/core_ext/hash/deep_merge'
|
5
|
+
require 'active_support/hash_with_indifferent_access'
|
6
|
+
|
7
|
+
module Fulmar
|
8
|
+
module Domain
|
9
|
+
module Model
|
10
|
+
# Loads and prepares the configuration from the yaml file
|
11
|
+
class Configuration
|
12
|
+
attr_accessor :environment, :target, :base_path
|
13
|
+
attr_reader :debug
|
14
|
+
|
15
|
+
def initialize(data, base_path = '', debug = false)
|
16
|
+
@data = data
|
17
|
+
@base_path = base_path
|
18
|
+
@debug = debug
|
19
|
+
prepare_data
|
20
|
+
end
|
21
|
+
|
22
|
+
# Allow access of configuration via array/hash access methods (read access)
|
23
|
+
def [](id)
|
24
|
+
ready? ? @data[:environments][@environment][@target][id] : nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# Allow access of configuration via array/hash access methods (write access)
|
28
|
+
def []=(id, value)
|
29
|
+
if ready?
|
30
|
+
@data[:environments][@environment][@target][id] = value
|
31
|
+
else
|
32
|
+
fail 'Environment or target not set. Please set both variables via configuration.environment = \'xxx\' / '\
|
33
|
+
'configuration.target = \'yyy\''
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Set the environment and target in one call
|
38
|
+
def set(environment, target = nil)
|
39
|
+
# For convenience, allow something like "environment:target" as string
|
40
|
+
if environment.class == String
|
41
|
+
fields = environment.split(':')
|
42
|
+
@environment = fields.first.to_sym
|
43
|
+
@target = fields.size > 1 ? fields[1].to_sym : nil
|
44
|
+
else
|
45
|
+
@environment = environment
|
46
|
+
@target = target unless target.nil?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Checks if environment and target are set
|
51
|
+
def ready?
|
52
|
+
return false if @environment.nil? || @target.nil?
|
53
|
+
fail 'Environment is invalid' if @data[:environments][@environment].nil?
|
54
|
+
fail 'Target is invalid' if @data[:environments][@environment][@target].nil?
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
# Return the project
|
59
|
+
def project
|
60
|
+
@project ||= Fulmar::Domain::Model::Project.new(@data[:project])
|
61
|
+
end
|
62
|
+
|
63
|
+
def plugins
|
64
|
+
@data[:plugins] || {}
|
65
|
+
end
|
66
|
+
|
67
|
+
# Allows iterating over all targets from all configured environments
|
68
|
+
def each
|
69
|
+
@data[:environments].each_key do |env|
|
70
|
+
@data[:environments][env].each_pair do |target, data|
|
71
|
+
yield(env, target, data)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Return the combined user and host
|
77
|
+
# @todo Is there another way to do this?
|
78
|
+
def ssh_user_and_host
|
79
|
+
self[:user].blank? ? self[:hostname] : self[:user] + '@' + self[:hostname]
|
80
|
+
end
|
81
|
+
|
82
|
+
# Handle dependencies
|
83
|
+
# @todo Refactor this to work with the dependencies plugin
|
84
|
+
def dependencies(env = nil)
|
85
|
+
if env.nil? || !@data[:dependencies].key?(env)
|
86
|
+
@data[:dependencies][:all]
|
87
|
+
else
|
88
|
+
@data[:dependencies][:all].deep_merge(@data[:dependencies][env])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Allow access to host list
|
93
|
+
def hosts
|
94
|
+
@data[:hosts]
|
95
|
+
end
|
96
|
+
|
97
|
+
# Check for a feature
|
98
|
+
# @todo Do we still need this? Maybe replace this with a "plugin?" method?
|
99
|
+
def feature?(feature)
|
100
|
+
return @data[:features].include? feature.to_s unless @data[:features].nil?
|
101
|
+
case feature
|
102
|
+
when :maria
|
103
|
+
key? :maria
|
104
|
+
else
|
105
|
+
false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Checks if a configuration key exists in one of the targets
|
110
|
+
def key?(key)
|
111
|
+
each { |_env, _target, data| return true unless data[key].nil? }
|
112
|
+
false
|
113
|
+
end
|
114
|
+
|
115
|
+
# Merge another configuration into the currently active one
|
116
|
+
# Useful for supplying a default configuration, as values are not overwritten.
|
117
|
+
# Hashes are merged.
|
118
|
+
# @param [Hash] other
|
119
|
+
def merge(other)
|
120
|
+
return unless @environment && @target
|
121
|
+
@data[:environments][@environment][@target] = other.deep_merge(@data[:environments][@environment][@target])
|
122
|
+
end
|
123
|
+
|
124
|
+
protected
|
125
|
+
|
126
|
+
# Prepares the configuration
|
127
|
+
def prepare_data
|
128
|
+
handle_inheritance
|
129
|
+
merge_hosts
|
130
|
+
absolutize_paths
|
131
|
+
end
|
132
|
+
|
133
|
+
# Merges the :all-configuration into targets.
|
134
|
+
# [:environments][:all] into *all* targets
|
135
|
+
# [:environments][:something][:all] into all targets from environment :something
|
136
|
+
def handle_inheritance
|
137
|
+
global_config = @data[:environments].delete(:all) || {}
|
138
|
+
environments = @data[:environments].keys
|
139
|
+
environments.each do |env|
|
140
|
+
environment_config = @data[:environments][env].delete(:all) || {}
|
141
|
+
targets = @data[:environments][env].keys
|
142
|
+
targets.each do |target|
|
143
|
+
local_config = @data[:environments][env][target] || {}
|
144
|
+
@data[:environments][env][target] = global_config.deep_merge(environment_config).deep_merge(local_config)
|
145
|
+
@data[:environments][env][target][:debug] = @debug
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Merges the host configuration into the targets referring to it
|
151
|
+
def merge_hosts
|
152
|
+
each do |env, target, data|
|
153
|
+
next if data[:host].nil?
|
154
|
+
host = data[:host].to_sym
|
155
|
+
@data[:environments][env][target] = @data[:hosts][host].deep_merge(data) unless @data[:hosts][host].nil?
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Prepends the base to the path if it is not already absolute
|
160
|
+
def absolutize(path, base = @base_path)
|
161
|
+
return base if path == '.'
|
162
|
+
path[0, 1] == '/' ? path : base + '/' + path
|
163
|
+
end
|
164
|
+
|
165
|
+
# Checks if a key is a local path
|
166
|
+
def local_path?(key)
|
167
|
+
key.to_s.split('_').last == 'path' && !key.to_s.include?('remote')
|
168
|
+
end
|
169
|
+
|
170
|
+
# Check all keys ending with '_path' and prepend either the
|
171
|
+
# @base_path or the local_path from the environment
|
172
|
+
def absolutize_paths
|
173
|
+
each do |_env, _target, data|
|
174
|
+
data.keys.each do |key|
|
175
|
+
data[:local_path] = absolutize(data[:local_path]) if data[:local_path]
|
176
|
+
if local_path?(key) && data[key].class == String
|
177
|
+
data[key] = absolutize(data[key], data[:local_path] || @base_path)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|