cronicle 0.1.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 +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/.travis.yml +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +127 -0
- data/Rakefile +5 -0
- data/Vagrantfile +56 -0
- data/bin/cronicle +6 -0
- data/cronicle.gemspec +31 -0
- data/cronicle.us-east-1.pem.enc +0 -0
- data/lib/cronicle.rb +28 -0
- data/lib/cronicle/cli.rb +105 -0
- data/lib/cronicle/client.rb +161 -0
- data/lib/cronicle/driver.rb +157 -0
- data/lib/cronicle/dsl.rb +7 -0
- data/lib/cronicle/dsl/context.rb +48 -0
- data/lib/cronicle/dsl/context/job.rb +58 -0
- data/lib/cronicle/exporter.rb +56 -0
- data/lib/cronicle/ext/hash_ext.rb +8 -0
- data/lib/cronicle/ext/sshkit_ext.rb +147 -0
- data/lib/cronicle/ext/string_ext.rb +11 -0
- data/lib/cronicle/host_list.rb +89 -0
- data/lib/cronicle/logger.rb +92 -0
- data/lib/cronicle/utils.rb +43 -0
- data/lib/cronicle/version.rb +3 -0
- data/spec/cronicle_create_spec.rb +295 -0
- data/spec/cronicle_delete_spec.rb +334 -0
- data/spec/cronicle_exec_spec.rb +159 -0
- data/spec/cronicle_update_spec.rb +365 -0
- data/spec/host_list_spec.rb +162 -0
- data/spec/spec_helper.rb +150 -0
- metadata +223 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ce9c4a8eff04541e0649bf02eb6e096774d2d42b
|
4
|
+
data.tar.gz: 0833f3ca301c909805d70910e08491de147f4507
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2237ab42e1da6822a540b0a806a67da32155288aeb9a6fdb345ec85baf6b66e137b9b95745898166e9446405e07dbb4dcad3604929ad00e4042978e68282f180
|
7
|
+
data.tar.gz: c48de4d137fcb1030b8fede56e6189b439d58cd788c12a4ad70be84e31594dd7bb2c8dce361e1985245fcc778701894ae4562fbe18e0e3641a72dbfeb146062a
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.0.0
|
4
|
+
before_install:
|
5
|
+
- openssl aes-256-cbc -K $encrypted_3e901e83a832_key -iv $encrypted_3e901e83a832_iv
|
6
|
+
-in cronicle.us-east-1.pem.enc -out cronicle.us-east-1.pem -d
|
7
|
+
install:
|
8
|
+
- wget https://dl.bintray.com/mitchellh/vagrant/vagrant_1.7.2_x86_64.deb
|
9
|
+
- sudo dpkg -i vagrant_1.7.2_x86_64.deb
|
10
|
+
before_script:
|
11
|
+
- vagrant plugin install vagrant-aws
|
12
|
+
- vagrant up --provider=aws
|
13
|
+
script:
|
14
|
+
- bundle install
|
15
|
+
- bundle exec rake
|
16
|
+
after_script:
|
17
|
+
- vagrant destroy -f
|
18
|
+
env:
|
19
|
+
global:
|
20
|
+
- secure: CdUw5RJ8GXVCD5Orr+hP7tscfE1rNFyz2bnIqWj8jLCnxcLY7TqcV9v1z130uuU+keAyzAFIOPu1GHrr83Sgyo2MIwQkYfA52K1gB5ThzZXZQDSvGKJk3mtfvRkxJVmUxeCEZVC9QXKqR5WW15Cj7wBG4Y6dClty3zwyH7MtPI8=
|
21
|
+
- secure: gR/fjFSW81OtcnI8lQREfwj/EeW0YOsXOTUpZCPRdWaaIKGYmjB+6H+KgPcw1nGy+kknb5HXyOTLTSKOngC6WW1EaeKYzKqKGn8xlFGpFMmLCVXyDJG85ABr8jC768AltbYPYuUYb0JgX2PEMH1NNbCVaQqh8oUlBOjMrMqCbzk=
|
22
|
+
- EC2_KEYPAIR_NAME=cronicle.us-east-1
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Genki Sugawara
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
# Cronicle
|
2
|
+
|
3
|
+
It is a tool for execute script, and define cron on remote hosts.
|
4
|
+
|
5
|
+
[](http://badge.fury.io/rb/cronicle)
|
6
|
+
[](https://travis-ci.org/winebarrel/cronicle)
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'cronicle'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install cronicle
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
```
|
27
|
+
Commands:
|
28
|
+
cronicle apply # Apply cron jobs to remote hosts
|
29
|
+
cronicle exec JOB_NAME # Execute a job on remote hosts
|
30
|
+
cronicle help [COMMAND] # Describe available commands or one specific command
|
31
|
+
|
32
|
+
Options:
|
33
|
+
-f, [--file=FILE] # Job definition file
|
34
|
+
# Default: Jobfile
|
35
|
+
-h, [--hosts=HOSTS] # Hosts definition file
|
36
|
+
-r, [--target-roles=one two three] # Target host role list
|
37
|
+
-p, [--sudo-password=SUDO-PASSWORD] # Sudo password
|
38
|
+
[--dry-run], [--no-dry-run] # Do not actually change
|
39
|
+
-c, [--ssh-config=SSH-CONFIG] # OpenSSH configuration file
|
40
|
+
[--connection-timeout=N] # SSH connection timeout
|
41
|
+
[--concurrency=N] # SSH concurrency
|
42
|
+
# Default: 10
|
43
|
+
[--libexec=LIBEXEC] # Cronicle libexec path
|
44
|
+
# Default: /var/lib/cronicle/libexec
|
45
|
+
-v, [--verbose], [--no-verbose] # Verbose mode
|
46
|
+
[--debug], [--no-debug] # Debug mode
|
47
|
+
[--color], [--no-color] # Colorize log
|
48
|
+
# Default: true
|
49
|
+
```
|
50
|
+
|
51
|
+
## Quick Start
|
52
|
+
|
53
|
+
```sh
|
54
|
+
$ cat Jobfile
|
55
|
+
on servers: :your_hostname do
|
56
|
+
job :my_job, user: "ec2-user", schedule: "* * * * *" do
|
57
|
+
puts "hello"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
$ cronicle exec my_job
|
62
|
+
my_job on your_hostname/ec2-user> Execute job
|
63
|
+
my_job on your_hostname/ec2-user> hello
|
64
|
+
|
65
|
+
$ cronicle apply --dry-run
|
66
|
+
my_job on your_hostname/ec2-user> Create job: schedule="* * * * *" (dry-run)
|
67
|
+
|
68
|
+
$ cronicle apply
|
69
|
+
my_job on your_hostname/ec2-user> Create job: schedule="* * * * *"
|
70
|
+
|
71
|
+
$ ssh your_hostname 'crontab -l'
|
72
|
+
* * * * * /var/lib/cronicle/libexec/ec2-user/my_job 2>&1 | logger -t cronicle/ec2-user/my_job
|
73
|
+
|
74
|
+
$ ssh your_hostname 'cat /var/lib/cronicle/libexec/ec2-user/my_job'
|
75
|
+
#!/usr/bin/env ruby
|
76
|
+
puts "hello"
|
77
|
+
```
|
78
|
+
|
79
|
+
## Hosts definition file
|
80
|
+
|
81
|
+
Any of the following formats:
|
82
|
+
|
83
|
+
```
|
84
|
+
server1,server2,...
|
85
|
+
```
|
86
|
+
```
|
87
|
+
server1
|
88
|
+
server2
|
89
|
+
...
|
90
|
+
```
|
91
|
+
```javascript
|
92
|
+
{
|
93
|
+
"servers": {
|
94
|
+
"server1": ["web", "app"]
|
95
|
+
"server2": ["db"]
|
96
|
+
}
|
97
|
+
}
|
98
|
+
```
|
99
|
+
```javascript
|
100
|
+
{
|
101
|
+
"roles": {
|
102
|
+
"web": ["server1"],
|
103
|
+
"app": ["server1"],
|
104
|
+
"db": ["server2"]
|
105
|
+
}
|
106
|
+
}
|
107
|
+
```
|
108
|
+
|
109
|
+
Hosts definition file is not required.
|
110
|
+
|
111
|
+
If you pass `--hosts` options, can be defined as follows in Jobfile:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
on servers: /any_host/ do # use regexp
|
115
|
+
job :foo, user: "ec2-user", schedule: "* * * * *" do
|
116
|
+
3.times do
|
117
|
+
puts "hello"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
on roles: "web" do # use role
|
123
|
+
job :my_job, user: "ec2-user", schedule: "* * * * *" do
|
124
|
+
puts "hello"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
```
|
data/Rakefile
ADDED
data/Vagrantfile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# -*- mode: ruby -*-
|
2
|
+
# vi: set ft=ruby :
|
3
|
+
VAGRANTFILE_API_VERSION = "2"
|
4
|
+
|
5
|
+
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
6
|
+
config.vm.box = "dummy"
|
7
|
+
config.vm.box_url = "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"
|
8
|
+
|
9
|
+
config.vm.synced_folder ".", "/vagrant", disabled: true
|
10
|
+
|
11
|
+
config.vm.define :amazon_linux do |machine|
|
12
|
+
machine.vm.provider :aws do |aws, override|
|
13
|
+
aws.access_key_id = ENV["AWS_ACCESS_KEY_ID"]
|
14
|
+
aws.secret_access_key = ENV["AWS_SECRET_ACCESS_KEY"]
|
15
|
+
aws.keypair_name = ENV["EC2_KEYPAIR_NAME"]
|
16
|
+
aws.instance_type = "m1.large"
|
17
|
+
aws.region = ENV["AWS_REGION"] || 'us-east-1'
|
18
|
+
aws.terminate_on_shutdown = true
|
19
|
+
aws.ami = ENV["AMAZON_LINUX_AMI"] || "ami-8e682ce6"
|
20
|
+
|
21
|
+
override.ssh.username = "ec2-user"
|
22
|
+
override.ssh.private_key_path = ENV["EC2_PRIVATE_KEY_PATH"] || ENV["EC2_KEYPAIR_NAME"] + ".pem"
|
23
|
+
override.ssh.pty = true
|
24
|
+
|
25
|
+
override.vm.provision "shell", inline: <<-SH
|
26
|
+
echo cronicle | passwd --stdin ec2-user
|
27
|
+
echo 'ec2-user ALL=(ALL) ALL' > /etc/sudoers.d/cloud-init
|
28
|
+
SH
|
29
|
+
|
30
|
+
override.vm.boot_timeout = 180
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
config.vm.define :ubuntu do |machine|
|
35
|
+
machine.vm.provider :aws do |aws, override|
|
36
|
+
aws.access_key_id = ENV["AWS_ACCESS_KEY_ID"]
|
37
|
+
aws.secret_access_key = ENV["AWS_SECRET_ACCESS_KEY"]
|
38
|
+
aws.keypair_name = ENV["EC2_KEYPAIR_NAME"]
|
39
|
+
aws.instance_type = "m1.large"
|
40
|
+
aws.region = ENV["AWS_REGION"] || 'us-east-1'
|
41
|
+
aws.terminate_on_shutdown = true
|
42
|
+
aws.ami = ENV['UBUNTU_AMI'] || "ami-84562dec"
|
43
|
+
|
44
|
+
override.ssh.username = "ubuntu"
|
45
|
+
override.ssh.private_key_path = ENV["EC2_PRIVATE_KEY_PATH"] || ENV["EC2_KEYPAIR_NAME"] + ".pem"
|
46
|
+
override.ssh.pty = true
|
47
|
+
|
48
|
+
override.vm.provision "shell", inline: <<-SH
|
49
|
+
echo ubuntu:cronicle | chpasswd
|
50
|
+
echo 'ubuntu ALL=(ALL) ALL' > /etc/sudoers.d/90-cloud-init-users
|
51
|
+
SH
|
52
|
+
|
53
|
+
override.vm.boot_timeout = 180
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/bin/cronicle
ADDED
data/cronicle.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cronicle/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'cronicle'
|
8
|
+
spec.version = Cronicle::VERSION
|
9
|
+
spec.authors = ['Genki Sugawara']
|
10
|
+
spec.email = ['sgwr_dts@yahoo.co.jp']
|
11
|
+
spec.summary = %q{It is a tool for execute script, and define cron on remote hosts.}
|
12
|
+
spec.description = %q{It is a tool for execute script, and define cron on remote hosts.}
|
13
|
+
spec.homepage = 'https://github.com/winebarrel/cronicle'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_dependency 'sourcify', '~> 0.6.0.rc4'
|
22
|
+
spec.add_dependency 'sshkit', '~> 1.6.1'
|
23
|
+
spec.add_dependency 'thor'
|
24
|
+
spec.add_dependency 'colorize'
|
25
|
+
spec.add_dependency 'parallel'
|
26
|
+
spec.add_dependency 'diffy'
|
27
|
+
spec.add_development_dependency 'bundler'
|
28
|
+
spec.add_development_dependency 'rake'
|
29
|
+
spec.add_development_dependency 'rspec', '>= 3.0.0'
|
30
|
+
spec.add_development_dependency 'serverspec'
|
31
|
+
end
|
Binary file
|
data/lib/cronicle.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
require 'diffy'
|
3
|
+
require 'json'
|
4
|
+
require 'parallel'
|
5
|
+
require 'set'
|
6
|
+
require 'shellwords'
|
7
|
+
require 'singleton'
|
8
|
+
require 'sourcify'
|
9
|
+
require 'sshkit'
|
10
|
+
require 'sshkit/dsl'
|
11
|
+
require 'tempfile'
|
12
|
+
require 'thor'
|
13
|
+
|
14
|
+
module Cronicle; end
|
15
|
+
require 'cronicle/ext/hash_ext'
|
16
|
+
require 'cronicle/ext/string_ext'
|
17
|
+
require 'cronicle/ext/sshkit_ext'
|
18
|
+
require 'cronicle/logger'
|
19
|
+
require 'cronicle/utils'
|
20
|
+
require 'cronicle/client'
|
21
|
+
require 'cronicle/cli'
|
22
|
+
require 'cronicle/driver'
|
23
|
+
require 'cronicle/dsl'
|
24
|
+
require 'cronicle/dsl/context'
|
25
|
+
require 'cronicle/dsl/context/job'
|
26
|
+
require 'cronicle/exporter'
|
27
|
+
require 'cronicle/host_list'
|
28
|
+
require 'cronicle/version'
|
data/lib/cronicle/cli.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
class Cronicle::CLI < Thor
|
2
|
+
include Cronicle::Logger::Helper
|
3
|
+
|
4
|
+
class_option 'file', :aliases => '-f', :desc => 'Job definition file', :default => 'Jobfile'
|
5
|
+
class_option 'hosts', :aliases => '-h', :desc => 'Hosts definition file'
|
6
|
+
class_option 'target-roles', :aliases => '-r', :desc => 'Target host role list', :type => :array
|
7
|
+
class_option 'sudo-password', :aliases => '-p', :desc => 'Sudo password'
|
8
|
+
class_option 'dry-run', :desc => 'Do not actually change', :type => :boolean, :default => false
|
9
|
+
class_option 'ssh-config', :aliases => '-c', :desc => 'OpenSSH configuration file', :default => nil
|
10
|
+
class_option 'connection-timeout', :desc => 'SSH connection timeout', :type => :numeric, :default => nil
|
11
|
+
class_option 'concurrency', :desc => 'SSH concurrency', :type => :numeric, :default => Cronicle::Client::DEFAULTS[:concurrency]
|
12
|
+
class_option 'libexec', :desc => 'Cronicle libexec path', :default => Cronicle::Client::DEFAULTS[:libexec]
|
13
|
+
class_option 'verbose', :aliases => '-v', :desc => 'Verbose mode', :type => :boolean, :default => false
|
14
|
+
class_option 'debug', :desc => 'Debug mode', :type => :boolean, :default => false
|
15
|
+
class_option 'color', :desc => 'Colorize log', :type => :boolean, :default => true
|
16
|
+
|
17
|
+
def initialize(*args)
|
18
|
+
super
|
19
|
+
|
20
|
+
if options['debug']
|
21
|
+
Cronicle::Logger.instance.set_debug(true)
|
22
|
+
end
|
23
|
+
|
24
|
+
if not $stdin.tty? or not options['color']
|
25
|
+
String.disable_colorization = true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
desc 'exec JOB_NAME', 'Execute a job on remote hosts'
|
30
|
+
def exec(job_name)
|
31
|
+
with_logging do
|
32
|
+
set_ssh_options
|
33
|
+
client.exec(jobfile, job_name)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
desc 'apply', 'Apply cron jobs to remote hosts'
|
38
|
+
def apply
|
39
|
+
with_logging do
|
40
|
+
set_ssh_options
|
41
|
+
client.apply(jobfile)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def with_logging
|
48
|
+
begin
|
49
|
+
yield
|
50
|
+
rescue => e
|
51
|
+
if options['debug']
|
52
|
+
raise e
|
53
|
+
else
|
54
|
+
log(:error, e.message, :color => :red)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def client
|
60
|
+
Cronicle::Client.new(host_list, client_options)
|
61
|
+
end
|
62
|
+
|
63
|
+
def jobfile
|
64
|
+
file = options['file']
|
65
|
+
|
66
|
+
unless File.exist?(file)
|
67
|
+
raise Thor::Error, "No Jobfile found (looking for: #{file})"
|
68
|
+
end
|
69
|
+
|
70
|
+
file
|
71
|
+
end
|
72
|
+
|
73
|
+
def host_list
|
74
|
+
Cronicle::HostList.new(
|
75
|
+
options.fetch('hosts', ''),
|
76
|
+
host_list_options
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def client_options
|
81
|
+
{
|
82
|
+
:sudo_password => options['sudo-password'],
|
83
|
+
:concurrency => options['concurrency'],
|
84
|
+
:libexec => options['libexec'],
|
85
|
+
:dry_run => options['dry-run'],
|
86
|
+
:verbose => options['verbose']
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def host_list_options
|
91
|
+
{
|
92
|
+
:roles => options['target-roles']
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
def set_ssh_options
|
97
|
+
conn_timeout = options['connection-timeout']
|
98
|
+
ssh_config = options['ssh-config']
|
99
|
+
|
100
|
+
SSHKit::Backend::Netssh.configure do |ssh|
|
101
|
+
ssh.connection_timeout = conn_timeout if conn_timeout
|
102
|
+
ssh.ssh_options = {:config => ssh_config} if ssh_config
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|