itamae 1.14.2 → 1.15.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4e4d3ac8c4c594f8721621e3ac992482d8f89a4391525e7f40ae7bbf6c492d1
4
- data.tar.gz: fdb3b1e0f358f66453c9097a1f3bc8577eb606f76522059885dbde4b938d92d8
3
+ metadata.gz: 12b8fcd57c70cc4c440ccd46a34786f1ac43a6bc7f4b66b68598ffeecfe9daaf
4
+ data.tar.gz: c1e77510842948b7cc4222e566939e8e32c1540d4b09178dc6492449b38d98c2
5
5
  SHA512:
6
- metadata.gz: 4e47d72e5095bf93a0e11f2ac0686bb787693de09b84672be240a41addb09d7b2a56b205a10f3738fd8b2d64d0d75a6ea431861aef5e27c1eb147d40c444ca37
7
- data.tar.gz: c03a588ab12d43ce1921b96e8d8581d9296f847ac127a0af956b6fba5edbc085a8eab53ff8876e93b4303854ecb60d34186b7dd49d68e1bac002d06eea0b7843
6
+ metadata.gz: 4d033e29941f7cc56de7626d1b3bdee2e00d1adca58e13c2927c4f3a1202046d80427eb614111424c8e21cc43c2bf480009069782fa5faebfdbf2f9dcd7fdbc5
7
+ data.tar.gz: bd55aa58068d1ec98b5bc74e4e6829d7e0927f575e352289b74801916ebab2f9e511a4b197a947da538c033b0d054af39dc91209facc0e1e74b797c0da07b599
@@ -6,3 +6,5 @@ updates:
6
6
  directory: /
7
7
  schedule:
8
8
  interval: monthly
9
+ cooldown:
10
+ default-days: 7
@@ -0,0 +1,3 @@
1
+ {
2
+ "registry-mirrors": ["https://mirror.gcr.io"]
3
+ }
@@ -18,20 +18,20 @@ jobs:
18
18
 
19
19
  steps:
20
20
  - name: Harden Runner
21
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
21
+ uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
22
22
  with:
23
23
  egress-policy: audit
24
24
 
25
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
25
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
26
26
 
27
27
  - name: Set up Ruby
28
- uses: ruby/setup-ruby@eaecf785f6a34567a6d97f686bbb7bccc1ac1e5c # v1.237.0
28
+ uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
29
29
  with:
30
30
  bundler-cache: true
31
31
  ruby-version: ruby
32
32
 
33
33
  - name: Publish to RubyGems
34
- uses: rubygems/release-gem@a25424ba2ba8b387abc8ef40807c2c85b96cbe32 # v1.1.1
34
+ uses: rubygems/release-gem@052cc82692552de3ef2b81fd670e41d13cba8092 # v1.4.0
35
35
 
36
36
  - name: Create GitHub release
37
37
  run: |
@@ -11,6 +11,7 @@ on:
11
11
  - reopened
12
12
  schedule:
13
13
  - cron: "0 0 * * 5" # JST 9:00 (Fri)
14
+ workflow_dispatch:
14
15
 
15
16
  jobs:
16
17
  test_main:
@@ -29,10 +30,12 @@ jobs:
29
30
  - "3.2"
30
31
  - "3.3"
31
32
  - "3.4"
33
+ - "4.0"
32
34
  rubyopt:
33
35
  - ""
34
36
  - "--jit"
35
37
  - "--yjit"
38
+ - "--zjit"
36
39
  exclude:
37
40
  # --jit is available since MRI 2.6
38
41
  - ruby: "2.3"
@@ -62,20 +65,35 @@ jobs:
62
65
  rubyopt: "--yjit"
63
66
  - ruby: "3.4"
64
67
  rubyopt: "--yjit"
68
+ - ruby: "4.0"
69
+ rubyopt: "--yjit"
70
+
71
+ # --zjit is available since MRI 4.0
72
+ - ruby: "2.3"
73
+ rubyopt: "--zjit"
74
+ - ruby: "2.4"
75
+ rubyopt: "--zjit"
76
+ - ruby: "2.5"
77
+ rubyopt: "--zjit"
78
+ - ruby: "2.6"
79
+ rubyopt: "--zjit"
80
+ - ruby: "2.7"
81
+ rubyopt: "--zjit"
82
+ - ruby: "3.0"
83
+ rubyopt: "--zjit"
84
+ - ruby: "3.1"
85
+ rubyopt: "--zjit"
86
+ - ruby: "3.2"
87
+ rubyopt: "--zjit"
88
+ - ruby: "3.3"
89
+ rubyopt: "--zjit"
90
+ - ruby: "3.4"
91
+ rubyopt: "--zjit"
65
92
 
66
93
  uses: ./.github/workflows/test_main.yml
67
94
  with:
68
95
  ruby: ${{ matrix.ruby }}
69
96
  rubyopt: ${{ matrix.rubyopt }}
70
-
71
- # This repository uses matrix to run 50+ builds in a pipeline.
72
- # This results in simultaneous requests being sent to httpbin.org,
73
- # similar to a DDoS attack, often causing 500 errors and test failures.
74
- #
75
- # e.g. https://github.com/itamae-kitchen/itamae/actions/runs/17045210601/job/48319099479?pr=379
76
- #
77
- # Therefore, I suppressed simultaneous requests to httpbin.org.
78
- skip_http_request_test: ${{ (matrix.ruby == '2.3' && matrix.rubyopt == '') && 'false' || 'true' }}
79
97
  secrets:
80
98
  SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
81
99
 
@@ -87,12 +105,9 @@ jobs:
87
105
 
88
106
  steps:
89
107
  - name: Slack Notification (success)
90
- uses: lazy-actions/slatify@master
108
+ uses: act10ns/slack@d96404edccc6d6467fc7f8134a420c851b1e9054 # v2.2.0
91
109
  if: always()
92
110
  continue-on-error: true
93
111
  with:
94
- job_name: '*notify*'
95
- type: ${{ job.status }}
96
- icon_emoji: ":octocat:"
97
- url: ${{ secrets.SLACK_WEBHOOK }}
98
- token: ${{ secrets.GITHUB_TOKEN }}
112
+ status: ${{ job.status }}
113
+ webhook-url: ${{ secrets.SLACK_WEBHOOK }}
@@ -10,9 +10,6 @@ on:
10
10
  required: false
11
11
  type: string
12
12
  default: ""
13
- skip_http_request_test:
14
- required: true
15
- type: string
16
13
  secrets:
17
14
  SLACK_WEBHOOK:
18
15
  required: true
@@ -22,9 +19,9 @@ jobs:
22
19
  runs-on: ubuntu-latest
23
20
 
24
21
  steps:
25
- - uses: actions/checkout@v6
22
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
26
23
 
27
- - uses: ruby/setup-ruby@v1
24
+ - uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
28
25
  with:
29
26
  ruby-version: ${{ inputs.ruby }}
30
27
  bundler-cache: true
@@ -37,15 +34,13 @@ jobs:
37
34
  RUBYOPT: ${{ inputs.rubyopt }}
38
35
 
39
36
  - name: Slack Notification (not success)
40
- uses: lazy-actions/slatify@master
37
+ uses: act10ns/slack@d96404edccc6d6467fc7f8134a420c851b1e9054 # v2.2.0
41
38
  if: "! success()"
42
39
  continue-on-error: true
43
40
  with:
44
- job_name: ${{ format('*unit* ({0},{1})', inputs.ruby, inputs.rubyopt) }}
45
- type: ${{ job.status }}
46
- icon_emoji: ":octocat:"
47
- url: ${{ secrets.SLACK_WEBHOOK }}
48
- token: ${{ secrets.GITHUB_TOKEN }}
41
+ status: ${{ job.status }}
42
+ webhook-url: ${{ secrets.SLACK_WEBHOOK }}
43
+ inputs: ${{ toJson(inputs) }}
49
44
 
50
45
  integration-docker:
51
46
  runs-on: ubuntu-latest
@@ -59,12 +54,16 @@ jobs:
59
54
 
60
55
  env:
61
56
  TEST_IMAGE: ${{ matrix.image }}
62
- SKIP_HTTP_REQUEST_TEST: ${{ inputs.skip_http_request_test }}
63
57
 
64
58
  steps:
65
- - uses: actions/checkout@v6
59
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
60
+
61
+ - name: Pull Docker Hub images through mirror.gcr.io
62
+ run: |
63
+ sudo cp .github/workflows/docker-daemon.json /etc/docker/daemon.json
64
+ sudo systemctl restart docker
66
65
 
67
- - uses: ruby/setup-ruby@v1
66
+ - uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
68
67
  with:
69
68
  ruby-version: ${{ inputs.ruby }}
70
69
  bundler-cache: true
@@ -72,6 +71,10 @@ jobs:
72
71
 
73
72
  - run: bundle update
74
73
 
74
+ - run: bundle exec rake spec:integration:docker:httpbin_start
75
+ env:
76
+ RUBYOPT: ${{ inputs.rubyopt }}
77
+
75
78
  - run: bundle exec rake spec:integration:docker:boot
76
79
  env:
77
80
  RUBYOPT: ${{ inputs.rubyopt }}
@@ -91,16 +94,19 @@ jobs:
91
94
  env:
92
95
  RUBYOPT: ${{ inputs.rubyopt }}
93
96
 
97
+ - run: bundle exec rake spec:integration:docker:httpbin_stop
98
+ if: always()
99
+ env:
100
+ RUBYOPT: ${{ inputs.rubyopt }}
101
+
94
102
  - name: Slack Notification (not success)
95
- uses: lazy-actions/slatify@master
103
+ uses: act10ns/slack@d96404edccc6d6467fc7f8134a420c851b1e9054 # v2.2.0
96
104
  if: "! success()"
97
105
  continue-on-error: true
98
106
  with:
99
- job_name: ${{ format('*integration-docker* ({0},{1},{2})', inputs.ruby, inputs.rubyopt, matrix.image) }}
100
- type: ${{ job.status }}
101
- icon_emoji: ":octocat:"
102
- url: ${{ secrets.SLACK_WEBHOOK }}
103
- token: ${{ secrets.GITHUB_TOKEN }}
107
+ status: ${{ job.status }}
108
+ webhook-url: ${{ secrets.SLACK_WEBHOOK }}
109
+ matrix: ${{ toJson(matrix) }}
104
110
 
105
111
  integration-local:
106
112
  runs-on: ubuntu-latest
@@ -108,13 +114,15 @@ jobs:
108
114
  # NOTE: When ruby is 2.5, tests run on buster. But buster is already EOL and didn't work apt-get
109
115
  if: ${{ inputs.ruby >= '2.6' }}
110
116
 
111
- env:
112
- SKIP_HTTP_REQUEST_TEST: ${{ inputs.skip_http_request_test }}
113
-
114
117
  steps:
115
- - uses: actions/checkout@v6
118
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
119
+
120
+ - name: Pull Docker Hub images through mirror.gcr.io
121
+ run: |
122
+ sudo cp .github/workflows/docker-daemon.json /etc/docker/daemon.json
123
+ sudo systemctl restart docker
116
124
 
117
- - uses: ruby/setup-ruby@v1
125
+ - uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
118
126
  with:
119
127
  ruby-version: ${{ inputs.ruby }}
120
128
  bundler-cache: true
@@ -131,12 +139,10 @@ jobs:
131
139
  RUBYOPT: ${{ inputs.rubyopt }}
132
140
 
133
141
  - name: Slack Notification (not success)
134
- uses: lazy-actions/slatify@master
142
+ uses: act10ns/slack@d96404edccc6d6467fc7f8134a420c851b1e9054 # v2.2.0
135
143
  if: "! success()"
136
144
  continue-on-error: true
137
145
  with:
138
- job_name: ${{ format('*integration-local* ({0},{1})', inputs.ruby, inputs.rubyopt) }}
139
- type: ${{ job.status }}
140
- icon_emoji: ":octocat:"
141
- url: ${{ secrets.SLACK_WEBHOOK }}
142
- token: ${{ secrets.GITHUB_TOKEN }}
146
+ status: ${{ job.status }}
147
+ webhook-url: ${{ secrets.SLACK_WEBHOOK }}
148
+ inputs: ${{ toJson(inputs) }}
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  ## Unreleased
2
- [full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.14.2...master)
2
+ [full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.15.0...master)
3
+
4
+ ## v1.15.0
5
+ [full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.14.2...v1.15.0)
6
+
7
+ Improvements
8
+
9
+ - [Add `system_group` option to `group` resource by takumin](https://github.com/itamae-kitchen/itamae/pull/297)
10
+ - NOTE: This bumps the minimum required specinfra version to 2.81.0.
11
+ - [Add 'etc' library requirement in backend.rb by neidiom](https://github.com/itamae-kitchen/itamae/pull/389)
12
+ - [Prefer `Bundler.with_unbundled_env` over deprecated `with_clean_env` by neidiom](https://github.com/itamae-kitchen/itamae/pull/390)
13
+ - [Convert ohai commands to array form in runner by neidiom](https://github.com/itamae-kitchen/itamae/pull/395)
14
+ - [Use `respond_to_missing?` instead of `respond_to?` in Node by neidiom](https://github.com/itamae-kitchen/itamae/pull/409)
15
+ - [Replace abort with raise in notification validation by neidiom](https://github.com/itamae-kitchen/itamae/pull/410)
16
+ - [Fix shadowed variable in `include_recipe` find block by neidiom](https://github.com/itamae-kitchen/itamae/pull/408)
3
17
 
4
18
  ## v1.14.2
5
19
  [full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.14.1...v1.14.2)
data/Gemfile CHANGED
@@ -11,3 +11,12 @@ group :test do
11
11
  gem 'growl'
12
12
  end
13
13
  end
14
+
15
+ # FIXME: Delete the following after https://github.com/fakefs/fakefs/pull/494 is merged and released
16
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("4.0.0.preview2")
17
+ gem "english"
18
+ gem "fileutils"
19
+ gem "find"
20
+ gem "irb"
21
+ gem "stringio"
22
+ end
data/Rakefile CHANGED
@@ -24,12 +24,22 @@ namespace :spec do
24
24
  task :all => ['spec:integration:docker', 'spec:integration:local']
25
25
 
26
26
  desc "Run provision and specs"
27
- task :docker => ["docker:boot", "docker:provision", "docker:serverspec", 'docker:clean_docker_container']
27
+ task :docker => ["docker:httpbin_start", "docker:boot", "docker:provision", "docker:serverspec", 'docker:clean_docker_container', "docker:httpbin_stop"]
28
28
 
29
29
  namespace :docker do
30
+ desc "Start a local go-httpbin server for http_request tests"
31
+ task :httpbin_start do
32
+ HttpbinServer.start
33
+ end
34
+
35
+ desc "Stop the local go-httpbin server"
36
+ task :httpbin_stop do
37
+ HttpbinServer.stop
38
+ end
39
+
30
40
  desc "Run docker"
31
41
  task :boot do
32
- sh "docker run --env SKIP_HTTP_REQUEST_TEST --privileged -d --name #{container_name} #{TEST_IMAGE} /sbin/init"
42
+ sh "docker run --privileged -d --name #{container_name} #{TEST_IMAGE} /sbin/init"
33
43
  end
34
44
 
35
45
  desc "Run itamae"
data/itamae.gemspec CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.require_paths = ["lib"]
29
29
 
30
30
  spec.add_runtime_dependency "thor", ">= 1.0.0"
31
- spec.add_runtime_dependency "specinfra", [">= 2.64.0", "< 3.0.0"]
31
+ spec.add_runtime_dependency "specinfra", [">= 2.81.0", "< 3.0.0"]
32
32
  spec.add_runtime_dependency "hashie"
33
33
  spec.add_runtime_dependency "ansi"
34
34
  spec.add_runtime_dependency "schash", "~> 0.1.0"
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
36
36
  spec.add_development_dependency "bundler", ">= 1.3"
37
37
  spec.add_development_dependency "rake"
38
38
  spec.add_development_dependency "rspec", "~> 3.0"
39
- spec.add_development_dependency "serverspec", "~> 2.1"
39
+ spec.add_development_dependency "serverspec", "~> 2.43"
40
40
  spec.add_development_dependency "pry-byebug"
41
41
  spec.add_development_dependency "docker-api", "~> 2"
42
42
  spec.add_development_dependency "fakefs"
@@ -2,6 +2,7 @@ require 'specinfra/core'
2
2
  require 'singleton'
3
3
  require 'io/console'
4
4
  require 'net/ssh'
5
+ require 'etc'
5
6
 
6
7
  Specinfra::Configuration.error_on_missing_backend_type = true
7
8
 
@@ -267,7 +268,8 @@ module Itamae
267
268
  hostname = opts[:host_name] || 'default'
268
269
  vagrant_cmd = "vagrant ssh-config #{hostname} > #{config.path}"
269
270
  if defined?(Bundler)
270
- Bundler.with_clean_env do
271
+ bundler_method = Bundler.respond_to?(:with_unbundled_env) ? :with_unbundled_env : :with_clean_env
272
+ Bundler.public_send(bundler_method) do
271
273
  `#{vagrant_cmd}`
272
274
  end
273
275
  else
data/lib/itamae/node.rb CHANGED
@@ -56,7 +56,7 @@ module Itamae
56
56
  super
57
57
  end
58
58
 
59
- def respond_to?(method, priv = false)
59
+ def respond_to_missing?(method, priv = false)
60
60
  @mash.respond_to?(method, priv) || super
61
61
  end
62
62
 
@@ -30,8 +30,7 @@ module Itamae
30
30
 
31
31
  def validate!
32
32
  unless [:delay, :delayed, :immediately].include?(timing)
33
- Itamae.logger.error "'#{timing}' is not valid notification timing. (Valid option is delayed or immediately)"
34
- abort
33
+ raise ArgumentError, "'#{timing}' is not valid notification timing. (Valid option is delayed or immediately)"
35
34
  end
36
35
  end
37
36
  end
data/lib/itamae/recipe.rb CHANGED
@@ -122,7 +122,7 @@ module Itamae
122
122
  expanded_path = ::File.join(expanded_path, 'default.rb') if ::Dir.exist?(expanded_path)
123
123
  expanded_path.concat('.rb') unless expanded_path.end_with?('.rb')
124
124
  candidate_paths = [expanded_path, Recipe.find_recipe_in_gem(target)].compact
125
- path = candidate_paths.find {|path| File.exist?(path) }
125
+ path = candidate_paths.find {|candidate_path| File.exist?(candidate_path) }
126
126
 
127
127
  unless path
128
128
  raise NotFoundError, "Recipe not found. (#{target})"
@@ -4,6 +4,7 @@ module Itamae
4
4
  define_attribute :action, default: :create
5
5
  define_attribute :groupname, type: String, default_name: true
6
6
  define_attribute :gid, type: Integer
7
+ define_attribute :system_group, type: [TrueClass, FalseClass]
7
8
 
8
9
  def set_current_attributes
9
10
  current.exist = exist?
@@ -21,7 +22,8 @@ module Itamae
21
22
  end
22
23
  else
23
24
  options = {
24
- gid: attributes.gid,
25
+ gid: attributes.gid,
26
+ system_group: attributes.system_group,
25
27
  }
26
28
 
27
29
  run_specinfra(:add_group, attributes.groupname, options)
data/lib/itamae/runner.rb CHANGED
@@ -101,14 +101,14 @@ module Itamae
101
101
  hash.extend(Hashie::Extensions::DeepMerge)
102
102
 
103
103
  if @options[:ohai]
104
- unless @backend.run_command("which ohai", error: false).exit_status == 0
104
+ unless @backend.run_command(["which", "ohai"], error: false).exit_status == 0
105
105
  # install Ohai
106
106
  Itamae.logger.info "Installing Chef package... (to use Ohai)"
107
107
  @backend.run_command("curl -L https://omnitruck.chef.io/install.sh | bash")
108
108
  end
109
109
 
110
110
  Itamae.logger.info "Loading node data via ohai..."
111
- hash.merge!(JSON.parse(@backend.run_command("ohai 2>/dev/null").stdout))
111
+ hash.merge!(JSON.parse(@backend.run_command(["ohai"], error: false).stdout))
112
112
  end
113
113
 
114
114
  @options.fetch(:node_json, []).each do |name|
@@ -1,3 +1,3 @@
1
1
  module Itamae
2
- VERSION = "1.14.2"
2
+ VERSION = "1.15.0"
3
3
  end
@@ -7,6 +7,16 @@ describe user("itamae") do
7
7
  it { should have_login_shell '/bin/dash' }
8
8
  end
9
9
 
10
+ describe user("itamae_system") do
11
+ it { should exist }
12
+ it { should be_is_system_user }
13
+ end
14
+
15
+ describe group("itamae_system") do
16
+ it { should exist }
17
+ it { should be_is_system_group }
18
+ end
19
+
10
20
  describe file('/tmp/included_recipe') do
11
21
  it { should be_file }
12
22
  end
@@ -77,34 +87,34 @@ describe file('/tmp/never_exist2') do
77
87
  it { should_not be_file }
78
88
  end
79
89
 
80
- describe file('/tmp/http_request.html'), unless: ENV["SKIP_HTTP_REQUEST_TEST"] == "true" do
90
+ describe file('/tmp/http_request.html') do
81
91
  it { should be_file }
82
- its(:content) { should match(/"from":\s*"itamae"/) }
92
+ its(:content) { should match(/"from":\s*\[?\s*"itamae"/) }
83
93
  end
84
94
 
85
- describe file('/tmp/http_request_delete.html'), unless: ENV["SKIP_HTTP_REQUEST_TEST"] == "true" do
95
+ describe file('/tmp/http_request_delete.html') do
86
96
  it { should be_file }
87
- its(:content) { should match(/"from":\s*"itamae"/) }
97
+ its(:content) { should match(/"from":\s*\[?\s*"itamae"/) }
88
98
  end
89
99
 
90
- describe file('/tmp/http_request_post.html'), unless: ENV["SKIP_HTTP_REQUEST_TEST"] == "true" do
100
+ describe file('/tmp/http_request_post.html') do
91
101
  it { should be_file }
92
- its(:content) { should match(/"from":\s*"itamae"/) }
93
- its(:content) { should match(/"love":\s*"sushi"/) }
102
+ its(:content) { should match(/"from":\s*\[?\s*"itamae"/) }
103
+ its(:content) { should match(/"love":\s*\[?\s*"sushi"/) }
94
104
  end
95
105
 
96
- describe file('/tmp/http_request_put.html'), unless: ENV["SKIP_HTTP_REQUEST_TEST"] == "true" do
106
+ describe file('/tmp/http_request_put.html') do
97
107
  it { should be_file }
98
- its(:content) { should match(/"from":\s*"itamae"/) }
99
- its(:content) { should match(/"love":\s*"sushi"/) }
108
+ its(:content) { should match(/"from":\s*\[?\s*"itamae"/) }
109
+ its(:content) { should match(/"love":\s*\[?\s*"sushi"/) }
100
110
  end
101
111
 
102
- describe file('/tmp/http_request_headers.html'), unless: ENV["SKIP_HTTP_REQUEST_TEST"] == "true" do
112
+ describe file('/tmp/http_request_headers.html') do
103
113
  it { should be_file }
104
- its(:content) { should match(/"User-Agent":\s*"Itamae"/) }
114
+ its(:content) { should match(/"User-Agent":\s*\[?\s*"Itamae"/) }
105
115
  end
106
116
 
107
- xdescribe file('/tmp/http_request_redirect.html'), unless: ENV["SKIP_HTTP_REQUEST_TEST"] == "true" do
117
+ xdescribe file('/tmp/http_request_redirect.html') do
108
118
  it { should be_file }
109
119
  its(:content) { should match(/"from":\s*"itamae"/) }
110
120
  end
@@ -84,25 +84,25 @@ end
84
84
 
85
85
  ###
86
86
 
87
- describe file('/tmp/http_request.html'), unless: ENV["SKIP_HTTP_REQUEST_TEST"] == "true" do
87
+ describe file('/tmp/http_request.html') do
88
88
  it { should be_file }
89
89
  it { should be_owned_by "ordinary_san" }
90
90
  it { should be_grouped_into "ordinary_san" }
91
- its(:content) { should match(/"from":\s*"itamae"/) }
91
+ its(:content) { should match(/"from":\s*\[?\s*"itamae"/) }
92
92
  end
93
93
 
94
- describe file('/tmp/http_request_root.html'), unless: ENV["SKIP_HTTP_REQUEST_TEST"] == "true" do
94
+ describe file('/tmp/http_request_root.html') do
95
95
  it { should be_file }
96
96
  it { should be_owned_by "root" }
97
97
  it { should be_grouped_into "root" }
98
- its(:content) { should match(/"from":\s*"itamae"/) }
98
+ its(:content) { should match(/"from":\s*\[?\s*"itamae"/) }
99
99
  end
100
100
 
101
101
  %w[/tmp/http_request_another_ordinary.html /tmp/http_request_another_ordinary_with_root.html].each do |path|
102
- describe file(path), unless: ENV["SKIP_HTTP_REQUEST_TEST"] == "true" do
102
+ describe file(path) do
103
103
  it { should be_file }
104
104
  it { should be_owned_by "itamae" }
105
105
  it { should be_grouped_into "itamae" }
106
- its(:content) { should match(/"from":\s*"itamae"/) }
106
+ its(:content) { should match(/"from":\s*\[?\s*"itamae"/) }
107
107
  end
108
108
  end
@@ -39,6 +39,17 @@ user "create itamae2 user with create home directory" do
39
39
  shell "/bin/sh"
40
40
  end
41
41
 
42
+ group "create itamae_system group" do
43
+ groupname "itamae_system"
44
+ system_group true
45
+ end
46
+
47
+ user "create itamae_system user" do
48
+ gid "itamae_system"
49
+ username "itamae_system"
50
+ system_user true
51
+ end
52
+
42
53
  ######
43
54
 
44
55
  package 'jq' do
@@ -192,39 +203,41 @@ end
192
203
 
193
204
  ######
194
205
 
195
- unless ENV["SKIP_HTTP_REQUEST_TEST"] == "true"
196
- http_request "/tmp/http_request.html" do
197
- url "https://httpbin.org/get?from=itamae"
198
- end
206
+ httpbin_url = ENV.fetch("HTTPBIN_URL") { "http://127.0.0.1:#{ENV.fetch('HTTPBIN_HOST_PORT', '8080')}" }
199
207
 
200
- http_request "/tmp/http_request_delete.html" do
201
- action :delete
202
- url "https://httpbin.org/delete?from=itamae"
203
- end
208
+ http_request "/tmp/http_request.html" do
209
+ url "#{httpbin_url}/get?from=itamae"
210
+ end
204
211
 
205
- http_request "/tmp/http_request_post.html" do
206
- action :post
207
- message "love=sushi"
208
- url "https://httpbin.org/post?from=itamae"
209
- end
212
+ http_request "/tmp/http_request_delete.html" do
213
+ action :delete
214
+ url "#{httpbin_url}/delete?from=itamae"
215
+ end
210
216
 
211
- http_request "/tmp/http_request_put.html" do
212
- action :put
213
- message "love=sushi"
214
- url "https://httpbin.org/put?from=itamae"
215
- end
217
+ http_request "/tmp/http_request_post.html" do
218
+ action :post
219
+ headers "Content-Type" => "application/x-www-form-urlencoded"
220
+ message "love=sushi"
221
+ url "#{httpbin_url}/post?from=itamae"
222
+ end
216
223
 
217
- http_request "/tmp/http_request_headers.html" do
218
- headers "User-Agent" => "Itamae"
219
- url "https://httpbin.org/get"
220
- end
224
+ http_request "/tmp/http_request_put.html" do
225
+ action :put
226
+ headers "Content-Type" => "application/x-www-form-urlencoded"
227
+ message "love=sushi"
228
+ url "#{httpbin_url}/put?from=itamae"
229
+ end
221
230
 
222
- # http_request "/tmp/http_request_redirect.html" do
223
- # redirect_limit 1
224
- # url "https://httpbin.org/redirect-to?url=https%3A%2F%2Fhttpbin.org%2Fget%3Ffrom%3Ditamae"
225
- # end
231
+ http_request "/tmp/http_request_headers.html" do
232
+ headers "User-Agent" => "Itamae"
233
+ url "#{httpbin_url}/get"
226
234
  end
227
235
 
236
+ # http_request "/tmp/http_request_redirect.html" do
237
+ # redirect_limit 1
238
+ # url "#{httpbin_url}/redirect-to?url=#{httpbin_url}/get%3Ffrom%3Ditamae"
239
+ # end
240
+
228
241
  link "/tmp-link" do
229
242
  to "/tmp"
230
243
  end
@@ -83,29 +83,29 @@ end
83
83
 
84
84
  ###
85
85
 
86
- unless ENV["SKIP_HTTP_REQUEST_TEST"] == "true"
87
- http_request "/tmp/http_request.html" do
88
- url "https://httpbin.org/get?from=itamae"
89
- end
90
-
91
- http_request "/tmp/http_request_root.html" do
92
- user 'root'
93
- owner 'root'
94
- group 'root'
95
- url "https://httpbin.org/get?from=itamae"
96
- end
97
-
98
- http_request "/tmp/http_request_another_ordinary.html" do
99
- user 'itamae'
100
- owner 'itamae'
101
- group 'itamae'
102
- url "https://httpbin.org/get?from=itamae"
103
- end
104
-
105
- http_request "/tmp/http_request_another_ordinary_with_root.html" do
106
- user 'root'
107
- owner 'itamae'
108
- group 'itamae'
109
- url "https://httpbin.org/get?from=itamae"
110
- end
86
+ httpbin_url = ENV.fetch("HTTPBIN_URL") { "http://127.0.0.1:#{ENV.fetch('HTTPBIN_HOST_PORT', '8080')}" }
87
+
88
+ http_request "/tmp/http_request.html" do
89
+ url "#{httpbin_url}/get?from=itamae"
90
+ end
91
+
92
+ http_request "/tmp/http_request_root.html" do
93
+ user 'root'
94
+ owner 'root'
95
+ group 'root'
96
+ url "#{httpbin_url}/get?from=itamae"
97
+ end
98
+
99
+ http_request "/tmp/http_request_another_ordinary.html" do
100
+ user 'itamae'
101
+ owner 'itamae'
102
+ group 'itamae'
103
+ url "#{httpbin_url}/get?from=itamae"
104
+ end
105
+
106
+ http_request "/tmp/http_request_another_ordinary_with_root.html" do
107
+ user 'root'
108
+ owner 'itamae'
109
+ group 'itamae'
110
+ url "#{httpbin_url}/get?from=itamae"
111
111
  end
@@ -0,0 +1,63 @@
1
+ # Manage a local mccutchen/go-httpbin container for the integration tests.
2
+ #
3
+ # The public httpbin.org service is flaky and, under the CI matrix, receives
4
+ # so many simultaneous requests that it returns 500s. Running an httpbin-compatible
5
+ # server locally removes that external dependency.
6
+ #
7
+ # The `http_request` resource issues the HTTP request from the itamae process
8
+ # itself (`Net::HTTP`), so the reachable URL differs per backend:
9
+ # - docker backend: itamae runs on the host -> host_url (published port)
10
+ # - local backend: itamae runs in a container -> NETWORK_URL (shared network)
11
+ # The container therefore exposes both a published loopback port and a shared network.
12
+ module HttpbinServer
13
+ IMAGE = 'ghcr.io/mccutchen/go-httpbin:2.24.0'
14
+ NETWORK = 'itamae-test'
15
+ CONTAINER = 'httpbin'
16
+ PORT = 8080 # container port; also the port in NETWORK_URL
17
+ NETWORK_URL = "http://#{CONTAINER}:#{PORT}".freeze # reachable from a container on NETWORK
18
+
19
+ module_function
20
+
21
+ # Host port to publish go-httpbin on. Overridable so local runs can avoid a
22
+ # port already in use on the host (CI runners always have the default free).
23
+ def host_port
24
+ ENV.fetch('HTTPBIN_HOST_PORT', PORT.to_s)
25
+ end
26
+
27
+ # URL reachable from the itamae host (docker backend). Kept in sync with the
28
+ # recipe default in spec/integration/recipes/*.rb.
29
+ def host_url
30
+ "http://127.0.0.1:#{host_port}"
31
+ end
32
+
33
+ def start
34
+ create_network
35
+ remove_container # drop any stale container from a previous run
36
+ run! 'docker', 'run', '-d', '--name', CONTAINER,
37
+ '--network', NETWORK,
38
+ '-p', "127.0.0.1:#{host_port}:#{PORT}",
39
+ IMAGE
40
+ end
41
+
42
+ def stop
43
+ remove_container
44
+ quiet 'docker', 'network', 'rm', NETWORK
45
+ end
46
+
47
+ def create_network
48
+ return if system('docker', 'network', 'inspect', NETWORK, out: File::NULL, err: File::NULL)
49
+ run! 'docker', 'network', 'create', NETWORK
50
+ end
51
+
52
+ def remove_container
53
+ quiet 'docker', 'rm', '-f', CONTAINER
54
+ end
55
+
56
+ def run!(*cmd)
57
+ system(*cmd) or raise "command failed: #{cmd.join(' ')}"
58
+ end
59
+
60
+ def quiet(*cmd)
61
+ system(*cmd, out: File::NULL, err: File::NULL)
62
+ end
63
+ end
@@ -10,22 +10,27 @@ namespace 'spec:integration:local' do
10
10
  next
11
11
  end
12
12
 
13
- IntegrationLocalSpecRunner.new(
14
- [
13
+ HttpbinServer.start
14
+ ENV['HTTPBIN_URL'] = HttpbinServer::NETWORK_URL
15
+ begin
16
+ IntegrationLocalSpecRunner.new(
15
17
  [
16
- "spec/integration/recipes/default.rb",
17
- "spec/integration/recipes/default2.rb",
18
- "spec/integration/recipes/redefine.rb",
19
- "spec/integration/recipes/local.rb",
18
+ [
19
+ "spec/integration/recipes/default.rb",
20
+ "spec/integration/recipes/default2.rb",
21
+ "spec/integration/recipes/redefine.rb",
22
+ "spec/integration/recipes/local.rb",
23
+ ],
24
+ [
25
+ "--dry-run",
26
+ "spec/integration/recipes/dry_run.rb",
27
+ ],
20
28
  ],
21
- [
22
- "--dry-run",
23
- "spec/integration/recipes/dry_run.rb",
24
- ],
25
- ],
26
- ['spec/integration/default_spec.rb']
27
- ).run
28
-
29
+ ['spec/integration/default_spec.rb']
30
+ ).run
31
+ ensure
32
+ HttpbinServer.stop
33
+ end
29
34
  end
30
35
 
31
36
  desc 'Run integration test for ordinary user with `itamae local`'
@@ -36,23 +41,29 @@ namespace 'spec:integration:local' do
36
41
  next
37
42
  end
38
43
 
39
- runner = IntegrationLocalSpecRunner.new(
40
- [
44
+ HttpbinServer.start
45
+ ENV['HTTPBIN_URL'] = HttpbinServer::NETWORK_URL
46
+ begin
47
+ runner = IntegrationLocalSpecRunner.new(
41
48
  [
42
- "--dry-run",
43
- "spec/integration/recipes/ordinary_user.rb",
49
+ [
50
+ "--dry-run",
51
+ "spec/integration/recipes/ordinary_user.rb",
52
+ ],
53
+ [
54
+ "spec/integration/recipes/ordinary_user.rb"
55
+ ],
44
56
  ],
45
- [
46
- "spec/integration/recipes/ordinary_user.rb"
47
- ],
48
- ],
49
- ['spec/integration/ordinary_user_spec.rb'],
50
- user: 'ordinary_san'
51
- )
52
- runner.docker_exec 'useradd', 'ordinary_san', '-p', '*'
53
- runner.docker_exec 'useradd', 'itamae', '-p', '*', '--create-home'
54
- runner.docker_exec 'sh', '-c', 'echo "ordinary_san ALL=(ALL:ALL) NOPASSWD: ALL" >> /etc/sudoers'
55
- runner.run
57
+ ['spec/integration/ordinary_user_spec.rb'],
58
+ user: 'ordinary_san'
59
+ )
60
+ runner.docker_exec 'useradd', 'ordinary_san', '-p', '*'
61
+ runner.docker_exec 'useradd', 'itamae', '-p', '*', '--create-home'
62
+ runner.docker_exec 'sh', '-c', 'echo "ordinary_san ALL=(ALL:ALL) NOPASSWD: ALL" >> /etc/sudoers'
63
+ runner.run
64
+ ensure
65
+ HttpbinServer.stop
66
+ end
56
67
  end
57
68
  end
58
69
 
@@ -78,7 +89,7 @@ class IntegrationLocalSpecRunner
78
89
 
79
90
  def docker_run
80
91
  mount_dir = Pathname(__dir__).join('../').to_s
81
- sh 'docker', 'run', '--env', 'SKIP_HTTP_REQUEST_TEST', '--privileged', '-d', '--name', CONTAINER_NAME, '-v', "#{mount_dir}:/itamae", "ruby:#{@ruby_version}", 'sleep', '1d'
92
+ sh 'docker', 'run', '--env', 'HTTPBIN_URL', '--network', HttpbinServer::NETWORK, '--privileged', '-d', '--name', CONTAINER_NAME, '-v', "#{mount_dir}:/itamae", "ruby:#{@ruby_version}", 'sleep', '1d'
82
93
  end
83
94
 
84
95
  def prepare
@@ -115,6 +126,6 @@ class IntegrationLocalSpecRunner
115
126
  end
116
127
 
117
128
  def docker_exec(*cmd, options: [])
118
- sh 'docker', 'exec', '--env', 'LANG=en_US.utf8', '--env', 'SKIP_HTTP_REQUEST_TEST', *options, CONTAINER_NAME, *cmd
129
+ sh 'docker', 'exec', '--env', 'LANG=en_US.utf8', '--env', 'HTTPBIN_URL', *options, CONTAINER_NAME, *cmd
119
130
  end
120
131
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: itamae
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.14.2
4
+ version: 1.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryota Arai
@@ -31,7 +31,7 @@ dependencies:
31
31
  requirements:
32
32
  - - ">="
33
33
  - !ruby/object:Gem::Version
34
- version: 2.64.0
34
+ version: 2.81.0
35
35
  - - "<"
36
36
  - !ruby/object:Gem::Version
37
37
  version: 3.0.0
@@ -41,7 +41,7 @@ dependencies:
41
41
  requirements:
42
42
  - - ">="
43
43
  - !ruby/object:Gem::Version
44
- version: 2.64.0
44
+ version: 2.81.0
45
45
  - - "<"
46
46
  - !ruby/object:Gem::Version
47
47
  version: 3.0.0
@@ -135,14 +135,14 @@ dependencies:
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: '2.1'
138
+ version: '2.43'
139
139
  type: :development
140
140
  prerelease: false
141
141
  version_requirements: !ruby/object:Gem::Requirement
142
142
  requirements:
143
143
  - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: '2.1'
145
+ version: '2.43'
146
146
  - !ruby/object:Gem::Dependency
147
147
  name: pry-byebug
148
148
  requirement: !ruby/object:Gem::Requirement
@@ -209,6 +209,7 @@ extensions: []
209
209
  extra_rdoc_files: []
210
210
  files:
211
211
  - ".github/dependabot.yml"
212
+ - ".github/workflows/docker-daemon.json"
212
213
  - ".github/workflows/release.yml"
213
214
  - ".github/workflows/test.yml"
214
215
  - ".github/workflows/test_main.yml"
@@ -309,6 +310,7 @@ files:
309
310
  - spec/unit/lib/itamae/resource_spec.rb
310
311
  - spec/unit/lib/itamae/runner_spec.rb
311
312
  - spec/unit/spec_helper.rb
313
+ - tasks/httpbin_server.rb
312
314
  - tasks/integration_local_spec.rb
313
315
  homepage: https://itamae.kitchen/
314
316
  licenses:
@@ -332,7 +334,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
332
334
  - !ruby/object:Gem::Version
333
335
  version: '0'
334
336
  requirements: []
335
- rubygems_version: 3.6.7
337
+ rubygems_version: 4.0.16
336
338
  specification_version: 4
337
339
  summary: Simple Configuration Management Tool
338
340
  test_files: