Codebase list itamae / 29b92b0
New upstream version 1.10.7 Antonio Terceiro 4 years ago
56 changed file(s) with 923 addition(s) and 367 deletion(s). Raw diff Collapse all Expand all
1616 tmp
1717 .vagrant
1818 Gemfile.local
19 .ruby-version
0 language: ruby
1 sudo: required
2 dist: xenial
3 services:
4 - docker
5 rvm:
6 - 2.3
7 - 2.4
8 - 2.5
9 - 2.6
10 - 2.7
11 - ruby-head
12 bundler_args: "--jobs=4 --retry=3"
13 cache:
14 bundler: true
15
16 before_install:
17 - travis_retry gem install bundler --no-document || travis_retry gem install bundler --no-document -v 1.17.3
18
19 script:
20 - RUBYOPT=$SPEC_RUBYOPT bundle exec rake spec
21 notifications:
22 email: false
23 slack:
24 secure: PcecHsVS6lw89K5PllW8xFlzu0d04p6lYfxlUZL0/yp9flAczElJME4RshSMSkbnu5e2Iw8KUA2xB1jkAzDo9qYoXveaKyjkFUOb1ZxYIVxzzfoDDwUNTMmSoyjZjvbeBUcpxxmxy6nXa3zS+gA2ohqWhS9WTTlTqyM5RriDjZ8=
25 matrix:
26 allow_failures:
27 - rvm: ruby-head
28 include:
29 - rvm: 2.6
30 env: SPEC_RUBYOPT="--jit"
31 - rvm: ruby-head
32 env: SPEC_RUBYOPT="--jit"
33 branches:
34 only:
35 - master
0 ## Unreleased
1 [full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.10.7...master)
2
3 ## v1.10.7
4 [full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.10.6...v1.10.7)
5
6 Improvements
7
8 - [Improve `file` resource performance](https://github.com/itamae-kitchen/itamae/pull/310)
9
10 ## v1.10.6
11 [full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.10.5...v1.10.6)
12
13 Improvements
14
15 - [Don't use `sudo` when `--no-sudo` is passed](https://github.com/itamae-kitchen/itamae/pull/302)
16
17 ## v1.10.5
18 [full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.10.4...v1.10.5)
19
20 Improvements
21
22 - [Check http status code in `http_request` resource (by @takumin)](https://github.com/itamae-kitchen/itamae/pull/296)
23
24 ## v1.10.4
25 [full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.10.3...v1.10.4)
26
27 Bugfixes
28
29 - [Suppress Ruby warnings (by @pocke)](https://github.com/itamae-kitchen/itamae/pull/284)
30 - [Suppress Ruby warning (by @pocke)](https://github.com/itamae-kitchen/itamae/pull/287)
31 - [Run test cases correctly (by @pocke)](https://github.com/itamae-kitchen/itamae/pull/289)
32
33 Improvements
34
35 - [Add description to --tag option of docker subcommand (by @pocke)](https://github.com/itamae-kitchen/itamae/pull/286)
36 - [Refine `itamae docker`'s created message (by @pocke)](https://github.com/itamae-kitchen/itamae/pull/288)
37
38 ## v1.10.3
39 [full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.10.2...v1.10.3)
40
41 Bugfixes
42
43 - [Make `send_file` aware of `user` option](https://github.com/itamae-kitchen/itamae/pull/277)
44
45 ## v1.10.2
46 [full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.10.1...v1.10.2)
47
48 Bugfixes
49
50 - [Disable mash warnings (review catch up)](https://github.com/itamae-kitchen/itamae/pull/273)
51
52 ## v1.10.1
53 [full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.10.0...v1.10.1)
54
55 Bugfixes
56
57 - [fail `--ohai` option when using ohai v13.0.1 or higher](https://github.com/itamae-kitchen/itamae/pull/251)
58
59 ## v1.10.0
60 [full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.9.13...v1.10.0)
61
62 Features
63
64 - [Support `only_if` and `not_if` inside a `define`](https://github.com/itamae-kitchen/itamae/pull/271)
65
66 ## v1.9.13
67 [full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.9.12...v1.9.13)
68
69 Bugfixes
70
71 - [Fixed. Can not create empty file](https://github.com/itamae-kitchen/itamae/pull/269)
72
73 ## v1.9.12
74 [full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.9.11...v1.9.12)
75
76 Features
77
78 - [jail backend: add support of FreeBSD Jail (`itamae jail`)](https://github.com/itamae-kitchen/itamae/pull/249)
79
80 Bugfixes
81
82 - [docker backend: Fixed edit action of file resource doesn't work with docker backend](https://github.com/itamae-kitchen/itamae/pull/257)
83
84 Improvements
85
86 - [Print '(dry-run)' first in dry-run mode](https://github.com/itamae-kitchen/itamae/pull/252)
87
88 ## v1.9.11
89
90 Features
91
92 - [docker backend: Support image tagging](https://github.com/itamae-kitchen/itamae/pull/230)
93 - [docker backend: Support docker_container_create_options](https://github.com/itamae-kitchen/itamae/pull/231)
94
95
96 Bugfixes
97
98 - [Fix help subcommand](https://github.com/itamae-kitchen/itamae/pull/235)
99
0100 ## v1.9.10
1101
2102 Features
438538
439539 Improvements
440540
441 - `source :auto` accepts a template without .erb extention. (by @ryotarai)
541 - `source :auto` accepts a template without .erb extension. (by @ryotarai)
442542
443543 ## v1.1.21
444544
11
22 # Specify your gem's dependencies in itamae.gemspec
33 gemspec
4
5 gem 'vagrant', github: 'ryotarai/vagrant', branch: 'latest-bundler'
6 gem 'vagrant-digitalocean'
74
85 path = Pathname.new("Gemfile.local")
96 eval(path.read) if path.exist?
00 # [![](https://raw.githubusercontent.com/itamae-kitchen/itamae-logos/master/small/FA-Itamae-horizontal-01-180x72.png)](https://github.com/itamae-kitchen/itamae)
11
2 [![Gem Version](https://badge.fury.io/rb/itamae.svg)](http://badge.fury.io/rb/itamae) [![Code Climate](https://codeclimate.com/github/ryotarai/itamae/badges/gpa.svg)](https://codeclimate.com/github/ryotarai/itamae) [![wercker status](https://app.wercker.com/status/3e7be3b982d3671940a07e3ef45d9f5f/s/master "wercker status")](https://app.wercker.com/project/bykey/3e7be3b982d3671940a07e3ef45d9f5f) [![Slack](https://img.shields.io/badge/slack-join-blue.svg)](https://itamae-slackin.herokuapp.com/)
2 [![Gem Version](https://badge.fury.io/rb/itamae.svg)](http://badge.fury.io/rb/itamae) [![Code Climate](https://codeclimate.com/github/ryotarai/itamae/badges/gpa.svg)](https://codeclimate.com/github/ryotarai/itamae) [![Build Status](https://travis-ci.org/itamae-kitchen/itamae.svg?branch=master)](https://travis-ci.org/itamae-kitchen/itamae) [![Slack](https://img.shields.io/badge/slack-join-blue.svg)](https://join.slack.com/t/itamae/shared_invite/enQtNTExNTI3ODM1NTY5LTM5MWJlZTgwODE0YTUwMThiNzZjN2I1MGNlZjE2NjlmNzg5NTNlOTliMDhkNDNmNTQ2ZTgwMzZjNjI5NDJiZGI)
33
44 Simple and lightweight configuration management tool inspired by Chef.
55
7676
7777 ## Get Involved
7878
79 - [Join Slack team](https://itamae-slackin.herokuapp.com/)
79 - [Join Slack team](https://itamae-slackin.herokuapp.com)
8080
8181 ## Presentations / Articles
8282
22 require 'tempfile'
33 require 'net/ssh'
44
5 vagrant_bin = 'vagrant'
5 Dir['tasks/*.rb'].each do |file|
6 require_relative file
7 end
68
79 desc 'Run unit and integration specs.'
810 task :spec => ['spec:unit', 'spec:integration:all']
1416 end
1517
1618 namespace :integration do
17 targets = []
18 status = `cd spec/integration && #{vagrant_bin} status`
19 unless $?.exitstatus == 0
20 raise "vagrant status failed.\n#{status}"
21 end
19 targets = ["ubuntu:trusty"]
20 container_name = 'itamae'
2221
23 status.split("\n\n")[1].each_line do |line|
24 targets << line.match(/^[^ ]+/)[0]
25 end
26
27 task :all => targets
22 task :all => targets + ['spec:integration:local']
2823
2924 targets.each do |target|
3025 desc "Run provision and specs to #{target}"
31 task target => ["provision:#{target}", "serverspec:#{target}"]
26 task target => ["docker:#{target}", "provision:#{target}", "serverspec:#{target}", 'clean_docker_container']
27
28 namespace :docker do
29 desc "Run docker for #{target}"
30 task target do
31 sh "docker run --privileged -d --name #{container_name} #{target} /sbin/init"
32 end
33 end
3234
3335 namespace :provision do
36 desc "Run itamae to #{target}"
3437 task target do
35 config = Tempfile.new('', Dir.tmpdir)
36 env = {"VAGRANT_CWD" => File.expand_path('./spec/integration')}
37 system env, "#{vagrant_bin} up #{target}"
38 system env, "#{vagrant_bin} ssh-config #{target} > #{config.path}"
39 options = Net::SSH::Config.for(target, [config.path])
40
4138 suites = [
4239 [
4340 "spec/integration/recipes/default.rb",
4441 "spec/integration/recipes/default2.rb",
4542 "spec/integration/recipes/redefine.rb",
43 "spec/integration/recipes/docker.rb",
4644 ],
4745 [
4846 "--dry-run",
5048 ],
5149 ]
5250 suites.each do |suite|
53 cmd = %w!bundle exec bin/itamae ssh!
54 cmd << "-h" << options[:host_name]
55 cmd << "-u" << options[:user]
56 cmd << "-p" << options[:port].to_s
57 cmd << "-i" << options[:keys].first
51 cmd = %w!bundle exec ruby -w bin/itamae docker!
5852 cmd << "-l" << (ENV['LOG_LEVEL'] || 'debug')
5953 cmd << "-j" << "spec/integration/recipes/node.json"
54 cmd << "--container" << container_name
55 cmd << "--tag" << "itamae:latest"
6056 cmd += suite
6157
6258 p cmd
7066 namespace :serverspec do
7167 desc "Run serverspec tests to #{target}"
7268 RSpec::Core::RakeTask.new(target.to_sym) do |t|
73 ENV['TARGET_HOST'] = target
69 ENV['DOCKER_CONTAINER'] = container_name
7470 t.ruby_opts = '-I ./spec/integration'
75 t.pattern = "spec/integration/*_spec.rb"
71 t.pattern = "spec/integration/{default,docker}_spec.rb"
7672 end
73 end
74
75 desc 'Clean a docker container for test'
76 task :clean_docker_container do
77 sh('docker', 'rm', '-f', container_name)
7778 end
7879 end
7980 end
8081 end
8182
83 task :default => :spec
+0
-22
ci/destroy_old_droplets.rb less more
0 require 'net/https'
1 require 'json'
2 require 'time'
3
4 http = Net::HTTP.new("api.digitalocean.com", 443)
5 http.use_ssl = true
6
7 res = http.start do
8 http.get("/v2/droplets", "Authorization" => "Bearer #{ENV['DIGITALOCEAN_TOKEN']}")
9 end
10
11 droplets = JSON.parse(res.body)['droplets']
12 droplets.each do |droplet|
13 next unless /^itamae-/ =~ droplet['name']
14 if Time.now - Time.parse(droplet['created_at']) >= 60 * 60
15 puts "destroying #{droplet}..."
16 res = http.start do
17 http.delete("/v2/droplets/#{droplet['id']}", "Authorization" => "Bearer #{ENV['DIGITALOCEAN_TOKEN']}")
18 end
19 end
20 end
21
55 Gem::Specification.new do |spec|
66 spec.name = "itamae"
77 spec.version = Itamae::VERSION
8 spec.authors = ["Ryota Arai"]
9 spec.email = ["ryota.arai@gmail.com"]
8 spec.authors = ["Ryota Arai", "Yusuke Nakamura", "sue445"]
9 spec.email = ["ryota.arai@gmail.com", "yusuke1994525@gmail.com", "sue445@sue445.net"]
1010 spec.summary = %q{Simple Configuration Management Tool}
11 spec.homepage = "https://github.com/itamae-kitchen/itamae"
11 spec.homepage = "https://itamae.kitchen/"
1212 spec.license = "MIT"
13
14 if spec.respond_to?(:metadata)
15 spec.metadata["homepage_uri"] = spec.homepage
16 spec.metadata["source_code_uri"] = "https://github.com/itamae-kitchen/itamae"
17 spec.metadata["changelog_uri"] = "https://github.com/itamae-kitchen/itamae/blob/master/CHANGELOG.md"
18 else
19 raise "RubyGems 2.0 or newer is required to protect against " \
20 "public gem pushes."
21 end
1322
1423 spec.files = `git ls-files`.split($/)
1524 spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
2231 spec.add_runtime_dependency "ansi"
2332 spec.add_runtime_dependency "schash", "~> 0.1.0"
2433
25 spec.add_development_dependency "bundler", "~> 1.3"
34 spec.add_development_dependency "bundler", ">= 1.3"
2635 spec.add_development_dependency "rake"
2736 spec.add_development_dependency "rspec", "~> 3.0"
2837 spec.add_development_dependency "serverspec", "~> 2.1"
88 module Configuration
99 def self.sudo_password
1010 return ENV['SUDO_PASSWORD'] if ENV['SUDO_PASSWORD']
11 return @sudo_password if @sudo_password
11 return @sudo_password if defined?(@sudo_password)
1212
1313 # TODO: Fix this dirty hack
1414 return nil unless caller.any? {|call| call.include?('channel_data') }
9797 @backend.receive_file(src, dst)
9898 end
9999
100 def send_file(src, dst)
100 def send_file(src, dst, user: nil)
101101 Itamae.logger.debug "Sending a file from '#{src}' to '#{dst}'..."
102102 unless ::File.exist?(src)
103103 raise SourceNotExistError, "The file '#{src}' doesn't exist."
105105 unless ::File.file?(src)
106106 raise SourceNotExistError, "'#{src}' is not a file."
107107 end
108 @backend.send_file(src, dst)
108
109 if self.instance_of?(Backend::Local)
110 read_command = build_command("cat #{src.shellescape}", {})
111 write_command = build_command("cat > #{dst.shellescape}", user: user)
112 command = [read_command, write_command].join(' | ')
113 run_command(command)
114 else
115 @backend.send_file(src, dst)
116 end
109117 end
110118
111119 def send_directory(src, dst)
179187 end
180188
181189 user = options[:user]
182 if user
190 if user && use_sudo?
183191 command = "cd ~#{user.shellescape} ; #{command}"
184192 command = "sudo -H -u #{user.shellescape} -- #{shell.shellescape} -c #{command.shellescape}"
185193 end
186194
187195 command
196 end
197
198 def use_sudo?
199 return true unless @options.key?(:sudo)
200 !!@options[:sudo]
188201 end
189202
190203 def shell
207220 def create_specinfra_backend
208221 Specinfra::Backend::Exec.new(
209222 shell: @options[:shell],
223 )
224 end
225 end
226
227 class Jexec < Base
228 private
229 def create_specinfra_backend
230 Specinfra::Backend::Jexec.new(
231 shell: @options[:shell],
232 login_shell: @options[:login_shell],
233 jail_name: @options[:jail_name],
210234 )
211235 end
212236 end
269293 class Docker < Base
270294 def finalize
271295 image = @backend.commit_container
272 Itamae.logger.info "Image created: #{image.id}"
296 /\A(?<repo>.+?)(?:|:(?<tag>[^:]+))\z/.match(@options[:tag]) do |m|
297 image.tag(repo: m[:repo], tag: m[:tag])
298 end
299 log_message = "Image created: #{image.id}"
300 log_message << ", and tagged as #{@options[:tag]}" if @options[:tag]
301 Itamae.logger.info log_message
273302 end
274303
275304 private
288317 docker_image: @options[:image],
289318 docker_container: @options[:container],
290319 shell: @options[:shell],
320 docker_container_create_options: @options[:docker_container_create_options],
291321 )
292322 end
293323 end
44 class CLI < Thor
55 GENERATE_TARGETS = %w[cookbook role].freeze
66
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
117 def initialize(*)
128 super
139
14 Itamae.logger.level = ::Logger.const_get(options[:log_level].upcase)
15 Itamae.logger.formatter.colored = options[:color]
10 Itamae.logger.level = ::Logger.const_get(options[:log_level].upcase) if options[:log_level]
11 Itamae.logger.formatter.colored = options[:color] if options[:color]
1612 end
1713
1814 def self.define_exec_options
2521 option :ohai, type: :boolean, default: false, desc: "This option is DEPRECATED and will be unavailable."
2622 option :profile, type: :string, desc: "[EXPERIMENTAL] Save profiling data", banner: "PATH"
2723 option :detailed_exitcode, type: :boolean, default: false, desc: "exit code 0 - The run succeeded with no changes or failures, exit code 1 - The run failed, exit code 2 - The run succeeded, and some resources were changed"
24 option :log_level, type: :string, aliases: ['-l'], default: 'info'
25 option :color, type: :boolean, default: true
26 option :config, type: :string, aliases: ['-c']
2827 end
2928
3029 desc "local RECIPE [RECIPE...]", "Run Itamae locally"
6463 option :image, type: :string, desc: "This option or 'container' option is required."
6564 option :container, type: :string, desc: "This option or 'image' option is required."
6665 option :tls_verify_peer, type: :boolean, default: true
66 option :tag, type: :string, desc: 'Tag name of created docker image.'
6767 def docker(*recipe_files)
6868 if recipe_files.empty?
6969 raise "Please specify recipe files."
7070 end
7171
7272 run(recipe_files, :docker, options)
73 end
74
75 desc "jail RECIPE [RECIPE...]", "Run Itamae in jail"
76 define_exec_options
77 option :jail_name, type: :string, desc: "Jail Hostname"
78 def jail(*recipe_files)
79 if recipe_files.empty?
80 raise "Please specify recipe files."
81 end
82
83 run(recipe_files, :jexec, options)
7384 end
7485
7586 desc "version", "Print version"
0 require 'itamae'
1
20 module Itamae
31 class Definition < Resource::Base
42 class << self
1313 FileUtils.cp(from, to)
1414 else
1515 ::File.read(from)
16 end
17 end
18 end
19
20 class Docker < Exec
21 def receive_file(from, to = nil)
22 if to
23 send_file(from, to)
24 else
25 run_command("cat #{from}").stdout
1626 end
1727 end
1828 end
0 require 'itamae'
10 require 'logger'
21 require 'ansi/code'
32
7170 class Formatter
7271 attr_accessor :colored
7372
73 def initialize
74 @color = nil
75 end
76
7477 def call(severity, datetime, progname, msg)
7578 log = "%s : %s" % ["%5s" % severity, msg2str(msg)]
7679
0 require 'hashie'
1
2 module Itamae
3 class Mash < Hashie::Mash
4 disable_warnings
5 end
6 end
0 require 'itamae'
10 require 'hashie'
21 require 'json'
32 require 'schash'
98 attr_reader :mash
109
1110 def initialize(hash, backend)
12 @mash = Hashie::Mash.new(hash)
11 @mash = Itamae::Mash.new(hash)
1312 @backend = backend
1413 end
1514
4241 private
4342
4443 def _reverse_merge(other_hash)
45 Hashie::Mash.new(other_hash).merge(@mash)
44 Itamae::Mash.new(other_hash).merge(@mash)
4645 end
4746
4847 def method_missing(method, *args)
6261 def fetch_inventory_value(key)
6362 value = @backend.host_inventory[key]
6463 if value.is_a?(Hash)
65 value = Hashie::Mash.new(value)
64 value = Itamae::Mash.new(value)
6665 end
6766
6867 value
0 require 'itamae'
1
20 module Itamae
31 class Notification < Struct.new(:defined_in_resource, :action, :target_resource_desc, :timing)
42 def self.create(*args)
0 require 'itamae'
1
20 module Itamae
31 class Recipe
42 NotFoundError = Class.new(StandardError)
160158 context.instance_eval(&@definition.class.definition_block)
161159 end
162160
161 def run
162 if @definition.do_not_run_because_of_only_if?
163 Itamae.logger.debug "#{@definition.resource_type}[#{@definition.resource_name}] Execution skipped because of only_if attribute"
164 return
165 elsif @definition.do_not_run_because_of_not_if?
166 Itamae.logger.debug "#{@definition.resource_type}[#{@definition.resource_name}] Execution skipped because of not_if attribute"
167 return
168 end
169
170 super
171 end
172
163173 private
164174
165175 def show_banner
0 require 'itamae'
10 require 'shellwords'
21 require 'hashie'
32
1514 def initialize(resource)
1615 @resource = resource
1716
18 @attributes = Hashie::Mash.new
17 @attributes = Itamae::Mash.new
1918 @notifications = []
2019 @subscriptions = []
2120 @verify_commands = []
157156
158157 def resource_type
159158 self.class.name.split("::").last.scan(/[A-Z][^A-Z]+/).map(&:downcase).join('_')
159 end
160
161 def do_not_run_because_of_only_if?
162 @only_if_command &&
163 run_command(@only_if_command, error: false).exit_status != 0
164 end
165
166 def do_not_run_because_of_not_if?
167 @not_if_command &&
168 run_command(@not_if_command, error: false).exit_status == 0
160169 end
161170
162171 private
211220 end
212221
213222 def clear_current_attributes
214 @current_attributes = Hashie::Mash.new
223 @current_attributes = Itamae::Mash.new
215224 end
216225
217226 def pre_action
269278 end
270279 end
271280
272 def do_not_run_because_of_only_if?
273 @only_if_command &&
274 run_command(@only_if_command, error: false).exit_status != 0
275 end
276
277 def do_not_run_because_of_not_if?
278 @not_if_command &&
279 run_command(@not_if_command, error: false).exit_status == 0
280 end
281
282281 def backend
283282 runner.backend
284283 end
0 require 'itamae'
1
20 module Itamae
31 module Resource
42 class Directory < Base
0 require 'itamae'
1
20 module Itamae
31 module Resource
42 class Execute < Base
0 require 'itamae'
1
20 module Itamae
31 module Resource
42 class File < Base
108 define_attribute :group, type: String
119 define_attribute :block, type: Proc, default: proc {}
1210
11 class << self
12 attr_accessor :sha256sum_available
13 end
14
1315 def pre_action
1416 current.exist = run_specinfra(:check_file_is_file, attributes.path)
1517
2830 end
2931 end
3032
33 if exists_and_not_modified?
34 attributes.modified = false
35 return
36 end
37
3138 send_tempfile
3239 compare_file
3340 end
114121 def compare_file
115122 attributes.modified = false
116123 unless @temppath
124 return
125 end
126
127 # When the path currently doesn't exist yet, :change_file_xxx should be performed against `@temppath`.
128 # Checking that by `diff -q /dev/null xxx` doesn't work when xxx's content is "", because /dev/null's content is also "".
129 if !current.exist && attributes.exist
130 attributes.modified = true
117131 return
118132 end
119133
125139 # error
126140 raise Itamae::Backend::CommandExecutionError, "diff command exited with 2"
127141 end
142 end
143
144 def exists_and_not_modified?
145 return false unless current.exist && sha256sum_available?
146
147 current_digest = run_command(["sha256sum", attributes.path]).stdout.split(/\s/, 2).first
148 digest = if content_file
149 Digest::SHA256.file(content_file).hexdigest
150 else
151 Digest::SHA256.hexdigest(attributes.content.to_s)
152 end
153
154 current_digest == digest
128155 end
129156
130157 def show_content_diff
175202
176203 if backend.is_a?(Itamae::Backend::Docker)
177204 run_command(["mkdir", @temppath])
178 backend.send_file(src, @temppath)
205 backend.send_file(src, @temppath, user: attributes.user)
179206 @temppath = ::File.join(@temppath, ::File.basename(src))
180207 else
181208 run_command(["touch", @temppath])
182209 run_specinfra(:change_file_mode, @temppath, '0600')
183 backend.send_file(src, @temppath)
210 backend.send_file(src, @temppath, user: attributes.user)
184211 end
185212
186213 run_specinfra(:change_file_mode, @temppath, '0600')
188215 f.unlink if f
189216 end
190217 end
218
219 def sha256sum_available?
220 return self.class.sha256sum_available unless self.class.sha256sum_available.nil?
221
222 self.class.sha256sum_available = run_command(["sha256sum", "--version"], error: false).exit_status == 0
223 end
191224 end
192225 end
193226 end
0 require 'itamae'
1
20 module Itamae
31 module Resource
42 class GemPackage < Base
0 require 'itamae'
1
20 module Itamae
31 module Resource
42 class Git < Base
0 require 'itamae'
1
20 module Itamae
31 module Resource
42 class Group < Base
0 require 'itamae'
10 require 'uri'
21 require 'net/https'
32
54 module Resource
65 class HttpRequest < File
76 RedirectLimitExceeded = Class.new(StandardError)
7 HTTPClientError = Class.new(StandardError)
8 HTTPServerError = Class.new(StandardError)
9 HTTPUnknownError = Class.new(StandardError)
810
911 alias_method :_action_create, :action_create
1012 undef_method :action_create, :action_delete, :action_edit
4850 response = http.method(attributes.action).call(uri.request_uri, attributes.message, attributes.headers)
4951 end
5052
51 if response.kind_of?(Net::HTTPRedirection)
53 case response
54 when Net::HTTPSuccess
55 break
56 when Net::HTTPRedirection
5257 if redirects_followed < attributes.redirect_limit
5358 uri = URI.parse(response["location"])
5459 redirects_followed += 1
5661 else
5762 raise RedirectLimitExceeded
5863 end
64 when Net::HTTPClientError
65 raise HTTPClientError
66 when Net::HTTPServerError
67 raise HTTPServerError
5968 else
60 break
69 raise HTTPUnknownError
6170 end
6271 end
6372
0 require 'itamae'
1
20 module Itamae
31 module Resource
42 class Link < Base
0 require 'itamae'
1
20 module Itamae
31 module Resource
42 class LocalRubyBlock < Base
0 require 'itamae'
1
20 module Itamae
31 module Resource
42 class Package < Base
0 require 'itamae'
1
20 module Itamae
31 module Resource
42 class RemoteDirectory < Base
0 require 'itamae'
1
20 module Itamae
31 module Resource
42 class RemoteFile < File
0 require 'itamae'
1
20 module Itamae
31 module Resource
42 class Service < Base
0 require 'itamae'
10 require 'erb'
21 require 'tempfile'
32
3736
3837 def render_file(src)
3938 template = ::File.read(src)
40 ERB.new(template, nil, '-').tap do |erb|
41 erb.filename = src
42 end.result(binding)
39 erb =
40 if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
41 ERB.new(template, trim_mode: '-')
42 else
43 ERB.new(template, nil, '-')
44 end
45 erb.filename = src
46 erb.result(binding)
4347 end
4448
4549 def node
0 require 'itamae'
1
20 module Itamae
31 module Resource
42 class User < Base
0 require 'itamae'
10 require 'itamae/resource/base'
21 require 'itamae/resource/file'
32 require 'itamae/resource/package'
0 require 'itamae'
10 require 'json'
21 require 'yaml'
32
54 class Runner
65 class << self
76 def run(recipe_files, backend_type, options)
8 Itamae.logger.info "Starting Itamae..."
7 Itamae.logger.info "Starting Itamae... #{options[:dry_run] ? '(dry-run)' : ''}"
98
109 backend = Backend.create(backend_type, options)
1110 runner = self.new(backend, options)
102101 end
103102
104103 Itamae.logger.info "Loading node data via ohai..."
105 hash.merge!(JSON.parse(@backend.run_command("ohai").stdout))
104 hash.merge!(JSON.parse(@backend.run_command("ohai 2>/dev/null").stdout))
106105 end
107106
108107 if @options[:node_json]
00 module Itamae
1 VERSION = "1.9.10"
1 VERSION = "1.10.7"
22 end
00 require "itamae/version"
11 require "itamae/runner"
2 require "itamae/cli"
32 require "itamae/recipe"
43 require "itamae/resource"
54 require "itamae/handler"
1211 require "itamae/definition"
1312 require "itamae/ext"
1413 require "itamae/generators"
14 require "itamae/mash"
1515
1616 module Itamae
1717 # Your code goes here...
+0
-37
spec/integration/Vagrantfile less more
0 # -*- mode: ruby -*-
1 # vi: set ft=ruby :
2 require 'vagrant-digitalocean'
3
4 # Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
5 VAGRANTFILE_API_VERSION = "2"
6
7 Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
8 config.vm.define :trusty do |c|
9 c.vm.hostname = 'itamae-trusty'
10 c.vm.hostname += "-#{ENV['WERCKER_BUILD_ID']}" if ENV['WERCKER_BUILD_ID']
11 c.vm.provider :virtualbox do |provider, override|
12 override.vm.box = "ubuntu/trusty64"
13 override.vm.provision :shell, inline: <<-EOC
14 cat /etc/apt/sources.list | sed -e 's|http://[^ ]*|mirror://mirrors.ubuntu.com/mirrors.txt|g' > /tmp/sources.list
15 if !(diff -q /etc/apt/sources.list /tmp/sources.list); then
16 mv /tmp/sources.list /etc/apt/sources.list
17 apt-get update
18 fi
19 echo America/New_York > /etc/timezone
20 dpkg-reconfigure --frontend noninteractive tzdata
21 EOC
22 end
23
24 c.vm.provider :digital_ocean do |provider, override|
25 override.ssh.private_key_path = '~/.ssh/id_rsa.vagrant'
26 override.vm.box = 'digital_ocean'
27 override.vm.box_url = "https://github.com/smdahlen/vagrant-digitalocean/raw/master/box/digital_ocean.box"
28
29 provider.ssh_key_name = ENV['WERCKER'] ? 'vagrant/wercker/itamae' : 'Vagrant'
30 provider.token = ENV['DIGITALOCEAN_TOKEN']
31 provider.image = 'ubuntu-14-04-x64' # ubuntu
32 provider.region = 'nyc3'
33 provider.size = '512mb'
34 end
35 end
36 end
7878
7979 describe file('/tmp/http_request.html') do
8080 it { should be_file }
81 its(:content) { should match(/"from": "itamae"/) }
81 its(:content) { should match(/"from":\s*"itamae"/) }
8282 end
8383
8484 describe file('/tmp/http_request_delete.html') do
8585 it { should be_file }
86 its(:content) { should match(/"from": "itamae"/) }
86 its(:content) { should match(/"from":\s*"itamae"/) }
8787 end
8888
8989 describe file('/tmp/http_request_post.html') do
9090 it { should be_file }
91 its(:content) do
92 should match(/"from": "itamae"/)
93 should match(/"love": "sushi"/)
94 end
91 its(:content) { should match(/"from":\s*"itamae"/) }
92 its(:content) { should match(/"love":\s*"sushi"/) }
9593 end
9694
9795 describe file('/tmp/http_request_put.html') do
9896 it { should be_file }
99 its(:content) do
100 should match(/"from": "itamae"/)
101 should match(/"love": "sushi"/)
102 end
97 its(:content) { should match(/"from":\s*"itamae"/) }
98 its(:content) { should match(/"love":\s*"sushi"/) }
10399 end
104100
105101 describe file('/tmp/http_request_headers.html') do
106102 it { should be_file }
107 its(:content) { should match(/"User-Agent": "Itamae"/) }
103 its(:content) { should match(/"User-Agent":\s*"Itamae"/) }
108104 end
109105
110106 describe file('/tmp/http_request_redirect.html') do
111107 it { should be_file }
112 its(:content) { should match(/"from": "itamae"/) }
108 its(:content) { should match(/"from":\s*"itamae"/) }
113109 end
114110
115111 describe file('/tmp/notifies') do
120116 describe file('/tmp/subscribes') do
121117 it { should be_file }
122118 its(:content) { should eq("2431") }
123 end
124
125 describe file('/tmp/cron_stopped') do
126 it { should be_file }
127 its(:content) do
128 expect(subject.content.lines.size).to eq 1
129 end
130 end
131
132 describe file('/tmp/cron_running') do
133 it { should be_file }
134 its(:content) do
135 expect(subject.content.lines.size).to eq 2
136 end
137119 end
138120
139121 describe file('/tmp-link') do
195177 end
196178
197179 describe command('gem list') do
198 its(:stdout) { should include('rake (11.1.0)') }
180 its(:stdout) { should match(/^rake \(.*11.1.0.*\)/) }
199181 end
200182
201183 describe command('gem list') do
202184 its(:stdout) { should_not include('test-unit') }
203185 end
204186
205 describe command('ri Bundler') do
206 its(:stderr) { should eq("Nothing known about Bundler\n") }
187 describe command('gem list') do
188 its(:stdout) { should include('ast (2.0.0)') }
189 end
190
191 describe command('ri AST') do
192 its(:stderr) { should eq("Nothing known about AST\n") }
207193 end
208194
209195 describe file('/tmp/created_by_definition') do
214200 describe file('/tmp/remote_file_in_definition') do
215201 it { should be_file }
216202 its(:content) { should eq("definition_example\n") }
203 end
204
205 describe file('/tmp/created_by_definition_2_created') do
206 it { should be_file }
207 its(:content) { should eq("name:created,key:value2,message:Hello, Itamae\n") }
208 end
209
210 describe file('/tmp/remote_file_in_definition_2_created') do
211 it { should be_file }
212 its(:content) { should eq("definition_example_2\n") }
213 end
214
215 describe file('/tmp/created_by_definition_2_not_created') do
216 it { should_not exist }
217 end
218
219 describe file('/tmp/remote_file_in_definition_2_not_created') do
220 it { should_not exist }
221 end
222
223 describe file('/tmp/created_by_definition_3_created') do
224 it { should be_file }
225 its(:content) { should eq("name:created,key:value3,message:Hello, Itamae\n") }
226 end
227
228 describe file('/tmp/remote_file_in_definition_3_created') do
229 it { should be_file }
230 its(:content) { should eq("definition_example_3\n") }
231 end
232
233 describe file('/tmp/created_by_definition_3_not_created') do
234 it { should_not exist }
235 end
236
237 describe file('/tmp/remote_file_in_definition_3_not_created') do
238 it { should_not exist }
217239 end
218240
219241 describe file('/tmp/multi_delayed_notifies') do
292314 describe file('/tmp/subscribed_from_parent') do
293315 it { should be_file }
294316 end
317
318 describe file('/tmp/empty_file1') do
319 it { should exist }
320 it { should be_file }
321 its(:content) { should eq "" }
322 end
323
324 describe file('/tmp/empty_file2') do
325 it { should exist }
326 it { should be_file }
327 its(:content) { should eq "" }
328 end
329
330 describe file('/tmp/empty_file3') do
331 it { should exist }
332 it { should be_file }
333 its(:content) { should eq "" }
334 end
0 require 'spec_helper'
1
2 describe file('/tmp/cron_stopped') do
3 it { should be_file }
4 its(:content) do
5 expect(subject.content.lines.size).to eq 1
6 end
7 end
8
9 # FIXME: cron service is not running in docker...
10 #
11 # root@3450c6da6ea5:/# ps -C cron
12 # PID TTY TIME CMD
13 # root@3450c6da6ea5:/# service cron start
14 # Rather than invoking init scripts through /etc/init.d, use the service(8)
15 # utility, e.g. service cron start
16 #
17 # Since the script you are attempting to invoke has been converted to an
18 # Upstart job, you may also use the start(8) utility, e.g. start cron
19 # root@3450c6da6ea5:/# ps -C cron
20 # PID TTY TIME CMD
21 # root@3450c6da6ea5:/#
22
23 # describe file('/tmp/cron_running') do
24 # it { should be_file }
25 # its(:content) do
26 # expect(subject.content.lines.size).to eq 2
27 # end
28 # end
0 describe file('/tmp/file_as_ordinary_user') do
1 it { should be_file }
2 it { should be_owned_by "itamae" }
3 it { should be_grouped_into "itamae" }
4 end
5
0 require 'spec_helper'
1
2 describe file('/tmp/remote_file') do
3 it { should be_file }
4 it { should be_owned_by "ordinary_san" }
5 it { should be_grouped_into "ordinary_san" }
6 its(:content) { should match(/Hello Itamae/) }
7 end
8
9 describe file('/tmp/remote_file_root') do
10 it { should be_file }
11 it { should be_owned_by "root" }
12 it { should be_grouped_into "root" }
13 its(:content) { should match(/Hello Itamae/) }
14 end
15
16 %w[/tmp/remote_file_another_ordinary /tmp/remote_file_another_ordinary_with_root].each do |path|
17 describe file(path) do
18 it { should be_file }
19 it { should be_owned_by "itamae" }
20 it { should be_grouped_into "itamae" }
21 its(:content) { should match(/Hello Itamae/) }
22 end
23 end
24
25 ###
26
27 describe file('/tmp/file') do
28 it { should be_file }
29 it { should be_owned_by "ordinary_san" }
30 it { should be_grouped_into "ordinary_san" }
31 its(:content) { should match(/Hello World/) }
32 end
33
34 describe file('/tmp/file_root') do
35 it { should be_file }
36 it { should be_owned_by "root" }
37 it { should be_grouped_into "root" }
38 its(:content) { should match(/Hello World/) }
39 end
40
41 %w[/tmp/file_another_ordinary /tmp/file_another_ordinary_with_root].each do |path|
42 describe file(path) do
43 it { should be_file }
44 it { should be_owned_by "itamae" }
45 it { should be_grouped_into "itamae" }
46 its(:content) { should match(/Hello World/) }
47 end
48 end
49
50 ###
51
52 describe file('/tmp/template') do
53 it { should be_file }
54 it { should be_owned_by "ordinary_san" }
55 it { should be_grouped_into "ordinary_san" }
56 its(:content) { should match(/Hello/) }
57 its(:content) { should match(/Good bye/) }
58 its(:content) { should match(/^total memory: \d+kB$/) }
59 its(:content) { should match(/^uninitialized node key: $/) }
60 end
61
62 describe file('/tmp/template_root') do
63 it { should be_file }
64 it { should be_owned_by "root" }
65 it { should be_grouped_into "root" }
66 its(:content) { should match(/Hello/) }
67 its(:content) { should match(/Good bye/) }
68 its(:content) { should match(/^total memory: \d+kB$/) }
69 its(:content) { should match(/^uninitialized node key: $/) }
70 end
71
72 %w[/tmp/template_another_ordinary /tmp/template_another_ordinary_with_root].each do |path|
73 describe file(path) do
74 it { should be_file }
75 it { should be_owned_by "itamae" }
76 it { should be_grouped_into "itamae" }
77 its(:content) { should match(/Hello/) }
78 its(:content) { should match(/Good bye/) }
79 its(:content) { should match(/^total memory: \d+kB$/) }
80 its(:content) { should match(/^uninitialized node key: $/) }
81 end
82 end
83
84 ###
85
86 describe file('/tmp/http_request.html') do
87 it { should be_file }
88 it { should be_owned_by "ordinary_san" }
89 it { should be_grouped_into "ordinary_san" }
90 its(:content) { should match(/"from":\s*"itamae"/) }
91 end
92
93 describe file('/tmp/http_request_root.html') do
94 it { should be_file }
95 it { should be_owned_by "root" }
96 it { should be_grouped_into "root" }
97 its(:content) { should match(/"from":\s*"itamae"/) }
98 end
99
100 %w[/tmp/http_request_another_ordinary.html /tmp/http_request_another_ordinary_with_root.html].each do |path|
101 describe file(path) do
102 it { should be_file }
103 it { should be_owned_by "itamae" }
104 it { should be_grouped_into "itamae" }
105 its(:content) { should match(/"from":\s*"itamae"/) }
106 end
107 end
4444 action :install
4545 end
4646
47 package 'sl' do
48 version '3.03-17'
49 end
50
5147 package 'resolvconf' do
5248 action :remove
5349 end
6258
6359 gem_package 'tzinfo' do
6460 version '1.2.2'
65 end
66
67 gem_package 'bundler' do
68 options ['--no-ri', '--no-rdoc']
6961 end
7062
7163 gem_package 'rake' do
8274 end
8375
8476 gem_package 'test-unit' do
85 version '3.2.0'
77 version '2.5.5'
8678 end
8779
8880 gem_package 'test-unit' do
89 version '3.1.9'
81 version '2.4.9'
9082 end
9183
9284 gem_package 'test-unit' do
222214 url "https://httpbin.org/redirect-to?url=https%3A%2F%2Fhttpbin.org%2Fget%3Ffrom%3Ditamae"
223215 end
224216
225 ######
226
227 service "cron" do
228 action :stop
229 end
230
231 execute "ps -C cron > /tmp/cron_stopped; true"
232
233 service "cron" do
234 action :start
235 end
236
237 execute "ps -C cron > /tmp/cron_running; true"
238
239 ######
240
241 package "nginx" do
242 options "--force-yes"
243 end
244
245 service "nginx" do
246 action [:enable, :start]
247 end
248
249 execute "test -f /etc/rc3.d/S20nginx" # test
250 execute "test $(ps h -C nginx | wc -l) -gt 0" # test
251
252 service "nginx" do
253 action [:disable, :stop]
254 end
255
256 execute "test ! -f /etc/rc3.d/S20nginx" # test
257 execute "test $(ps h -C nginx | wc -l) -eq 0" # test
258
259 ######
260
261217 link "/tmp-link" do
262218 to "/tmp"
263219 end
270226
271227 ######
272228
273 execute "mkdir /tmp/link-force-no-dereference1"
229 execute "mkdir -p /tmp/link-force-no-dereference1"
274230 link "link-force-no-dereference" do
275231 cwd "/tmp"
276232 to "link-force-no-dereference1"
277233 force true
278234 end
279235
280 execute "mkdir /tmp/link-force-no-dereference2"
236 execute "mkdir -p /tmp/link-force-no-dereference2"
281237 link "link-force-no-dereference" do
282238 cwd "/tmp"
283239 to "link-force-no-dereference2"
339295
340296 definition_example "name" do
341297 key 'value'
298 end
299
300 execute "touch /tmp/trigger_for_definition_example_2"
301
302 definition_example_2 "created" do
303 key "value2"
304 only_if "test -f /tmp/trigger_for_definition_example_2"
305 end
306
307 definition_example_2 "not_created" do
308 key "value2"
309 not_if "test -f /tmp/trigger_for_definition_example_2"
310 end
311
312 definition_example_3 "created" do
313 key "value3"
314 not_if "test -f /tmp/this_file_is_not_exists"
315 end
316
317 definition_example_3 "not_created" do
318 key "value3"
319 only_if "test -f /tmp/this_file_is_not_exists"
342320 end
343321
344322 #####
555533
556534 ###
557535
536 file "/tmp/empty_file1" do
537 content ""
538 end
539
540 remote_file "/tmp/empty_file2" do
541 source "files/empty_file"
542 end
543
544 template "/tmp/empty_file3" do
545 source "templates/empty_file.erb"
546 end
547
548 ###
549
558550 v1 = node.memory.total
559551 v2 = node[:memory][:total]
560552 v3 = node['memory']['total']
33 remote_file "/tmp/remote_file_in_definition"
44 end
55
6 define :definition_example_2, key: 'default' do
7 execute "echo 'name:#{params[:name]},key:#{params[:key]},message:#{node[:message]}' > /tmp/created_by_definition_2_#{params[:name]}"
8
9 remote_file "/tmp/remote_file_in_definition_2_#{params[:name]}" do
10 source "files/remote_file_in_definition_2"
11 end
12 end
13
14 define :definition_example_3, key: 'default' do
15 execute "echo 'name:#{params[:name]},key:#{params[:key]},message:#{node[:message]}' > /tmp/created_by_definition_3_#{params[:name]}"
16
17 remote_file "/tmp/remote_file_in_definition_3_#{params[:name]}" do
18 source "files/remote_file_in_definition_3"
19 end
20 end
0 package 'sl' do
1 version '3.03-17'
2 end
3
4 ######
5
6 gem_package 'ast' do
7 version '2.0.0'
8 options ['--no-ri', '--no-rdoc']
9 end
10
11 ######
12
13 service "cron" do
14 action :stop
15 end
16
17 execute "ps -C cron > /tmp/cron_stopped; true"
18
19 service "cron" do
20 action :start
21 end
22
23 execute "ps -C cron > /tmp/cron_running; true"
24
25 ######
26
27 package "nginx" do
28 options "--force-yes"
29 end
30
31 service "nginx" do
32 action [:enable, :start]
33 end
34
35 execute "test -f /etc/rc3.d/S20nginx" # test
36 execute "test $(ps h -C nginx | wc -l) -gt 0" # test
37
38 service "nginx" do
39 action [:disable, :stop]
40 end
41
42 execute "test ! -f /etc/rc3.d/S20nginx" # test
43 execute "test $(ps h -C nginx | wc -l) -eq 0" # test
0 package 'sl'
1
2 ######
3
4 gem_package 'ast' do
5 version '2.0.0'
6 options ['--no-document']
7 end
8
9 ######
10
11 # Docker backend raises an error with `user` option, so it tests only on `itamae local`.
12 # After fix this error, please move this code and the spec to `default.rb`.
13 file "/tmp/file_as_ordinary_user" do
14 content "Hello World"
15 user "itamae"
16 owner "itamae"
17 group "itamae"
18 end
0 remote_file "/tmp/remote_file" do
1 source "hello.txt"
2 end
3
4 remote_file "/tmp/remote_file_root" do
5 user 'root'
6 owner 'root'
7 group 'root'
8 source "hello.txt"
9 end
10
11 remote_file "/tmp/remote_file_another_ordinary" do
12 user 'itamae'
13 owner 'itamae'
14 group 'itamae'
15 source "hello.txt"
16 end
17
18 remote_file "/tmp/remote_file_another_ordinary_with_root" do
19 user 'root'
20 owner 'itamae'
21 group 'itamae'
22 source "hello.txt"
23 end
24
25 ###
26
27 file "/tmp/file" do
28 content "Hello World"
29 end
30
31 file "/tmp/file_root" do
32 user 'root'
33 owner 'root'
34 group 'root'
35 content 'Hello World'
36 end
37
38 file "/tmp/file_another_ordinary" do
39 user 'itamae'
40 owner 'itamae'
41 group 'itamae'
42 content 'Hello World'
43 end
44
45 file "/tmp/file_another_ordinary_with_root" do
46 user 'root'
47 owner 'itamae'
48 group 'itamae'
49 content 'Hello World'
50 end
51
52 ###
53
54 template "/tmp/template" do
55 source "hello.erb"
56 variables goodbye: "Good bye"
57 end
58
59 template "/tmp/template_root" do
60 user 'root'
61 owner 'root'
62 group 'root'
63 source "hello.erb"
64 variables goodbye: "Good bye"
65 end
66
67 template "/tmp/template_another_ordinary" do
68 user 'itamae'
69 owner 'itamae'
70 group 'itamae'
71 source "hello.erb"
72 variables goodbye: "Good bye"
73 end
74
75 template "/tmp/template_another_ordinary_with_root" do
76 user 'root'
77 owner 'itamae'
78 group 'itamae'
79 source "hello.erb"
80 variables goodbye: "Good bye"
81 end
82
83 ###
84
85 http_request "/tmp/http_request.html" do
86 url "https://httpbin.org/get?from=itamae"
87 end
88
89 http_request "/tmp/http_request_root.html" do
90 user 'root'
91 owner 'root'
92 group 'root'
93 url "https://httpbin.org/get?from=itamae"
94 end
95
96 http_request "/tmp/http_request_another_ordinary.html" do
97 user 'itamae'
98 owner 'itamae'
99 group 'itamae'
100 url "https://httpbin.org/get?from=itamae"
101 end
102
103 http_request "/tmp/http_request_another_ordinary_with_root.html" do
104 user 'root'
105 owner 'itamae'
106 group 'itamae'
107 url "https://httpbin.org/get?from=itamae"
108 end
0 require 'serverspec'
1 require 'net/ssh'
2 require 'tempfile'
0 require "serverspec"
1 require "docker"
32
4 set :backend, :ssh
3 set :backend, :docker
54
6 def vagrant(cmd)
7 env = {"VAGRANT_CWD" => File.dirname(__FILE__)}
8 system(env, "vagrant #{cmd}")
9 end
10
11 if ENV['ASK_SUDO_PASSWORD']
12 begin
13 require 'highline/import'
14 rescue LoadError
15 fail "highline is not available. Try installing it."
16 end
17 set :sudo_password, ask("Enter sudo password: ") { |q| q.echo = false }
18 else
19 set :sudo_password, ENV['SUDO_PASSWORD']
20 end
21
22 host = ENV['TARGET_HOST']
23
24 config = Tempfile.new('', Dir.tmpdir)
25 vagrant "ssh-config #{host} > #{config.path}"
26
27 options = Net::SSH::Config.for(host, [config.path])
28
29 options[:user] ||= Etc.getlogin
30
31 set :host, options[:host_name] || host
32 set :ssh_options, options
5 set :docker_image, ENV["DOCKER_IMAGE"]
6 set :docker_container, ENV["DOCKER_CONTAINER"]
337
348 # Disable sudo
359 # set :disable_sudo, true
3913
4014 # Set PATH
4115 # set :path, '/sbin:/usr/local/sbin:$PATH'
16
17 RSpec.configure do |config|
18 unless ENV["CI"]
19 # focus is enabled only local (Run all specs at CI)
20 config.filter_run_when_matching :focus
21 end
22 end
1616
1717 describe ".send_file" do
1818 context "the source file doesn't exist" do
19 subject { -> { itamae_backend.send_file("src", "dst") } }
20 it { expect(subject).to raise_error(Itamae::Backend::SourceNotExistError, "The file 'src' doesn't exist.") }
19 subject { itamae_backend.send_file("src", "dst") }
20 it { expect{ subject }.to raise_error(Itamae::Backend::SourceNotExistError, "The file 'src' doesn't exist.") }
2121 end
2222
2323 context "the source file exist, but it is not a regular file" do
2424 before { Dir.mkdir("src") }
25 subject { -> { itamae_backend.send_file("src", "dst") } }
26 it { expect(subject).to raise_error(Itamae::Backend::SourceNotExistError, "'src' is not a file.") }
25 subject { itamae_backend.send_file("src", "dst") }
26 it { expect{ subject }.to raise_error(Itamae::Backend::SourceNotExistError, "'src' is not a file.") }
2727 end
2828
2929 context "the source file is a regular file" do
3030 before { FileUtils.touch("src") }
31 subject { -> { itamae_backend.send_file("src", "dst") } }
31 subject { itamae_backend.send_file("src", "dst") }
3232 it { expect { subject }.not_to raise_error }
3333 end
3434 end
3535
3636 describe ".send_directory" do
3737 context "the source directory doesn't exist" do
38 subject { -> { itamae_backend.send_directory("src", "dst") } }
39 it { expect(subject).to raise_error(Itamae::Backend::SourceNotExistError, "The directory 'src' doesn't exist.") }
38 subject { itamae_backend.send_directory("src", "dst") }
39 it { expect{ subject }.to raise_error(Itamae::Backend::SourceNotExistError, "The directory 'src' doesn't exist.") }
4040 end
4141
4242 context "the source directory exist, but it is not a directory" do
4343 before { FileUtils.touch("src") }
44 subject { -> { itamae_backend.send_directory("src", "dst") } }
45 it { expect(subject).to raise_error(Itamae::Backend::SourceNotExistError, "'src' is not a directory.") }
44 subject { itamae_backend.send_directory("src", "dst") }
45 it { expect{ subject }.to raise_error(Itamae::Backend::SourceNotExistError, "'src' is not a directory.") }
4646 end
4747
4848 context "the source directory is a directory" do
4949 before { Dir.mkdir("src") }
50 subject { -> { itamae_backend.send_directory("src", "dst") } }
50 subject { itamae_backend.send_directory("src", "dst") }
5151 it { expect { subject }.not_to raise_error }
5252 end
5353 end
1919 expect(handler).to receive(:event).with(:name_started, :arg)
2020 expect(handler).to receive(:event).with(:name_failed, :arg)
2121 expect {
22 subject.event(:name, :arg) { raise }
23 }.to raise_error
22 subject.event(:name, :arg) { raise "name is failed" }
23 }.to raise_error "name is failed"
2424 end
2525 end
2626 end
0 desc 'Run all integration tests on `itamae local` command'
1 task 'spec:integration:local' => ['spec:integration:local:main', 'spec:integration:local:ordinary_user']
2
3 namespace 'spec:integration:local' do
4 desc 'Run main integration test with `itamae local`'
5 task 'main' do
6 if RUBY_DESCRIPTION.include?('dev')
7 $stderr.puts "This integration test is skipped with unreleased Ruby."
8 $stderr.puts "Use released Ruby to execute this integration test."
9 next
10 end
11
12 IntegrationLocalSpecRunner.new(
13 [
14 [
15 "spec/integration/recipes/default.rb",
16 "spec/integration/recipes/default2.rb",
17 "spec/integration/recipes/redefine.rb",
18 "spec/integration/recipes/local.rb",
19 ],
20 [
21 "--dry-run",
22 "spec/integration/recipes/dry_run.rb",
23 ],
24 ],
25 ['spec/integration/default_spec.rb']
26 ).run
27
28 end
29
30 desc 'Run integration test for ordinary user with `itamae local`'
31 task 'ordinary_user' do
32 if RUBY_DESCRIPTION.include?('dev')
33 $stderr.puts "This integration test is skipped with unreleased Ruby."
34 $stderr.puts "Use released Ruby to execute this integration test."
35 next
36 end
37
38 runner = IntegrationLocalSpecRunner.new(
39 [
40 [
41 "--dry-run",
42 "spec/integration/recipes/ordinary_user.rb",
43 ],
44 [
45 "spec/integration/recipes/ordinary_user.rb"
46 ],
47 ],
48 ['spec/integration/ordinary_user_spec.rb'],
49 user: 'ordinary_san'
50 )
51 runner.docker_exec 'useradd', 'ordinary_san', '-p', '*'
52 runner.docker_exec 'useradd', 'itamae', '-p', '*', '--create-home'
53 runner.docker_exec 'sh', '-c', 'echo "ordinary_san ALL=(ALL:ALL) NOPASSWD: ALL" >> /etc/sudoers'
54 runner.run
55 end
56 end
57
58 class IntegrationLocalSpecRunner
59 CONTAINER_NAME = 'itamae'
60 include FileUtils
61
62 def initialize(suites, specs, ruby_version: RUBY_VERSION.split('.')[0..1].join('.'), user: nil)
63 @suites = suites
64 @specs = specs
65 @ruby_version = ruby_version
66 @user = user
67
68 docker_run
69 prepare
70 end
71
72 def run
73 provision
74 serverspec
75 clean_docker_container
76 end
77
78 def docker_run
79 mount_dir = Pathname(__dir__).join('../').to_s
80 sh 'docker', 'run', '--privileged', '-d', '--name', CONTAINER_NAME, '-v', "#{mount_dir}:/itamae", "ruby:#{@ruby_version}", 'sleep', '1d'
81 end
82
83 def prepare
84 docker_exec 'gem', 'install', 'bundler'
85 docker_exec 'bundle', 'install', options: %w[--workdir /itamae]
86 docker_exec 'apt-get', 'update', '-y'
87 docker_exec 'apt-get', 'install', 'locales', 'sudo', '-y'
88 docker_exec 'localedef', '-i', 'en_US', '-c', '-f', 'UTF-8', '-A', '/usr/share/locale/locale.alias', 'en_US.UTF-8'
89 end
90
91 def provision
92 @suites.each do |suite|
93 cmd = %W!bundle exec ruby -w bin/itamae local!
94 cmd << "-l" << (ENV['LOG_LEVEL'] || 'debug')
95 cmd << "-j" << "spec/integration/recipes/node.json"
96 cmd += suite
97
98 options = %w[--workdir /itamae]
99 options.push('--user', @user) if @user
100 docker_exec(*cmd, options: options)
101 end
102 end
103
104 def serverspec
105 ENV['DOCKER_CONTAINER'] = CONTAINER_NAME
106 sh('bundle', 'exec', 'rspec', '-I', './spec/integration', *@specs)
107 end
108
109 def clean_docker_container
110 sh('docker', 'rm', '-f', CONTAINER_NAME)
111 end
112
113 def docker_exec(*cmd, options: [])
114 sh 'docker', 'exec', '--env', 'LANG=en_US.utf8', *options, CONTAINER_NAME, *cmd
115 end
116 end
+0
-77
wercker.yml less more
0 box: wercker/rvm
1 # Build definition
2 build:
3 # The steps that will be executed on build
4 # See the Ruby section on the wercker devcenter:
5 # http://devcenter.wercker.com/articles/languages/ruby.html
6 steps:
7 # Uncomment this to force RVM to use a specific Ruby version
8 - rvm-use:
9 version: 2.2.3
10
11 - script:
12 name: update bundler
13 code: gem update bundler
14
15 # A step that executes `bundle install` command
16 - bundle-install
17
18 # A custom script step, name value is used in the UI
19 # and the code value contains the command that get executed
20 - script:
21 name: echo ruby information
22 code: |
23 echo "ruby version $(ruby --version) running"
24 echo "from location $(which ruby)"
25 echo -p "gem list: $(gem list)"
26
27 - script:
28 name: create .ssh directory
29 code: mkdir -p $HOME/.ssh
30
31 - create-file:
32 name: put private key
33 filename: $HOME/.ssh/id_rsa.vagrant
34 overwrite: true
35 hide-from-log: true
36 content: $DIGITALOCEAN_PRIVATE_KEY
37
38 - create-file:
39 name: put public key
40 filename: $HOME/.ssh/id_rsa.vagrant.pub
41 overwrite: true
42 hide-from-log: true
43 content: $DIGITALOCEAN_PUBLIC_KEY
44
45 - script:
46 name: chmod 600 id_rsa
47 code: chmod 600 $HOME/.ssh/id_rsa.vagrant
48
49 - script:
50 name: install libxml and libxslt
51 code: sudo apt-get install libxml2-dev libxslt1-dev
52
53 - script:
54 name: bundle install
55 code: bundle install --deployment -j4
56
57 - script:
58 name: start vm
59 code: bundle exec vagrant up --provider=digital_ocean
60 cwd: spec/integration
61
62 # Add more steps here:
63 - script:
64 name: rspec
65 code: bundle exec rake spec
66
67 after-steps:
68 - script:
69 name: shutdown vm
70 code: bundle exec vagrant destroy -f
71 cwd: spec/integration
72
73 - script:
74 name: shutdown old vms
75 code: bundle exec ruby ci/destroy_old_droplets.rb
76