itamae-mitsurin 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +7 -0
  5. data/LICENSE.txt +26 -0
  6. data/README.md +44 -0
  7. data/Rakefile +55 -0
  8. data/bin/itamae +5 -0
  9. data/bin/itamae-mitsurin +5 -0
  10. data/itamae-mitsurin.gemspec +35 -0
  11. data/lib/itamae.rb +18 -0
  12. data/lib/itamae/backend.rb +293 -0
  13. data/lib/itamae/cli.rb +86 -0
  14. data/lib/itamae/definition.rb +40 -0
  15. data/lib/itamae/ext.rb +1 -0
  16. data/lib/itamae/ext/specinfra.rb +39 -0
  17. data/lib/itamae/generators.rb +20 -0
  18. data/lib/itamae/generators/cookbook.rb +22 -0
  19. data/lib/itamae/generators/project.rb +22 -0
  20. data/lib/itamae/generators/role.rb +22 -0
  21. data/lib/itamae/generators/templates/cookbook/default.rb +0 -0
  22. data/lib/itamae/generators/templates/cookbook/files/.keep +0 -0
  23. data/lib/itamae/generators/templates/cookbook/templates/.keep +0 -0
  24. data/lib/itamae/generators/templates/project/Gemfile +4 -0
  25. data/lib/itamae/generators/templates/project/cookbooks/.keep +0 -0
  26. data/lib/itamae/generators/templates/project/roles/.keep +0 -0
  27. data/lib/itamae/generators/templates/role/default.rb +0 -0
  28. data/lib/itamae/generators/templates/role/files/.keep +0 -0
  29. data/lib/itamae/generators/templates/role/templates/.keep +0 -0
  30. data/lib/itamae/handler.rb +21 -0
  31. data/lib/itamae/handler/base.rb +40 -0
  32. data/lib/itamae/handler/debug.rb +10 -0
  33. data/lib/itamae/handler/fluentd.rb +44 -0
  34. data/lib/itamae/handler/json.rb +22 -0
  35. data/lib/itamae/handler_proxy.rb +38 -0
  36. data/lib/itamae/logger.rb +124 -0
  37. data/lib/itamae/mitsurin.rb +13 -0
  38. data/lib/itamae/mitsurin/cli.rb +56 -0
  39. data/lib/itamae/mitsurin/creators.rb +19 -0
  40. data/lib/itamae/mitsurin/creators/cookbook.rb +24 -0
  41. data/lib/itamae/mitsurin/creators/project.rb +24 -0
  42. data/lib/itamae/mitsurin/creators/templates/project/.rspec +2 -0
  43. data/lib/itamae/mitsurin/creators/templates/project/Gemfile +3 -0
  44. data/lib/itamae/mitsurin/creators/templates/project/Rakefile +2 -0
  45. data/lib/itamae/mitsurin/creators/templates/project/environments/.keep +0 -0
  46. data/lib/itamae/mitsurin/creators/templates/project/environments/sample.json +7 -0
  47. data/lib/itamae/mitsurin/creators/templates/project/nodes/.keep +0 -0
  48. data/lib/itamae/mitsurin/creators/templates/project/nodes/sample01.json +8 -0
  49. data/lib/itamae/mitsurin/creators/templates/project/roles/.keep +0 -0
  50. data/lib/itamae/mitsurin/creators/templates/project/site-cookbooks/_base/_base/attributes/.keep +0 -0
  51. data/lib/itamae/mitsurin/creators/templates/project/site-cookbooks/_base/_base/files/.keep +0 -0
  52. data/lib/itamae/mitsurin/creators/templates/project/site-cookbooks/_base/_base/recipes/default.rb +0 -0
  53. data/lib/itamae/mitsurin/creators/templates/project/site-cookbooks/_base/_base/spec/default_spec.rb +2 -0
  54. data/lib/itamae/mitsurin/creators/templates/project/site-cookbooks/_base/_base/templates/.keep +0 -0
  55. data/lib/itamae/mitsurin/creators/templates/project/spec/spec_helper.rb +32 -0
  56. data/lib/itamae/mitsurin/creators/templates/project/tmp-nodes/.keep +0 -0
  57. data/lib/itamae/mitsurin/creators/templates/site-cookbooks/attributes/.keep +0 -0
  58. data/lib/itamae/mitsurin/creators/templates/site-cookbooks/files/.keep +0 -0
  59. data/lib/itamae/mitsurin/creators/templates/site-cookbooks/recipes/default.rb +0 -0
  60. data/lib/itamae/mitsurin/creators/templates/site-cookbooks/spec/.keep +0 -0
  61. data/lib/itamae/mitsurin/creators/templates/site-cookbooks/templates/.keep +0 -0
  62. data/lib/itamae/mitsurin/itamae_task.rb +199 -0
  63. data/lib/itamae/mitsurin/serverspec_task.rb +110 -0
  64. data/lib/itamae/mitsurin/version.rb +5 -0
  65. data/lib/itamae/mitsurin/version.txt +1 -0
  66. data/lib/itamae/node.rb +74 -0
  67. data/lib/itamae/notification.rb +46 -0
  68. data/lib/itamae/recipe.rb +171 -0
  69. data/lib/itamae/recipe_children.rb +86 -0
  70. data/lib/itamae/resource.rb +73 -0
  71. data/lib/itamae/resource/aws_ebs_volume.rb +84 -0
  72. data/lib/itamae/resource/base.rb +374 -0
  73. data/lib/itamae/resource/directory.rb +63 -0
  74. data/lib/itamae/resource/execute.rb +26 -0
  75. data/lib/itamae/resource/file.rb +176 -0
  76. data/lib/itamae/resource/gem_package.rb +81 -0
  77. data/lib/itamae/resource/git.rb +94 -0
  78. data/lib/itamae/resource/group.rb +42 -0
  79. data/lib/itamae/resource/http_request.rb +71 -0
  80. data/lib/itamae/resource/link.rb +33 -0
  81. data/lib/itamae/resource/local_ruby_block.rb +15 -0
  82. data/lib/itamae/resource/package.rb +44 -0
  83. data/lib/itamae/resource/remote_directory.rb +84 -0
  84. data/lib/itamae/resource/remote_file.rb +54 -0
  85. data/lib/itamae/resource/service.rb +69 -0
  86. data/lib/itamae/resource/template.rb +53 -0
  87. data/lib/itamae/resource/user.rb +93 -0
  88. data/lib/itamae/runner.rb +124 -0
  89. data/spec/integration/Vagrantfile +35 -0
  90. data/spec/integration/default_spec.rb +226 -0
  91. data/spec/integration/recipes/default.rb +423 -0
  92. data/spec/integration/recipes/default2.rb +6 -0
  93. data/spec/integration/recipes/define/default.rb +6 -0
  94. data/spec/integration/recipes/define/files/remote_file_in_definition +1 -0
  95. data/spec/integration/recipes/dry_run.rb +6 -0
  96. data/spec/integration/recipes/files/remote_file_auto +1 -0
  97. data/spec/integration/recipes/hello.erb +6 -0
  98. data/spec/integration/recipes/hello.txt +1 -0
  99. data/spec/integration/recipes/included.rb +9 -0
  100. data/spec/integration/recipes/node.json +3 -0
  101. data/spec/integration/recipes/redefine.rb +20 -0
  102. data/spec/integration/recipes/templates/template_auto.erb +6 -0
  103. data/spec/integration/spec_helper.rb +42 -0
  104. data/spec/unit/lib/itamae/backend_spec.rb +95 -0
  105. data/spec/unit/lib/itamae/handler/base_spec.rb +34 -0
  106. data/spec/unit/lib/itamae/handler/fluentd_spec.rb +19 -0
  107. data/spec/unit/lib/itamae/handler_proxy_spec.rb +38 -0
  108. data/spec/unit/lib/itamae/handler_spec.rb +11 -0
  109. data/spec/unit/lib/itamae/node_spec.rb +14 -0
  110. data/spec/unit/lib/itamae/recipe_spec.rb +6 -0
  111. data/spec/unit/lib/itamae/resource/base_spec.rb +127 -0
  112. data/spec/unit/lib/itamae/resource_spec.rb +23 -0
  113. data/spec/unit/lib/itamae/runner_spec.rb +32 -0
  114. data/spec/unit/spec_helper.rb +23 -0
  115. metadata +393 -0
@@ -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
+
@@ -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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'itamae'
4
+ # gem 'serverspec'
@@ -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,10 @@
1
+ module Itamae
2
+ module Handler
3
+ class Debug < Base
4
+ def event(type, payload = {})
5
+ super
6
+ Itamae.logger.info("EVENT:#{type} #{payload}")
7
+ end
8
+ end
9
+ end
10
+ 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
+