itamae-mitsurin 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 +19 -0
- data/.rspec +2 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +26 -0
- data/README.md +44 -0
- data/Rakefile +55 -0
- data/bin/itamae +5 -0
- data/bin/itamae-mitsurin +5 -0
- data/itamae-mitsurin.gemspec +35 -0
- data/lib/itamae.rb +18 -0
- data/lib/itamae/backend.rb +293 -0
- data/lib/itamae/cli.rb +86 -0
- data/lib/itamae/definition.rb +40 -0
- data/lib/itamae/ext.rb +1 -0
- data/lib/itamae/ext/specinfra.rb +39 -0
- data/lib/itamae/generators.rb +20 -0
- data/lib/itamae/generators/cookbook.rb +22 -0
- data/lib/itamae/generators/project.rb +22 -0
- data/lib/itamae/generators/role.rb +22 -0
- data/lib/itamae/generators/templates/cookbook/default.rb +0 -0
- data/lib/itamae/generators/templates/cookbook/files/.keep +0 -0
- data/lib/itamae/generators/templates/cookbook/templates/.keep +0 -0
- data/lib/itamae/generators/templates/project/Gemfile +4 -0
- data/lib/itamae/generators/templates/project/cookbooks/.keep +0 -0
- data/lib/itamae/generators/templates/project/roles/.keep +0 -0
- data/lib/itamae/generators/templates/role/default.rb +0 -0
- data/lib/itamae/generators/templates/role/files/.keep +0 -0
- data/lib/itamae/generators/templates/role/templates/.keep +0 -0
- data/lib/itamae/handler.rb +21 -0
- data/lib/itamae/handler/base.rb +40 -0
- data/lib/itamae/handler/debug.rb +10 -0
- data/lib/itamae/handler/fluentd.rb +44 -0
- data/lib/itamae/handler/json.rb +22 -0
- data/lib/itamae/handler_proxy.rb +38 -0
- data/lib/itamae/logger.rb +124 -0
- data/lib/itamae/mitsurin.rb +13 -0
- data/lib/itamae/mitsurin/cli.rb +56 -0
- data/lib/itamae/mitsurin/creators.rb +19 -0
- data/lib/itamae/mitsurin/creators/cookbook.rb +24 -0
- data/lib/itamae/mitsurin/creators/project.rb +24 -0
- data/lib/itamae/mitsurin/creators/templates/project/.rspec +2 -0
- data/lib/itamae/mitsurin/creators/templates/project/Gemfile +3 -0
- data/lib/itamae/mitsurin/creators/templates/project/Rakefile +2 -0
- data/lib/itamae/mitsurin/creators/templates/project/environments/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/project/environments/sample.json +7 -0
- data/lib/itamae/mitsurin/creators/templates/project/nodes/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/project/nodes/sample01.json +8 -0
- data/lib/itamae/mitsurin/creators/templates/project/roles/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/project/site-cookbooks/_base/_base/attributes/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/project/site-cookbooks/_base/_base/files/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/project/site-cookbooks/_base/_base/recipes/default.rb +0 -0
- data/lib/itamae/mitsurin/creators/templates/project/site-cookbooks/_base/_base/spec/default_spec.rb +2 -0
- data/lib/itamae/mitsurin/creators/templates/project/site-cookbooks/_base/_base/templates/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/project/spec/spec_helper.rb +32 -0
- data/lib/itamae/mitsurin/creators/templates/project/tmp-nodes/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/site-cookbooks/attributes/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/site-cookbooks/files/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/site-cookbooks/recipes/default.rb +0 -0
- data/lib/itamae/mitsurin/creators/templates/site-cookbooks/spec/.keep +0 -0
- data/lib/itamae/mitsurin/creators/templates/site-cookbooks/templates/.keep +0 -0
- data/lib/itamae/mitsurin/itamae_task.rb +199 -0
- data/lib/itamae/mitsurin/serverspec_task.rb +110 -0
- data/lib/itamae/mitsurin/version.rb +5 -0
- data/lib/itamae/mitsurin/version.txt +1 -0
- data/lib/itamae/node.rb +74 -0
- data/lib/itamae/notification.rb +46 -0
- data/lib/itamae/recipe.rb +171 -0
- data/lib/itamae/recipe_children.rb +86 -0
- data/lib/itamae/resource.rb +73 -0
- data/lib/itamae/resource/aws_ebs_volume.rb +84 -0
- data/lib/itamae/resource/base.rb +374 -0
- data/lib/itamae/resource/directory.rb +63 -0
- data/lib/itamae/resource/execute.rb +26 -0
- data/lib/itamae/resource/file.rb +176 -0
- data/lib/itamae/resource/gem_package.rb +81 -0
- data/lib/itamae/resource/git.rb +94 -0
- data/lib/itamae/resource/group.rb +42 -0
- data/lib/itamae/resource/http_request.rb +71 -0
- data/lib/itamae/resource/link.rb +33 -0
- data/lib/itamae/resource/local_ruby_block.rb +15 -0
- data/lib/itamae/resource/package.rb +44 -0
- data/lib/itamae/resource/remote_directory.rb +84 -0
- data/lib/itamae/resource/remote_file.rb +54 -0
- data/lib/itamae/resource/service.rb +69 -0
- data/lib/itamae/resource/template.rb +53 -0
- data/lib/itamae/resource/user.rb +93 -0
- data/lib/itamae/runner.rb +124 -0
- data/spec/integration/Vagrantfile +35 -0
- data/spec/integration/default_spec.rb +226 -0
- data/spec/integration/recipes/default.rb +423 -0
- data/spec/integration/recipes/default2.rb +6 -0
- data/spec/integration/recipes/define/default.rb +6 -0
- data/spec/integration/recipes/define/files/remote_file_in_definition +1 -0
- data/spec/integration/recipes/dry_run.rb +6 -0
- data/spec/integration/recipes/files/remote_file_auto +1 -0
- data/spec/integration/recipes/hello.erb +6 -0
- data/spec/integration/recipes/hello.txt +1 -0
- data/spec/integration/recipes/included.rb +9 -0
- data/spec/integration/recipes/node.json +3 -0
- data/spec/integration/recipes/redefine.rb +20 -0
- data/spec/integration/recipes/templates/template_auto.erb +6 -0
- data/spec/integration/spec_helper.rb +42 -0
- data/spec/unit/lib/itamae/backend_spec.rb +95 -0
- data/spec/unit/lib/itamae/handler/base_spec.rb +34 -0
- data/spec/unit/lib/itamae/handler/fluentd_spec.rb +19 -0
- data/spec/unit/lib/itamae/handler_proxy_spec.rb +38 -0
- data/spec/unit/lib/itamae/handler_spec.rb +11 -0
- data/spec/unit/lib/itamae/node_spec.rb +14 -0
- data/spec/unit/lib/itamae/recipe_spec.rb +6 -0
- data/spec/unit/lib/itamae/resource/base_spec.rb +127 -0
- data/spec/unit/lib/itamae/resource_spec.rb +23 -0
- data/spec/unit/lib/itamae/runner_spec.rb +32 -0
- data/spec/unit/spec_helper.rb +23 -0
- metadata +393 -0
data/lib/itamae/cli.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'itamae'
|
2
|
+
require 'thor'
|
3
|
+
|
4
|
+
module Itamae
|
5
|
+
class CLI < Thor
|
6
|
+
|
7
|
+
class_option :log_level, type: :string, aliases: ['-l'], default: 'info'
|
8
|
+
class_option :color, type: :boolean, default: true
|
9
|
+
class_option :config, type: :string, aliases: ['-c']
|
10
|
+
|
11
|
+
def initialize(*)
|
12
|
+
super
|
13
|
+
|
14
|
+
Itamae.logger.level = ::Logger.const_get(options[:log_level].upcase)
|
15
|
+
Itamae.logger.formatter.colored = options[:color]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.define_exec_options
|
19
|
+
option :recipe_graph, type: :string, desc: "[EXPERIMENTAL] Write recipe dependency graph in DOT", banner: "PATH"
|
20
|
+
option :node_json, type: :string, aliases: ['-j']
|
21
|
+
option :node_yaml, type: :string, aliases: ['-y']
|
22
|
+
option :dry_run, type: :boolean, aliases: ['-n']
|
23
|
+
option :shell, type: :string, default: "/bin/sh"
|
24
|
+
option :ohai, type: :boolean, default: false, desc: "This option is DEPRECATED and will be unavailable."
|
25
|
+
option :profile, type: :string, desc: "[EXPERIMENTAL] Save profiling data", banner: "PATH"
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "local RECIPE [RECIPE...]", "Run Itamae locally"
|
29
|
+
define_exec_options
|
30
|
+
def local(*recipe_files)
|
31
|
+
if recipe_files.empty?
|
32
|
+
raise "Please specify recipe files."
|
33
|
+
end
|
34
|
+
|
35
|
+
Runner.run(recipe_files, :local, options)
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "ssh RECIPE [RECIPE...]", "Run Itamae via ssh"
|
39
|
+
define_exec_options
|
40
|
+
option :host, type: :string, aliases: ['-h']
|
41
|
+
option :user, type: :string, aliases: ['-u']
|
42
|
+
option :key, type: :string, aliases: ['-i']
|
43
|
+
option :port, type: :numeric, aliases: ['-p']
|
44
|
+
option :vagrant, type: :boolean, default: false
|
45
|
+
option :ask_password, type: :boolean, default: false
|
46
|
+
option :sudo, type: :boolean, default: true
|
47
|
+
def ssh(*recipe_files)
|
48
|
+
if recipe_files.empty?
|
49
|
+
raise "Please specify recipe files."
|
50
|
+
end
|
51
|
+
|
52
|
+
unless options[:host] || options[:vagrant]
|
53
|
+
raise "Please set '-h <hostname>' or '--vagrant'"
|
54
|
+
end
|
55
|
+
|
56
|
+
Runner.run(recipe_files, :ssh, options)
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "docker RECIPE [RECIPE...]", "Create Docker image"
|
60
|
+
define_exec_options
|
61
|
+
option :image, type: :string, desc: "This option or 'container' option is required."
|
62
|
+
option :container, type: :string, desc: "This option or 'image' option is required."
|
63
|
+
option :tls_verify_peer, type: :boolean, default: true
|
64
|
+
def docker(*recipe_files)
|
65
|
+
if recipe_files.empty?
|
66
|
+
raise "Please specify recipe files."
|
67
|
+
end
|
68
|
+
|
69
|
+
Runner.run(recipe_files, :docker, options)
|
70
|
+
end
|
71
|
+
|
72
|
+
desc "version", "Print version"
|
73
|
+
def version
|
74
|
+
puts "Itamae v#{Itamae::Mitsurin::VERSION}"
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
def options
|
79
|
+
@itamae_options ||= super.dup.tap do |options|
|
80
|
+
if config = options[:config]
|
81
|
+
options.merge!(YAML.load_file(config))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'itamae'
|
2
|
+
|
3
|
+
module Itamae
|
4
|
+
class Definition < Resource::Base
|
5
|
+
class << self
|
6
|
+
attr_accessor :definition_block
|
7
|
+
attr_accessor :defined_in_recipe
|
8
|
+
|
9
|
+
def create_class(name, params, defined_in_recipe, &block)
|
10
|
+
Class.new(self).tap do |klass|
|
11
|
+
klass.definition_block = block
|
12
|
+
klass.defined_in_recipe = defined_in_recipe
|
13
|
+
|
14
|
+
klass.define_attribute :action, default: :run
|
15
|
+
params.each_pair do |key, value|
|
16
|
+
klass.define_attribute key.to_sym, type: Object, default: value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(*args)
|
23
|
+
super
|
24
|
+
|
25
|
+
r = Recipe::RecipeFromDefinition.new(
|
26
|
+
runner,
|
27
|
+
self.class.defined_in_recipe.path,
|
28
|
+
)
|
29
|
+
recipe.children << r
|
30
|
+
|
31
|
+
r.definition = self
|
32
|
+
r.load(params: @attributes.merge(name: resource_name))
|
33
|
+
end
|
34
|
+
|
35
|
+
def run(*args)
|
36
|
+
# nothing
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
data/lib/itamae/ext.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'itamae/ext/specinfra'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# TODO: Send patches to Specinfra
|
2
|
+
|
3
|
+
module Specinfra
|
4
|
+
module Backend
|
5
|
+
class Base
|
6
|
+
def receive_file(from, to = nil)
|
7
|
+
raise NotImplementedError
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Exec < Base
|
12
|
+
def receive_file(from, to = nil)
|
13
|
+
if to
|
14
|
+
FileUtils.cp(from, to)
|
15
|
+
else
|
16
|
+
::File.read(from)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Ssh < Exec
|
22
|
+
def receive_file(from, to = nil)
|
23
|
+
scp_download!(from, to)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def scp_download!(from, to, opt={})
|
29
|
+
if get_config(:scp).nil?
|
30
|
+
set_config(:scp, create_scp)
|
31
|
+
end
|
32
|
+
|
33
|
+
scp = get_config(:scp)
|
34
|
+
scp.download!(from, to, opt)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "itamae/generators/cookbook"
|
2
|
+
require "itamae/generators/project"
|
3
|
+
require "itamae/generators/role"
|
4
|
+
|
5
|
+
module Itamae
|
6
|
+
module Generators
|
7
|
+
def self.find(target)
|
8
|
+
case target
|
9
|
+
when 'cookbook'
|
10
|
+
Cookbook
|
11
|
+
when 'project'
|
12
|
+
Project
|
13
|
+
when 'role'
|
14
|
+
Role
|
15
|
+
else
|
16
|
+
raise "Unexpected target: #{target}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'thor/group'
|
3
|
+
|
4
|
+
module Itamae
|
5
|
+
module Generators
|
6
|
+
class Cookbook < Thor::Group
|
7
|
+
include Thor::Actions
|
8
|
+
|
9
|
+
def self.source_root
|
10
|
+
File.expand_path('../templates/cookbook', __FILE__)
|
11
|
+
end
|
12
|
+
|
13
|
+
def copy_files
|
14
|
+
directory '.'
|
15
|
+
end
|
16
|
+
|
17
|
+
def remove_files
|
18
|
+
remove_file '.'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'thor/group'
|
3
|
+
|
4
|
+
module Itamae
|
5
|
+
module Generators
|
6
|
+
class Project < Thor::Group
|
7
|
+
include Thor::Actions
|
8
|
+
|
9
|
+
def self.source_root
|
10
|
+
File.dirname(__FILE__) + '/templates/project'
|
11
|
+
end
|
12
|
+
|
13
|
+
def copy_files
|
14
|
+
directory '.'
|
15
|
+
end
|
16
|
+
|
17
|
+
def bundle
|
18
|
+
run 'bundle install'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'thor/group'
|
3
|
+
|
4
|
+
module Itamae
|
5
|
+
module Generators
|
6
|
+
class Role < Thor::Group
|
7
|
+
include Thor::Actions
|
8
|
+
|
9
|
+
def self.source_root
|
10
|
+
File.expand_path('../templates/role', __FILE__)
|
11
|
+
end
|
12
|
+
|
13
|
+
def copy_files
|
14
|
+
directory '.'
|
15
|
+
end
|
16
|
+
|
17
|
+
def remove_files
|
18
|
+
remove_file '.'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'itamae/handler/base'
|
2
|
+
|
3
|
+
module Itamae
|
4
|
+
module Handler
|
5
|
+
def self.from_type(type)
|
6
|
+
first_time = true
|
7
|
+
|
8
|
+
class_name = type.split('_').map(&:capitalize).join
|
9
|
+
self.const_get(class_name)
|
10
|
+
rescue NameError
|
11
|
+
require "itamae/handler/#{type}"
|
12
|
+
|
13
|
+
if first_time
|
14
|
+
first_time = false
|
15
|
+
retry
|
16
|
+
else
|
17
|
+
raise
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Itamae
|
4
|
+
module Handler
|
5
|
+
class Base
|
6
|
+
attr_reader :recipes, :resources, :actions
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
@options = options
|
10
|
+
|
11
|
+
@recipes = []
|
12
|
+
@resources = []
|
13
|
+
@actions = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def event(type, payload = {})
|
17
|
+
case type
|
18
|
+
when :recipe_started
|
19
|
+
@recipes << payload
|
20
|
+
when :recipe_completed, :recipe_failed
|
21
|
+
@recipes.pop
|
22
|
+
when :resource_started
|
23
|
+
@resources << payload
|
24
|
+
when :resource_completed, :resource_failed
|
25
|
+
@resources.pop
|
26
|
+
when :action_started
|
27
|
+
@actions << payload
|
28
|
+
when :action_completed, :action_failed
|
29
|
+
@actions.pop
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def hostname
|
36
|
+
@hostname ||= @options['hostname'] || Socket.gethostname
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Itamae
|
2
|
+
module Handler
|
3
|
+
class Fluentd < Base
|
4
|
+
attr_accessor :fluent_logger # for test
|
5
|
+
|
6
|
+
def initialize(*)
|
7
|
+
super
|
8
|
+
load_fluent_logger
|
9
|
+
end
|
10
|
+
|
11
|
+
def event(type, payload = {})
|
12
|
+
super
|
13
|
+
|
14
|
+
unless @fluent_logger.post(type, payload.merge(hostname: hostname))
|
15
|
+
Itamae.logger.warn "Sending logs to Fluentd failed: #{@fluent_logger.last_error}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def load_fluent_logger
|
22
|
+
begin
|
23
|
+
require 'fluent-logger'
|
24
|
+
rescue LoadError
|
25
|
+
raise "Loading fluent-logger gem failed. Please install 'fluent-logger' gem to use fluentd handler."
|
26
|
+
end
|
27
|
+
|
28
|
+
@fluent_logger = Fluent::Logger::FluentLogger.new(tag_prefix, host: fluentd_host, port: fluentd_port)
|
29
|
+
end
|
30
|
+
|
31
|
+
def tag_prefix
|
32
|
+
@options['tag_prefix'] || 'itamae_server'
|
33
|
+
end
|
34
|
+
|
35
|
+
def fluentd_host
|
36
|
+
@options['host'] || 'localhost'
|
37
|
+
end
|
38
|
+
|
39
|
+
def fluentd_port
|
40
|
+
(@options['port'] || 24224).to_i
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Itamae
|
2
|
+
module Handler
|
3
|
+
class Json < Base
|
4
|
+
def initialize(*)
|
5
|
+
super
|
6
|
+
require 'time'
|
7
|
+
open_file
|
8
|
+
end
|
9
|
+
|
10
|
+
def event(type, payload = {})
|
11
|
+
super
|
12
|
+
@f.puts({'time' => Time.now.iso8601, 'event' => type, 'payload' => payload}.to_json)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def open_file
|
18
|
+
@f = open(@options.fetch('path'), 'a')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Itamae
|
2
|
+
class HandlerProxy
|
3
|
+
def initialize
|
4
|
+
@instances = []
|
5
|
+
end
|
6
|
+
|
7
|
+
def register_instance(instance)
|
8
|
+
@instances << instance
|
9
|
+
end
|
10
|
+
|
11
|
+
def event(*args, &block)
|
12
|
+
if block_given?
|
13
|
+
_event_with_block(*args, &block)
|
14
|
+
else
|
15
|
+
_event(*args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def _event(*args)
|
22
|
+
@instances.each do |i|
|
23
|
+
i.event(*args)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def _event_with_block(event_name, *args, &block)
|
28
|
+
event("#{event_name}_started".to_sym, *args)
|
29
|
+
block.call
|
30
|
+
rescue
|
31
|
+
event("#{event_name}_failed".to_sym, *args)
|
32
|
+
raise
|
33
|
+
else
|
34
|
+
event("#{event_name}_completed".to_sym, *args)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|