mamiya 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -15
- data/docs/quick_start.md +212 -0
- data/lib/mamiya/cli/client.rb +3 -5
- data/lib/mamiya/storages/s3.rb +16 -1
- data/lib/mamiya/version.rb +1 -1
- data/spec/storages/s3_spec.rb +19 -3
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3fff2664bac4156374129f9094fe5fc7459d3b8b
|
4
|
+
data.tar.gz: efe0195812cbb482507dae05cacbb05cf8ca57a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed7ec72684c57cd099a95e2c66bca126637dd8837eb5c66ea2b9b74fc6a289b87acf041359289319ff79684ed3e6e3ebd56266d3facf1d14b21dd80cebb86083
|
7
|
+
data.tar.gz: aa23567e2219203fb190e5436398c5ae934baceb0ced7dcb7e4e1c226cfeb83c404c32fbb617d358fa37e9e31657556352e8f89caaf9a4384e02ed9aec1206b1
|
data/README.md
CHANGED
@@ -2,23 +2,17 @@
|
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/sorah/mamiya.png?branch=master)](https://travis-ci.org/sorah/mamiya)
|
4
4
|
|
5
|
-
Mamiya allows you to deploy using without ssh -- using tarballs, some storages (S3, etc), and [Serf](http://www.serfdom.io/).
|
6
5
|
|
7
|
-
##
|
6
|
+
## What's mamiya?
|
8
7
|
|
9
|
-
(
|
8
|
+
Mamiya allows you to deploy without ssh -- using tarballs, some storages (S3, etc), and [Serf](http://www.serfdom.io/).
|
9
|
+
Build application package on CI or somewhere, then distribute earlier (before you say "Deploy!"). You can switch to a new application quickly, when you're saying "deploy."
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
4. Write deploy script for your own
|
17
|
-
- how to build package, how to prepare release, how to release, etc.
|
18
|
-
5. Build then push package
|
19
|
-
- `mamiya build`
|
20
|
-
- `mamiya push path/to/package.tar.gz`
|
21
|
-
6. Time to deploy: `mamiya client deploy -a app package`
|
11
|
+
Mamiya uses similar directory structure with Capistrano 'deploy_to' -- you can try this easy.
|
12
|
+
|
13
|
+
## Quick Start
|
14
|
+
|
15
|
+
See [./docs/quick_start.md](./docs/quick_start.md).
|
22
16
|
|
23
17
|
### Example configuration
|
24
18
|
|
@@ -30,7 +24,7 @@ Try Mamiya in your local machine: `foreman start`
|
|
30
24
|
Existing major deploy tools (capistrano, mina, ...) use SSH to operate servers.
|
31
25
|
But connecting to lot of servers via SSH makes deployment slow.
|
32
26
|
|
33
|
-
|
27
|
+
Mamiya solves such problem by using [Serf](http://www.serfdom.io/) and tarball on one storage (Amazon S3).
|
34
28
|
|
35
29
|
Also, I'm planning to allow to distribute files before the deploy command. I guess this can skip or shorten
|
36
30
|
file transferring phase in deploy.
|
data/docs/quick_start.md
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
# Quick Start
|
2
|
+
|
3
|
+
## What's mamiya?
|
4
|
+
|
5
|
+
Mamiya allows you to deploy using without ssh -- using tarballs, some storages (S3, etc), and [Serf](http://www.serfdom.io/).
|
6
|
+
Build application package on CI or somewhere, then distribute earlier (before you say "Deploy!"). You can switch to an new application quickly, when you're saying "deploy."
|
7
|
+
|
8
|
+
Also, this free you from SSH and RSync problems.
|
9
|
+
|
10
|
+
## Concepts
|
11
|
+
|
12
|
+
- __Package__ is a tarball contains your application.
|
13
|
+
- __Storage__ is a place to store package.
|
14
|
+
- __Script__ is a Ruby script that defines how to deploy (prepare, restart, etc) your application.
|
15
|
+
|
16
|
+
- __CI (or, somewhere)__ builds packages and push them on _Storage_
|
17
|
+
- __Master__ controls the cluster of _Agents_
|
18
|
+
- __Agent__ receives deployment and do it.
|
19
|
+
|
20
|
+
## Prepare master and agent
|
21
|
+
|
22
|
+
Run `sudo gem install mamiya` to install mamiya on your systems.
|
23
|
+
|
24
|
+
(TODO: write init.d/systemd/upstart script for easier boot up)
|
25
|
+
|
26
|
+
### Master
|
27
|
+
|
28
|
+
On a server for master node; it'll controll the cluster:
|
29
|
+
|
30
|
+
#### Configure
|
31
|
+
|
32
|
+
typical configuration for the master here:
|
33
|
+
|
34
|
+
``` ruby
|
35
|
+
# /etc/mamiya/config.rb
|
36
|
+
|
37
|
+
set :storage, {
|
38
|
+
type: :s3,
|
39
|
+
bucket: 'YOUR-S3-BUCKET-NAME',
|
40
|
+
region: 'ap-northeast-1', # set it to your region
|
41
|
+
access_key_id: '...',
|
42
|
+
secret_access_key: '...',
|
43
|
+
}
|
44
|
+
|
45
|
+
set :serf, {
|
46
|
+
agent: {
|
47
|
+
bind: "0.0.0.0:7760",
|
48
|
+
rpc_addr: "127.0.0.1:7762",
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
set :web, {
|
53
|
+
port: 7761,
|
54
|
+
}
|
55
|
+
```
|
56
|
+
|
57
|
+
#### Run it
|
58
|
+
|
59
|
+
```
|
60
|
+
mamiya master -c /etc/mamiya/config.rb
|
61
|
+
```
|
62
|
+
|
63
|
+
### Agent
|
64
|
+
|
65
|
+
On a server for agent node; where the package will be deployed:
|
66
|
+
|
67
|
+
#### Configure
|
68
|
+
|
69
|
+
typical configuration for the agents here:
|
70
|
+
|
71
|
+
``` ruby
|
72
|
+
# /etc/mamiya/config.rb
|
73
|
+
|
74
|
+
set :storage, {
|
75
|
+
type: :s3,
|
76
|
+
bucket: 'YOUR-S3-BUCKET-NAME',
|
77
|
+
region: 'ap-northeast-1', # set it to your region
|
78
|
+
access_key_id: '...',
|
79
|
+
secret_access_key: '...',
|
80
|
+
}
|
81
|
+
|
82
|
+
set :serf, {
|
83
|
+
agent: {
|
84
|
+
bind: "0.0.0.0:7760",
|
85
|
+
rpc_addr: "127.0.0.1:7762",
|
86
|
+
join: 'YOUR-MASTER-HOST:7760', # set it to your master host
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
set :web, {
|
91
|
+
port: 7761,
|
92
|
+
}
|
93
|
+
|
94
|
+
# Where should the application go. :myapp is a Symbol to identify application.
|
95
|
+
applications[:myapp] = {deploy_to: '/home/app/myapp'}
|
96
|
+
|
97
|
+
# Where should Mamiya store packages, pre-releases temporarily.
|
98
|
+
set :packages_dir, '/tmp/mamiya/packages'
|
99
|
+
set :prereleases_dir, '/tmp/mamiya/prereleases'
|
100
|
+
|
101
|
+
# And how many you want to keep them?
|
102
|
+
set :keep_packages, 3
|
103
|
+
set :keep_prereleases, 3
|
104
|
+
|
105
|
+
# Label your agent. Block will be called every time when referencing labels.
|
106
|
+
# (Recommend to implement thread or timeout for periodically fetch...)
|
107
|
+
labels do
|
108
|
+
%i(app production active)
|
109
|
+
end
|
110
|
+
```
|
111
|
+
|
112
|
+
#### Run it
|
113
|
+
|
114
|
+
```
|
115
|
+
mamiya master -c /etc/mamiya/config.rb
|
116
|
+
```
|
117
|
+
|
118
|
+
### Confirm both working
|
119
|
+
|
120
|
+
Use `mamiya client` command family to communicate with master process. By default `http://localhost:7761/` is used. You can change by `-m`, `--master` option or `$MAMIYA_MASTER_URL` environment variable.
|
121
|
+
|
122
|
+
```
|
123
|
+
master $ mamiya client list-agents
|
124
|
+
AGENT_NAME alive
|
125
|
+
```
|
126
|
+
|
127
|
+
## Prepare deploy script
|
128
|
+
|
129
|
+
_Script_ should have required operations and configurations for an application being deployed.
|
130
|
+
|
131
|
+
Script is used for package building and deployments. It'll be copied into a package during package build, then deliver, used on agents.
|
132
|
+
|
133
|
+
```
|
134
|
+
set :application, 'myapp'
|
135
|
+
|
136
|
+
# Using for package build
|
137
|
+
set :exclude_from_package, ['tmp', 'log', 'spec', '.sass-cache']
|
138
|
+
set :dereference_symlinks, true
|
139
|
+
set :build_from, "/tmp/build_from"
|
140
|
+
set :build_to, "./builds"
|
141
|
+
# set :package_under, 'dir'
|
142
|
+
|
143
|
+
# You may use helpers here
|
144
|
+
# set :repository, '...'
|
145
|
+
# use :git, exclude_git_clean_targets: true
|
146
|
+
|
147
|
+
# Procedure for build
|
148
|
+
build 'write built at' do
|
149
|
+
# build something...
|
150
|
+
File.write('built_at', "#{Time.now}\n")
|
151
|
+
end
|
152
|
+
|
153
|
+
# Step `prepare` on agents,
|
154
|
+
prepare 'write prepared_at' do
|
155
|
+
File.write release_path.join('prepared_at'), Time.now
|
156
|
+
end
|
157
|
+
|
158
|
+
# You can declare multiples. Run by order defined.
|
159
|
+
prepare 'bundle stuff' do
|
160
|
+
run 'bundle', 'install'
|
161
|
+
end
|
162
|
+
|
163
|
+
# Step `release` run when Release is required. Usually for restarting app process, etc.
|
164
|
+
# Also these step declaration accepts labels for `only` and `except` key to limit agents to run on.
|
165
|
+
# (Labels can be set by agent's configuration)
|
166
|
+
release 'reload unicorn', only [:app] do
|
167
|
+
run 'pkill', '-HUP', '-f', 'unicorn'
|
168
|
+
end
|
169
|
+
```
|
170
|
+
|
171
|
+
## Build your first package and push it
|
172
|
+
|
173
|
+
Have all agents and master node run? Okay, let's build your first package!
|
174
|
+
|
175
|
+
```
|
176
|
+
$ mamiya build -S ./deploy.rb
|
177
|
+
11/18 09:37:01 INFO [Build] Running script.before_build
|
178
|
+
11/18 09:37:01 INFO [Build] Running script.prepare_build
|
179
|
+
11/18 09:37:01 INFO [Build] Running script.build
|
180
|
+
11/18 09:37:01 INFO [Build] Copying script files
|
181
|
+
11/18 09:37:01 INFO [Build] Packed.
|
182
|
+
11/18 09:37:01 INFO [Build] Running script.after_build
|
183
|
+
11/18 09:37:01 INFO [Build] DONE: 20141118093701-myapp built at .../builds/20141118093701-myapp.tar.gz
|
184
|
+
```
|
185
|
+
|
186
|
+
(`-S`,` --script` specifies where the script is. More options available; see `mamiya help build`)
|
187
|
+
|
188
|
+
Great! You can unpack using `tar xf` to confirm what has packed, if you want.
|
189
|
+
|
190
|
+
You have to push it onto storage to make available for agents.
|
191
|
+
|
192
|
+
```
|
193
|
+
$ mamiya push -C ./config.rb
|
194
|
+
11/18 09:42:43 INFO [Push] Pushing builds/20141118093701-myapp.tar.gz to storage(app=myapp)...
|
195
|
+
11/18 09:42:43 INFO [Push] DONE!
|
196
|
+
```
|
197
|
+
|
198
|
+
(`-C`, `--config` specifies where configuration file is... to find `:storage` configuration.)
|
199
|
+
|
200
|
+
## Deploy it
|
201
|
+
|
202
|
+
`mamiya client deploy <PACKAGE>` prepares the package then release it after all nodes get ready.
|
203
|
+
|
204
|
+
```
|
205
|
+
$ export MAMIYA_APP=myapp
|
206
|
+
$ mamiya client list-packages
|
207
|
+
20141118093701-myapp
|
208
|
+
$ mamiya client deploy 20141118093701-myapp
|
209
|
+
```
|
210
|
+
|
211
|
+
(You can use `-a`, `--application` option instead of `$MAMIYA_APP`)
|
212
|
+
|
data/lib/mamiya/cli/client.rb
CHANGED
@@ -295,19 +295,17 @@ not distributed: #{dist['not_distributed_count']} agents
|
|
295
295
|
|
296
296
|
if type == :deploy
|
297
297
|
do_prep = -> do
|
298
|
-
puts "
|
298
|
+
puts " * sending prepare request"
|
299
299
|
prepare(package)
|
300
300
|
end
|
301
301
|
|
302
|
-
|
303
|
-
|
304
|
-
puts " * Wait until prepared"
|
302
|
+
puts "=> Wait agents to have prepared"
|
305
303
|
puts ""
|
306
304
|
|
307
305
|
i = 0
|
308
306
|
loop do
|
309
307
|
i += 1
|
310
|
-
do_prep[] if i % 25 == 0
|
308
|
+
do_prep[] if i == 2 || i % 25 == 0
|
311
309
|
|
312
310
|
s = pkg_status(package, :short)
|
313
311
|
puts ""
|
data/lib/mamiya/storages/s3.rb
CHANGED
@@ -6,6 +6,18 @@ require 'json'
|
|
6
6
|
module Mamiya
|
7
7
|
module Storages
|
8
8
|
class S3 < Mamiya::Storages::Abstract
|
9
|
+
class MultipleObjectsDeletionError < StandardError
|
10
|
+
attr_accessor :errors
|
11
|
+
|
12
|
+
def initialize(errors)
|
13
|
+
message = errors.map do |error|
|
14
|
+
"#{error.code}: #{error.message} (key=#{error.key})"
|
15
|
+
end.join(', ')
|
16
|
+
super(message)
|
17
|
+
@errors = errors
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
9
21
|
def self.find(config={})
|
10
22
|
s3 = initiate_s3_with_config(config)
|
11
23
|
Hash[s3.list_objects(bucket: config[:bucket], delimiter: '/').common_prefixes.map { |prefix|
|
@@ -96,7 +108,10 @@ module Mamiya
|
|
96
108
|
}.compact
|
97
109
|
raise NotFound if objs_to_delete.empty?
|
98
110
|
|
99
|
-
s3.delete_objects(bucket: @config[:bucket], delete: {objects: objs_to_delete})
|
111
|
+
result = s3.delete_objects(bucket: @config[:bucket], delete: {objects: objs_to_delete})
|
112
|
+
unless result.errors.empty?
|
113
|
+
raise MultipleObjectsDeletionError.new(result.errors)
|
114
|
+
end
|
100
115
|
end
|
101
116
|
|
102
117
|
def self.initiate_s3_with_config(config) # :nodoc:
|
data/lib/mamiya/version.rb
CHANGED
data/spec/storages/s3_spec.rb
CHANGED
@@ -250,6 +250,7 @@ describe Mamiya::Storages::S3 do
|
|
250
250
|
|
251
251
|
describe "#remove(package_name)" do
|
252
252
|
let(:package_name) { 'test' }
|
253
|
+
let(:s3_delete_response) { double('response for POST /?delete', errors: []) }
|
253
254
|
subject(:remove) { storage.remove(package_name) }
|
254
255
|
|
255
256
|
before do
|
@@ -257,7 +258,7 @@ describe Mamiya::Storages::S3 do
|
|
257
258
|
end
|
258
259
|
|
259
260
|
it "removes specified package from S3" do
|
260
|
-
expect(s3).to receive(:delete_objects).with(bucket: 'testbucket', delete: {objects: [{key: 'myapp/test.tar.gz'}, {key: 'myapp/test.json'}]})
|
261
|
+
expect(s3).to receive(:delete_objects).with(bucket: 'testbucket', delete: {objects: [{key: 'myapp/test.tar.gz'}, {key: 'myapp/test.json'}]}).and_return(s3_delete_response)
|
261
262
|
remove
|
262
263
|
end
|
263
264
|
|
@@ -265,7 +266,7 @@ describe Mamiya::Storages::S3 do
|
|
265
266
|
let(:package_name) { 'test.tar.gz' }
|
266
267
|
|
267
268
|
it "removes specified package from S3" do
|
268
|
-
expect(s3).to receive(:delete_objects).with(bucket: 'testbucket', delete: {objects: [{key: 'myapp/test.tar.gz'}, {key: 'myapp/test.json'}]})
|
269
|
+
expect(s3).to receive(:delete_objects).with(bucket: 'testbucket', delete: {objects: [{key: 'myapp/test.tar.gz'}, {key: 'myapp/test.json'}]}).and_return(s3_delete_response)
|
269
270
|
remove
|
270
271
|
end
|
271
272
|
end
|
@@ -274,7 +275,7 @@ describe Mamiya::Storages::S3 do
|
|
274
275
|
let(:package_name) { 'test.json' }
|
275
276
|
|
276
277
|
it "removes specified package from S3" do
|
277
|
-
expect(s3).to receive(:delete_objects).with(bucket: 'testbucket', delete: {objects: [{key: 'myapp/test.tar.gz'}, {key: 'myapp/test.json'}]})
|
278
|
+
expect(s3).to receive(:delete_objects).with(bucket: 'testbucket', delete: {objects: [{key: 'myapp/test.tar.gz'}, {key: 'myapp/test.json'}]}).and_return(s3_delete_response)
|
278
279
|
remove
|
279
280
|
end
|
280
281
|
end
|
@@ -288,6 +289,21 @@ describe Mamiya::Storages::S3 do
|
|
288
289
|
expect { storage.remove('test') }.to raise_error(Mamiya::Storages::Abstract::NotFound)
|
289
290
|
end
|
290
291
|
end
|
292
|
+
|
293
|
+
context "when delete_objects fails" do
|
294
|
+
let(:errors) do
|
295
|
+
[
|
296
|
+
double('Aws::S3::Errors::InteranlError', code: 'InteranlError', message: 'Internal Error', key: 'myapp/test.tar.gz'),
|
297
|
+
double('Aws::S3::Errors::AccessDenied', code: 'AccessDenied', message: 'Access Denied', key: 'myapp/test.json'),
|
298
|
+
]
|
299
|
+
end
|
300
|
+
let(:s3_delete_response) { double('response for POST /?delete', errors: errors) }
|
301
|
+
|
302
|
+
it "raises error" do
|
303
|
+
expect(s3).to receive(:delete_objects).with(bucket: 'testbucket', delete: {objects: [{key: 'myapp/test.tar.gz'}, {key: 'myapp/test.json'}]}).and_return(s3_delete_response)
|
304
|
+
expect { remove }.to raise_error(Mamiya::Storages::S3::MultipleObjectsDeletionError, /Access Denied/)
|
305
|
+
end
|
306
|
+
end
|
291
307
|
end
|
292
308
|
|
293
309
|
describe ".find" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mamiya
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shota Fukumori (sora_h)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10
|
11
|
+
date: 2014-12-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -183,6 +183,7 @@ files:
|
|
183
183
|
- docs/internal/serf_events.md
|
184
184
|
- docs/internal/serf_queries.md
|
185
185
|
- docs/internal/tasks.md
|
186
|
+
- docs/quick_start.md
|
186
187
|
- docs/upgrading.md
|
187
188
|
- example/.gitignore
|
188
189
|
- example/Procfile
|
@@ -297,7 +298,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
297
298
|
version: '0'
|
298
299
|
requirements: []
|
299
300
|
rubyforge_project:
|
300
|
-
rubygems_version: 2.
|
301
|
+
rubygems_version: 2.4.1
|
301
302
|
signing_key:
|
302
303
|
specification_version: 4
|
303
304
|
summary: Fast deploy tool using tarballs and serf
|