martilla 0.0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +5 -0
- data/Gemfile.lock +44 -12
- data/README.md +124 -15
- data/config.yml +14 -0
- data/lib/martilla/backup.rb +115 -0
- data/lib/martilla/cli.rb +5 -3
- data/lib/martilla/database.rb +17 -6
- data/lib/martilla/databases/mysql.rb +37 -2
- data/lib/martilla/databases/postgres.rb +38 -2
- data/lib/martilla/notifier.rb +27 -8
- data/lib/martilla/notifiers/email_notifier.rb +65 -0
- data/lib/martilla/notifiers/sendmail.rb +21 -0
- data/lib/martilla/notifiers/ses.rb +86 -2
- data/lib/martilla/notifiers/slack.rb +2 -3
- data/lib/martilla/notifiers/smtp.rb +70 -0
- data/lib/martilla/storage.rb +47 -7
- data/lib/martilla/storages/local.rb +7 -3
- data/lib/martilla/storages/s3.rb +39 -2
- data/lib/martilla/storages/scp.rb +26 -2
- data/lib/martilla/utilities.rb +45 -0
- data/lib/martilla/version.rb +1 -1
- data/lib/martilla.rb +17 -24
- data/martilla.gemspec +7 -2
- metadata +84 -12
- data/lib/martilla/databases/base.rb +0 -11
- data/lib/martilla/notifiers/base.rb +0 -7
- data/lib/martilla/notifiers/email.rb +0 -5
- data/lib/martilla/storages/base.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14bd7c780f6612c6adfbd67e932bf30db058e6af7148a7889d18ab0249e4d768
|
4
|
+
data.tar.gz: a4cd222e53ac9eefb6fdef35bbe91ff3a2b16f49ddbfefe221a8d5ab244ae2e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c6fa4a885951c8a07c88cf62973fa82c63e889b2a4b63080a21888f9a4d8363fdcec841b65b4117af32f8821cccd62798ee1b36271c32a3cf8a6f7d09694a82
|
7
|
+
data.tar.gz: 612b5bf6139d30935919e2989f9a87b2ebc58cc9f874b9e9a1b3b993f29e2069f1e13001d12696a563c0962cac17b6cba8f36c6b8bf57b404908bab9469adfaf
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,29 +1,54 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
martilla (0.0
|
5
|
-
aws-
|
4
|
+
martilla (0.2.0)
|
5
|
+
aws-sdk-s3 (~> 1.49)
|
6
|
+
aws-sdk-ses (~> 1.26)
|
7
|
+
memoist (~> 0.16.0)
|
6
8
|
pony (~> 1.13)
|
7
9
|
thor (~> 0.20.3)
|
8
10
|
|
9
11
|
GEM
|
10
12
|
remote: https://rubygems.org/
|
11
13
|
specs:
|
12
|
-
aws-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
aws-eventstream (1.0.3)
|
15
|
+
aws-partitions (1.226.0)
|
16
|
+
aws-sdk-core (3.69.1)
|
17
|
+
aws-eventstream (~> 1.0, >= 1.0.2)
|
18
|
+
aws-partitions (~> 1.0)
|
19
|
+
aws-sigv4 (~> 1.1)
|
20
|
+
jmespath (~> 1.0)
|
21
|
+
aws-sdk-kms (1.24.0)
|
22
|
+
aws-sdk-core (~> 3, >= 3.61.1)
|
23
|
+
aws-sigv4 (~> 1.1)
|
24
|
+
aws-sdk-s3 (1.50.0)
|
25
|
+
aws-sdk-core (~> 3, >= 3.61.1)
|
26
|
+
aws-sdk-kms (~> 1)
|
27
|
+
aws-sigv4 (~> 1.1)
|
28
|
+
aws-sdk-ses (1.26.0)
|
29
|
+
aws-sdk-core (~> 3, >= 3.61.1)
|
30
|
+
aws-sigv4 (~> 1.1)
|
31
|
+
aws-sigv4 (1.1.0)
|
32
|
+
aws-eventstream (~> 1.0, >= 1.0.2)
|
33
|
+
byebug (11.0.1)
|
34
|
+
coderay (1.1.2)
|
18
35
|
diff-lcs (1.3)
|
36
|
+
docile (1.3.2)
|
37
|
+
jmespath (1.4.0)
|
38
|
+
json (2.2.0)
|
19
39
|
mail (2.7.1)
|
20
40
|
mini_mime (>= 0.1.1)
|
21
|
-
|
22
|
-
|
23
|
-
mime-types-data (3.2019.1009)
|
41
|
+
memoist (0.16.0)
|
42
|
+
method_source (0.9.2)
|
24
43
|
mini_mime (1.0.2)
|
25
44
|
pony (1.13.1)
|
26
45
|
mail (>= 2.0)
|
46
|
+
pry (0.12.2)
|
47
|
+
coderay (~> 1.1.0)
|
48
|
+
method_source (~> 0.9.0)
|
49
|
+
pry-byebug (3.7.0)
|
50
|
+
byebug (~> 11.0)
|
51
|
+
pry (~> 0.10)
|
27
52
|
rake (13.0.0)
|
28
53
|
rspec (3.9.0)
|
29
54
|
rspec-core (~> 3.9.0)
|
@@ -38,17 +63,24 @@ GEM
|
|
38
63
|
diff-lcs (>= 1.2.0, < 2.0)
|
39
64
|
rspec-support (~> 3.9.0)
|
40
65
|
rspec-support (3.9.0)
|
66
|
+
simplecov (0.17.1)
|
67
|
+
docile (~> 1.1)
|
68
|
+
json (>= 1.8, < 3)
|
69
|
+
simplecov-html (~> 0.10.0)
|
70
|
+
simplecov-html (0.10.2)
|
41
71
|
thor (0.20.3)
|
42
|
-
xml-simple (1.1.5)
|
43
72
|
|
44
73
|
PLATFORMS
|
45
74
|
ruby
|
46
75
|
|
47
76
|
DEPENDENCIES
|
48
77
|
bundler (~> 2.0)
|
78
|
+
byebug (~> 11.0)
|
49
79
|
martilla!
|
80
|
+
pry-byebug (~> 3.7)
|
50
81
|
rake (~> 13.0)
|
51
82
|
rspec (~> 3.9)
|
83
|
+
simplecov (~> 0.17.1)
|
52
84
|
|
53
85
|
BUNDLED WITH
|
54
86
|
2.0.2
|
data/README.md
CHANGED
@@ -1,36 +1,145 @@
|
|
1
1
|
# Martilla
|
2
2
|
|
3
|
-
|
3
|
+
Martilla is a tool to automate your backups. With simple but flexible configuration options you can have a database backup configured to run (using cron jobs or similar). Receive a notification whenever a backup fails, choose multiple ways of getting notified (i.e. email + slack).
|
4
|
+
|
5
|
+
The name Martilla comes from a local name for the [Kinkajou](https://en.wikipedia.org/wiki/Kinkajou). This nocturnal animal is goes fairly unnoticed, just like we hope database backups should remain.
|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
7
|
-
|
9
|
+
To use as a CLI tool
|
10
|
+
|
11
|
+
$ gem install martilla
|
12
|
+
|
13
|
+
Or add this line to your application's Gemfile:
|
8
14
|
|
9
15
|
```ruby
|
10
16
|
gem 'martilla'
|
11
17
|
```
|
12
18
|
|
13
|
-
And then execute:
|
14
|
-
|
15
|
-
$ bundle
|
16
|
-
|
17
|
-
Or install it yourself as:
|
18
|
-
|
19
|
-
$ gem install martilla
|
20
|
-
|
21
19
|
## Usage
|
22
20
|
|
23
|
-
|
21
|
+
Martilla uses a YAML configuration file that specifies the backup to be performed. The gem works by making three main concepts work together, they're listed out with details that should generally be specified in the config file:
|
22
|
+
|
23
|
+
- Database
|
24
|
+
- What database are we going to backup
|
25
|
+
- How can we connect to the database
|
26
|
+
- Storage
|
27
|
+
- Where is this backup going to be stored
|
28
|
+
- Credentials needed to persist the backup
|
29
|
+
- Notifiers
|
30
|
+
- How will you get notified of the backup result
|
31
|
+
- Can be a list of multiple ways to get notified
|
32
|
+
|
33
|
+
Execute `martilla setup backup-config.yml` and you'll have your first (default) config file that looks like the following:
|
34
|
+
|
35
|
+
```yaml
|
36
|
+
---
|
37
|
+
db:
|
38
|
+
type: postgres
|
39
|
+
options:
|
40
|
+
host: localhost
|
41
|
+
user: username
|
42
|
+
password: password
|
43
|
+
db: databasename
|
44
|
+
storage:
|
45
|
+
type: local
|
46
|
+
options:
|
47
|
+
filename: database-backup.sql
|
48
|
+
notifiers:
|
49
|
+
- type: none
|
50
|
+
```
|
51
|
+
|
52
|
+
From here on you pick the building blocks that work for your specific case:
|
53
|
+
|
54
|
+
### Databases
|
55
|
+
|
56
|
+
Currently available DB types to choose from are 'postgres' & 'mysql'. They both have the same available options:
|
57
|
+
- `host`
|
58
|
+
- defaults to localhost
|
59
|
+
- can be set in ENV variable `PG_HOST` or `MYSQL_HOST`
|
60
|
+
- `user`
|
61
|
+
- required
|
62
|
+
- can be set in ENV variable `PG_USER` or `MYSQL_USER`
|
63
|
+
- `password`
|
64
|
+
- required
|
65
|
+
- can be set in ENV variable `PG_PASSWORD` or `MYSQL_PASSWORD`
|
66
|
+
- `db`
|
67
|
+
- required
|
68
|
+
- can be set in ENV variable `PG_DATABASE` or `MYSQL_DATABASE`
|
69
|
+
- `port`
|
70
|
+
- defaults to 5432 or 3306
|
71
|
+
- can be set in ENV variable `PG_USER` or `MYSQL_USER`
|
72
|
+
|
73
|
+
### Storages
|
74
|
+
|
75
|
+
The available Storages types are 'local', 'S3' & 'SCP'. They each have different available options:
|
76
|
+
- options for type: 'local'
|
77
|
+
- `filename`
|
78
|
+
- The location to where the backup will be stored
|
79
|
+
- options for type: 's3'
|
80
|
+
- `filename`
|
81
|
+
- The location to where the backup will be stored within the S3 bucket
|
82
|
+
- `bucket`
|
83
|
+
- `region`
|
84
|
+
- `access_key_id`
|
85
|
+
- can be specified with the usual ENV variables or IAM roles
|
86
|
+
- `secret_access_key`
|
87
|
+
- can be specified with the usual ENV variables or IAM roles
|
88
|
+
- options for type: 'scp'
|
89
|
+
- `filename`
|
90
|
+
- The location to where the backup will be stored within remote server
|
91
|
+
- `host`
|
92
|
+
- `user`
|
93
|
+
- `identity_file`
|
94
|
+
|
95
|
+
All storage types also accept 'suffix' as a boolean that enables or disables a timestamp to be added as a suffix to the backup 'filename', it defaults as `true`.
|
96
|
+
|
97
|
+
### Notifiers
|
98
|
+
|
99
|
+
The available Notifiers are 'ses', 'sendmail' & 'smtp'. They each have different available options:
|
100
|
+
- options for type: 'ses' (email notifier)
|
101
|
+
- `aws_region`
|
102
|
+
- `access_key_id`
|
103
|
+
- can be specified with the usual ENV variables or IAM role
|
104
|
+
- `secret_access_key`
|
105
|
+
- can be specified with the usual ENV variables or IAM roles
|
106
|
+
- options for type: 'sendmail' (email notifier)
|
107
|
+
- no custom options
|
108
|
+
- options for type: 'smtp' (email notifier)
|
109
|
+
- `address`
|
110
|
+
- `domain`
|
111
|
+
- `user_name`
|
112
|
+
- `password`
|
113
|
+
|
114
|
+
All of the previous email notifiers also have the following options they all use:
|
115
|
+
- `to`
|
116
|
+
- a list of comma separated emails to be notified
|
117
|
+
- `from`
|
118
|
+
- defaults to 'martilla@no-reply.com'
|
119
|
+
- `success_subject`
|
120
|
+
- the subject of the success email
|
121
|
+
- `failure_subject`
|
122
|
+
- the subject of the failure email
|
123
|
+
|
124
|
+
It's **HIGHLY RECOMMENDED** to test and make sure emails are being delivered correctly to each target inbox. Emails with standard messages like these automated backup notifications tend to be easily marked as spam.
|
125
|
+
|
126
|
+
### Perform a backup
|
127
|
+
|
128
|
+
As simple as running the `backup` command on the martilla CLI and passing as argument the configuration file you want to use
|
129
|
+
|
130
|
+
$ martilla backup backup-config.yml
|
131
|
+
|
132
|
+
Help the help command help you
|
133
|
+
|
134
|
+
$ martilla help
|
24
135
|
|
25
136
|
## Development
|
26
137
|
|
27
138
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
28
139
|
|
29
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
30
|
-
|
31
140
|
## Contributing
|
32
141
|
|
33
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
142
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/fdoxyz/martilla. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
34
143
|
|
35
144
|
## License
|
36
145
|
|
@@ -38,4 +147,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
38
147
|
|
39
148
|
## Code of Conduct
|
40
149
|
|
41
|
-
Everyone interacting in the Martilla project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
150
|
+
Everyone interacting in the Martilla project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/fdoxyz/martilla/blob/master/CODE_OF_CONDUCT.md).
|
data/config.yml
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'memoist'
|
2
|
+
require 'martilla/utilities'
|
3
|
+
|
4
|
+
module Martilla
|
5
|
+
class Backup
|
6
|
+
extend Memoist
|
7
|
+
|
8
|
+
include Utilities
|
9
|
+
|
10
|
+
attr_reader :file_size
|
11
|
+
|
12
|
+
def initialize(config)
|
13
|
+
@options = config['options']
|
14
|
+
@db = Database.create(config['db'])
|
15
|
+
@storage = Storage.create(config['storage'])
|
16
|
+
@notifiers = config['notifiers'].map { |c| Notifier.create(c) }.compact
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.create(config)
|
20
|
+
backup = Backup.new(config)
|
21
|
+
backup.execute
|
22
|
+
backup
|
23
|
+
end
|
24
|
+
|
25
|
+
def gzip?
|
26
|
+
return true if @options['gzip'].nil?
|
27
|
+
@options['gzip']
|
28
|
+
end
|
29
|
+
|
30
|
+
def tmp_file
|
31
|
+
filename = @options.dig('tmp_file') || '/tmp/backup'
|
32
|
+
return "#{filename}.gz" if gzip?
|
33
|
+
filename
|
34
|
+
end
|
35
|
+
|
36
|
+
def execute
|
37
|
+
begin
|
38
|
+
# Perform DB dump
|
39
|
+
@ticks = [Time.now]
|
40
|
+
@db.dump(tmp_file: tmp_file, gzip: gzip?)
|
41
|
+
|
42
|
+
# Metadata capture
|
43
|
+
@file_size = File.size(tmp_file).to_f if File.exist?(tmp_file)
|
44
|
+
@ticks << Time.now
|
45
|
+
|
46
|
+
# Persist the backup
|
47
|
+
@storage.persist(tmp_file: tmp_file, gzip: gzip?)
|
48
|
+
rescue Exception => e
|
49
|
+
@notifiers.each do |notifier|
|
50
|
+
notifier.error(e.message, metadata)
|
51
|
+
end
|
52
|
+
puts "An error occurred: #{e.inspect}"
|
53
|
+
else
|
54
|
+
@notifiers.each do |notifier|
|
55
|
+
notifier.success(metadata)
|
56
|
+
end
|
57
|
+
puts "Backup created and persisted successfully"
|
58
|
+
end
|
59
|
+
|
60
|
+
File.delete(tmp_file) if File.exist?(tmp_file)
|
61
|
+
end
|
62
|
+
|
63
|
+
def metadata
|
64
|
+
@ticks << Time.now
|
65
|
+
data = []
|
66
|
+
|
67
|
+
# Total backup size
|
68
|
+
if @file_size.nil?
|
69
|
+
data << "No backup file was created"
|
70
|
+
else
|
71
|
+
data << "Total backup size: #{formatted_file_size}"
|
72
|
+
end
|
73
|
+
|
74
|
+
# Backup time measurements
|
75
|
+
if @ticks.count >= 2
|
76
|
+
time_diff = duration_format(@ticks[1] - @ticks[0])
|
77
|
+
data << "Backup 'dump' duration: #{time_diff}"
|
78
|
+
end
|
79
|
+
|
80
|
+
# Storage time measurements
|
81
|
+
if @ticks.count >= 3
|
82
|
+
time_diff = duration_format(@ticks[2] - @ticks[1])
|
83
|
+
data << "Backup storage duration: #{time_diff}"
|
84
|
+
end
|
85
|
+
|
86
|
+
data
|
87
|
+
end
|
88
|
+
memoize :metadata
|
89
|
+
|
90
|
+
def self.sample_config
|
91
|
+
{
|
92
|
+
'db' => {
|
93
|
+
'type' => 'postgres',
|
94
|
+
'options' => {
|
95
|
+
'host' => 'localhost',
|
96
|
+
'user' => 'username',
|
97
|
+
'password' => 'password',
|
98
|
+
'db' => 'databasename'
|
99
|
+
}
|
100
|
+
},
|
101
|
+
'storage' => {
|
102
|
+
'type' => 'local',
|
103
|
+
'options' => {
|
104
|
+
'filename' => 'database-backup.sql'
|
105
|
+
}
|
106
|
+
},
|
107
|
+
'notifiers' => [
|
108
|
+
{
|
109
|
+
'type' => 'none'
|
110
|
+
}
|
111
|
+
]
|
112
|
+
}
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/lib/martilla/cli.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'byebug'
|
1
2
|
require 'yaml'
|
2
3
|
require 'thor'
|
3
4
|
|
@@ -15,13 +16,14 @@ module Martilla
|
|
15
16
|
rescue Errno::ENOENT
|
16
17
|
puts "Couldn't access non-existent file #{file_path}"
|
17
18
|
else
|
18
|
-
|
19
|
+
backup = Backup.create(backup_config)
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
22
23
|
desc "setup FILEPATH", "Generates a sample backup config file at FILEPATH"
|
23
|
-
def setup(
|
24
|
-
|
24
|
+
def setup(filename = 'martilla.yml')
|
25
|
+
file_path = File.join(Dir.pwd, filename)
|
26
|
+
File.write(file_path, Backup.sample_config.to_yaml)
|
25
27
|
end
|
26
28
|
end
|
27
29
|
end
|
data/lib/martilla/database.rb
CHANGED
@@ -2,20 +2,31 @@ require 'forwardable'
|
|
2
2
|
|
3
3
|
module Martilla
|
4
4
|
class Database
|
5
|
-
|
5
|
+
attr_reader :options
|
6
6
|
|
7
7
|
def initialize(config)
|
8
|
-
|
8
|
+
@options = config
|
9
|
+
raise Error.new(invalid_options_msg) if @options.nil?
|
10
|
+
end
|
11
|
+
|
12
|
+
def dump(temp_file:, gzip:)
|
13
|
+
raise NotImplementedError, 'You must implement the dump method'
|
14
|
+
end
|
15
|
+
|
16
|
+
def invalid_options_msg
|
17
|
+
'DB configuration is invalid. Details here: https://github.com/fdoxyz/martilla'
|
18
|
+
end
|
19
|
+
|
20
|
+
# When a new DB is supported it needs to go here
|
21
|
+
def self.create(config = {})
|
9
22
|
case config['type'].downcase
|
10
23
|
when 'postgres'
|
11
|
-
|
24
|
+
Postgres.new(config['options'])
|
12
25
|
when 'mysql'
|
13
|
-
|
26
|
+
Mysql.new(config['options'])
|
14
27
|
else
|
15
28
|
raise Error.new("Invalid Database type: #{config['type']}")
|
16
29
|
end
|
17
30
|
end
|
18
|
-
|
19
|
-
def_delegators :@database, :dump
|
20
31
|
end
|
21
32
|
end
|
@@ -1,5 +1,40 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module Martilla
|
2
|
+
class Mysql < Database
|
3
|
+
def dump(tmp_file:, gzip:)
|
4
|
+
if gzip
|
5
|
+
`mysqldump #{connection_arguments} | gzip -c > #{tmp_file}`
|
6
|
+
else
|
7
|
+
`mysqldump #{connection_arguments} > #{tmp_file}`
|
8
|
+
end
|
3
9
|
|
10
|
+
return if $?.success?
|
11
|
+
raise Error.new("Database dump failed with code #{$?.exitstatus}")
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def connection_arguments
|
17
|
+
"-u #{user} -p #{password} -P #{port} #{db}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def host
|
21
|
+
@options['host'] || ENV['MYSQL_HOST'] || 'localhost'
|
22
|
+
end
|
23
|
+
|
24
|
+
def port
|
25
|
+
@options['port'] || ENV['MYSQL_PORT'] || '3306'
|
26
|
+
end
|
27
|
+
|
28
|
+
def user
|
29
|
+
@options['user'] || ENV['MYSQL_USER']
|
30
|
+
end
|
31
|
+
|
32
|
+
def password
|
33
|
+
@options['password'] || ENV['MYSQL_PASSWORD']
|
34
|
+
end
|
35
|
+
|
36
|
+
def db
|
37
|
+
@options['db'] || ENV['MYSQL_DATABASE']
|
38
|
+
end
|
4
39
|
end
|
5
40
|
end
|
@@ -1,4 +1,40 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module Martilla
|
2
|
+
class Postgres < Database
|
3
|
+
def dump(tmp_file:, gzip:)
|
4
|
+
if gzip
|
5
|
+
`pg_dump #{connection_string} | gzip -c > #{tmp_file}`
|
6
|
+
else
|
7
|
+
`pg_dump #{connection_string} > #{tmp_file}`
|
8
|
+
end
|
9
|
+
|
10
|
+
return if $?.success?
|
11
|
+
raise Error.new("Database dump failed with code #{$?.exitstatus}")
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def connection_string
|
17
|
+
"postgres://#{user}:#{password}@#{host}:#{port}/#{db}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def host
|
21
|
+
@options['host'] || ENV['PG_HOST'] || 'localhost'
|
22
|
+
end
|
23
|
+
|
24
|
+
def port
|
25
|
+
@options['port'] || ENV['PG_PORT'] || '5432'
|
26
|
+
end
|
27
|
+
|
28
|
+
def user
|
29
|
+
@options['user'] || ENV['PG_USER']
|
30
|
+
end
|
31
|
+
|
32
|
+
def password
|
33
|
+
@options['password'] || ENV['PG_PASSWORD']
|
34
|
+
end
|
35
|
+
|
36
|
+
def db
|
37
|
+
@options['db'] || ENV['PG_DATABASE']
|
38
|
+
end
|
3
39
|
end
|
4
40
|
end
|
data/lib/martilla/notifier.rb
CHANGED
@@ -2,22 +2,41 @@ require 'forwardable'
|
|
2
2
|
|
3
3
|
module Martilla
|
4
4
|
class Notifier
|
5
|
-
|
5
|
+
attr_reader :options
|
6
6
|
|
7
7
|
def initialize(config)
|
8
|
-
|
8
|
+
@options = config
|
9
|
+
raise Error.new(invalid_options_msg) if @options.nil?
|
10
|
+
end
|
11
|
+
|
12
|
+
def success(data)
|
13
|
+
raise NotImplementedError, 'You must implement the success method'
|
14
|
+
end
|
15
|
+
|
16
|
+
def error(msg, data)
|
17
|
+
raise NotImplementedError, 'You must implement the error method'
|
18
|
+
end
|
19
|
+
|
20
|
+
def invalid_options_msg
|
21
|
+
'Notifier configuration is invalid. Details here: https://github.com/fdoxyz/martilla'
|
22
|
+
end
|
23
|
+
|
24
|
+
# When a new Notifier is supported it needs to go here
|
25
|
+
def self.create(config = {})
|
9
26
|
case config['type'].downcase
|
10
|
-
when '
|
11
|
-
|
27
|
+
when 'sendmail'
|
28
|
+
Sendmail.new(config['options'])
|
29
|
+
when 'smtp'
|
30
|
+
Smtp.new(config['options'])
|
12
31
|
when 'ses'
|
13
|
-
|
32
|
+
Ses.new(config['options'])
|
14
33
|
when 'slack'
|
15
|
-
|
34
|
+
Slack.new(config['options'])
|
35
|
+
when 'none'
|
36
|
+
nil
|
16
37
|
else
|
17
38
|
raise Error.new("Invalid Notifier type: #{config['type']}")
|
18
39
|
end
|
19
40
|
end
|
20
|
-
|
21
|
-
def_delegators :@notifier, :success, :error
|
22
41
|
end
|
23
42
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'pony'
|
2
|
+
|
3
|
+
module Martilla
|
4
|
+
class EmailNotifier < Notifier
|
5
|
+
private
|
6
|
+
|
7
|
+
def success_html(data)
|
8
|
+
data_list = data.map { |d| "<li>#{d}</li>" }.join("\n")
|
9
|
+
<<~HTML
|
10
|
+
<h2>The backup was created successfully</h2>
|
11
|
+
<ul>
|
12
|
+
#{data_list}
|
13
|
+
</ul>
|
14
|
+
HTML
|
15
|
+
end
|
16
|
+
|
17
|
+
def success_txt(data)
|
18
|
+
data_list = data.map { |d| "- #{d}" }.join("\n")
|
19
|
+
<<~TXT
|
20
|
+
The backup was created successfully
|
21
|
+
|
22
|
+
#{data_list}
|
23
|
+
TXT
|
24
|
+
end
|
25
|
+
|
26
|
+
def error_html(msg, data)
|
27
|
+
data_list = data.map { |d| "<li>#{d}</li>" }.join("\n")
|
28
|
+
<<~HTML
|
29
|
+
<h2>The backup attempt failed with the following error</h2>
|
30
|
+
<p><strong>#{msg}</strong></p>
|
31
|
+
<ul>
|
32
|
+
#{data_list}
|
33
|
+
</ul>
|
34
|
+
HTML
|
35
|
+
end
|
36
|
+
|
37
|
+
def error_txt(msg, data)
|
38
|
+
data_list = data.map { |d| "- #{d}" }.join("\n")
|
39
|
+
<<~TXT
|
40
|
+
The backup attempt failed with the following error:
|
41
|
+
#{msg}
|
42
|
+
|
43
|
+
#{data_list}
|
44
|
+
TXT
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_email
|
48
|
+
email = @options['to']
|
49
|
+
raise config_error('to') if email.nil?
|
50
|
+
email
|
51
|
+
end
|
52
|
+
|
53
|
+
def from_email
|
54
|
+
@options['from'] || 'martilla@no-reply.com'
|
55
|
+
end
|
56
|
+
|
57
|
+
def success_subject
|
58
|
+
@options['success_subject'] || '[SUCCESS] Backup created'
|
59
|
+
end
|
60
|
+
|
61
|
+
def failure_subject
|
62
|
+
@options['failure_subject'] || '[FAILURE] Backup failed'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|