stove 5.1.0 → 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +15 -0
- data/lib/stove.rb +17 -15
- data/lib/stove/artifactory.rb +83 -0
- data/lib/stove/cli.rb +27 -4
- data/lib/stove/community.rb +1 -1
- data/lib/stove/error.rb +2 -0
- data/lib/stove/plugins/artifactory.rb +14 -0
- data/lib/stove/runner.rb +5 -1
- data/lib/stove/version.rb +1 -1
- data/spec/fixtures/integration_cookbook/attributes/default.rb +1 -0
- data/spec/fixtures/integration_cookbook/metadata.rb +2 -0
- data/spec/fixtures/integration_cookbook/recipes/default.rb +1 -0
- data/spec/integration/artifactory_spec.rb +90 -0
- data/spec/spec_helper.rb +7 -1
- data/spec/unit/artifactory_spec.rb +131 -0
- data/stove.gemspec +2 -0
- data/templates/errors/artifactory_key_validation_failed.erb +11 -0
- data/templates/errors/cookbook_already_exists.erb +5 -0
- metadata +44 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a7b6e003eaa819a032278c65bdb28317ba36543
|
4
|
+
data.tar.gz: 71ab7d2ff0f46152784c3e4d0d60dec1cf157f01
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf2361dfafbaaca8e631d0b807f6b08c9fb0c1322c0865a7ac1188fc07c1e3ada760b4bd2af1712f2e295b5595d8bd3cc0869e82a534d0f0fd71f97ea953c5c7
|
7
|
+
data.tar.gz: a8a0a61d146ac8c7df835a5ff6613528d69c664e293edbffeca7fd7b8a9c8cf34f642f7a2c2492360615ac0c75b8f42116d96d299ea485ce637156692ed83ec1
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
This is the Changelog for the Stove gem.
|
4
4
|
|
5
|
+
## v5.2.0 (2017-05-12)
|
6
|
+
|
7
|
+
- Support for uploading to Artifactory
|
8
|
+
|
5
9
|
## v5.1.0 (2017-03-31)
|
6
10
|
|
7
11
|
- Match Chef's metadata behavior with chef_version and ohai_version to allow version constraints like '>= 12.5', '< 14'
|
data/README.md
CHANGED
@@ -76,6 +76,21 @@ And then use rake to publish the cookbook:
|
|
76
76
|
$ bin/rake publish
|
77
77
|
```
|
78
78
|
|
79
|
+
## Artifactory Support
|
80
|
+
|
81
|
+
In addition to uploading to a standard [Supermarket](https://github.com/chef/supermarket) server, Stove can upload to an Artifactory repository [configured to be in Chef mode](https://www.jfrog.com/confluence/display/RTF/Chef+Cookbook+Repositories).
|
82
|
+
|
83
|
+
You specify the URL to upload to via `--artifactory`, which you can find the URL Artifactory repository configuration. Your API key can be specified either in the `$ARTIFACTORY_API_KEY` environment variable or via `--artifactory-key`:
|
84
|
+
|
85
|
+
```bash
|
86
|
+
# API key in $ARTIFACTORY_API_KEY
|
87
|
+
$ stove --artifactory https://artifactory.example.com/api/chef/nameofrepo
|
88
|
+
# API key in a file
|
89
|
+
$ stove --artifactory https://artifactory.example.com/api/chef/nameofrepo --artifactory-key=@path/to/key/file
|
90
|
+
# API key on the command line
|
91
|
+
$ stove --artifactory https://artifactory.example.com/api/chef/nameofrepo --artifactory-key=XZg6tefUZYfiglmSbQ1s1e6habk
|
92
|
+
```
|
93
|
+
|
79
94
|
## Contributing
|
80
95
|
|
81
96
|
1. Fork it
|
data/lib/stove.rb
CHANGED
@@ -2,18 +2,19 @@ require 'logify'
|
|
2
2
|
require 'pathname'
|
3
3
|
|
4
4
|
module Stove
|
5
|
-
autoload :
|
6
|
-
autoload :
|
7
|
-
autoload :
|
8
|
-
autoload :
|
9
|
-
autoload :
|
10
|
-
autoload :
|
11
|
-
autoload :
|
12
|
-
autoload :
|
13
|
-
autoload :
|
14
|
-
autoload :
|
15
|
-
autoload :
|
16
|
-
autoload :
|
5
|
+
autoload :Artifactory, 'stove/artifactory'
|
6
|
+
autoload :Community, 'stove/community'
|
7
|
+
autoload :Config, 'stove/config'
|
8
|
+
autoload :Cookbook, 'stove/cookbook'
|
9
|
+
autoload :Cli, 'stove/cli'
|
10
|
+
autoload :Error, 'stove/error'
|
11
|
+
autoload :Filter, 'stove/filter'
|
12
|
+
autoload :Mash, 'stove/mash'
|
13
|
+
autoload :Packager, 'stove/packager'
|
14
|
+
autoload :Runner, 'stove/runner'
|
15
|
+
autoload :Util, 'stove/util'
|
16
|
+
autoload :Validator, 'stove/validator'
|
17
|
+
autoload :VERSION, 'stove/version'
|
17
18
|
|
18
19
|
module Middleware
|
19
20
|
autoload :ChefAuthentication, 'stove/middlewares/chef_authentication'
|
@@ -28,9 +29,10 @@ module Stove
|
|
28
29
|
end
|
29
30
|
|
30
31
|
module Plugin
|
31
|
-
autoload :
|
32
|
-
autoload :
|
33
|
-
autoload :
|
32
|
+
autoload :Artifactory, 'stove/plugins/artifactory'
|
33
|
+
autoload :Base, 'stove/plugins/base'
|
34
|
+
autoload :Community, 'stove/plugins/community'
|
35
|
+
autoload :Git, 'stove/plugins/git'
|
34
36
|
end
|
35
37
|
|
36
38
|
#
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module Stove
|
4
|
+
class Artifactory
|
5
|
+
include Mixin::Instanceable
|
6
|
+
|
7
|
+
#
|
8
|
+
# Upload a cookbook to an Artifactory server.
|
9
|
+
#
|
10
|
+
# @param [Cookbook] cookbook
|
11
|
+
# the cookbook to upload
|
12
|
+
#
|
13
|
+
def upload(cookbook, extended_metadata = false)
|
14
|
+
# Artifactory doesn't prevent uploading over an existing release in
|
15
|
+
# some cases so let's check for that. Seriously never do this, go delete
|
16
|
+
# and then re-upload if you have to.
|
17
|
+
response = request(:get, "api/v1/cookbooks/#{cookbook.name}/versions/#{cookbook.version}")
|
18
|
+
# Artifactory's version of the cookbook_version endpoint returns an
|
19
|
+
# empty 200 on an unknown version.
|
20
|
+
unless response.code == '404' || (response.code == '200' && response.body.to_s == '')
|
21
|
+
raise Error::CookbookAlreadyExists.new(cookbook: cookbook)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Run the upload.
|
25
|
+
response = request(:post, "api/v1/cookbooks/#{cookbook.name}.tgz") do |req|
|
26
|
+
req.body_stream = cookbook.tarball(extended_metadata)
|
27
|
+
req.content_length = req.body_stream.size
|
28
|
+
req['Content-Type'] = 'application/x-binary'
|
29
|
+
end
|
30
|
+
response.error! unless response.code == '201'
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
#
|
36
|
+
# Create an HTTP connect to the Artifactory server.
|
37
|
+
#
|
38
|
+
# @return [Net::HTTP]
|
39
|
+
#
|
40
|
+
def connection
|
41
|
+
@connection ||= begin
|
42
|
+
uri = URI(Config.artifactory.strip)
|
43
|
+
# Open the HTTP connection to artifactory.
|
44
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
45
|
+
if uri.scheme == 'https'
|
46
|
+
http.use_ssl = true
|
47
|
+
# Mimic the behavior of the Cookbook uploader for SSL verification.
|
48
|
+
if ENV['STOVE_NO_SSL_VERIFY'] || !Config.ssl_verify
|
49
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
50
|
+
end
|
51
|
+
end
|
52
|
+
http.start
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Send an HTTP request to the Artifactory server.
|
58
|
+
#
|
59
|
+
# @param [Symbol] method
|
60
|
+
# HTTP method to use
|
61
|
+
#
|
62
|
+
# @param [String] path
|
63
|
+
# URI path to request
|
64
|
+
#
|
65
|
+
# @param [Proc] block
|
66
|
+
# Optional block to set request values
|
67
|
+
#
|
68
|
+
# @return [Net::HTTPResponse]
|
69
|
+
#
|
70
|
+
def request(method, path, &block)
|
71
|
+
uri_string = Config.artifactory.strip
|
72
|
+
# Make sure we end up with the right number of separators.
|
73
|
+
uri_string << '/' unless uri_string.end_with?('/')
|
74
|
+
uri_string << path
|
75
|
+
uri = URI(uri_string)
|
76
|
+
request = Net::HTTP.const_get(method.to_s.capitalize).new(uri)
|
77
|
+
request['X-Jfrog-Art-Api'] = Config.artifactory_key.strip
|
78
|
+
block.call(request) if block
|
79
|
+
connection.request(request)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
data/lib/stove/cli.rb
CHANGED
@@ -36,10 +36,12 @@ module Stove
|
|
36
36
|
end
|
37
37
|
|
38
38
|
# Override configs
|
39
|
-
Config.endpoint
|
40
|
-
Config.username
|
41
|
-
Config.key
|
42
|
-
Config.
|
39
|
+
Config.endpoint = options[:endpoint] if options[:endpoint]
|
40
|
+
Config.username = options[:username] if options[:username]
|
41
|
+
Config.key = options[:key] if options[:key]
|
42
|
+
Config.artifactory = options[:artifactory] if options[:artifactory]
|
43
|
+
Config.artifactory_key = options[:artifactory_key] if options[:artifactory_key]
|
44
|
+
Config.ssl_verify = options[:ssl_verify]
|
43
45
|
|
44
46
|
# Set the log level
|
45
47
|
Stove.log_level = options[:log_level]
|
@@ -141,6 +143,23 @@ module Stove
|
|
141
143
|
options[:sign] = true
|
142
144
|
end
|
143
145
|
|
146
|
+
opts.separator ''
|
147
|
+
opts.separator 'Artifactory Options:'
|
148
|
+
|
149
|
+
opts.on('--artifactory [URL]', 'URL for the Artifactory repository') do |v|
|
150
|
+
options[:artifactory] = v
|
151
|
+
end
|
152
|
+
|
153
|
+
opts.on('--artifactory-key [KEY]', 'Artifactory API key to use') do |v|
|
154
|
+
options[:artifactory_key] = if v[0] == '@'
|
155
|
+
# If passed a key looking like @foo, read it as a file. This allows
|
156
|
+
# passing in the key securely.
|
157
|
+
IO.read(File.expand_path(v[1..-1]))
|
158
|
+
else
|
159
|
+
v
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
144
163
|
opts.separator ''
|
145
164
|
opts.separator 'Global Options:'
|
146
165
|
|
@@ -182,6 +201,10 @@ module Stove
|
|
182
201
|
:branch => 'master',
|
183
202
|
:sign => false,
|
184
203
|
|
204
|
+
# Artifactory options
|
205
|
+
:artifactory => false,
|
206
|
+
:artifactory_key => ENV['ARTIFACTORY_API_KEY'],
|
207
|
+
|
185
208
|
# Global options
|
186
209
|
:log_level => :warn,
|
187
210
|
:path => Dir.pwd,
|
data/lib/stove/community.rb
CHANGED
data/lib/stove/error.rb
CHANGED
@@ -40,6 +40,7 @@ module Stove
|
|
40
40
|
class GitFailed < StoveError; end
|
41
41
|
class MetadataNotFound < StoveError; end
|
42
42
|
class ServerUnavailable < StoveError; end
|
43
|
+
class CookbookAlreadyExists < StoveError; end
|
43
44
|
|
44
45
|
# Validations
|
45
46
|
class ValidationFailed < StoveError; end
|
@@ -49,5 +50,6 @@ module Stove
|
|
49
50
|
class GitCleanValidationFailed < ValidationFailed; end
|
50
51
|
class GitRepositoryValidationFailed < ValidationFailed; end
|
51
52
|
class GitUpToDateValidationFailed < ValidationFailed; end
|
53
|
+
class ArtifactoryKeyValidationFailed < ValidationFailed; end
|
52
54
|
end
|
53
55
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Stove
|
2
|
+
class Plugin::Artifactory < Plugin::Base
|
3
|
+
id 'artifactory'
|
4
|
+
description 'Publish the release to an Artifactory server'
|
5
|
+
|
6
|
+
validate(:key) do
|
7
|
+
Config.artifactory_key && !Config.artifactory_key.strip.empty?
|
8
|
+
end
|
9
|
+
|
10
|
+
run('Publishing the release to the Artifactory server') do
|
11
|
+
Artifactory.upload(cookbook, options[:extended_metadata])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/stove/runner.rb
CHANGED
data/lib/stove/version.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
default['key'] = 'value'
|
@@ -0,0 +1 @@
|
|
1
|
+
log 'this is a test recipe'
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# This integration test uses some environment variables to configure which
|
4
|
+
# Artifactory server to talk to, as there is no ArtifactoryZero to test against.
|
5
|
+
# If those aren't present, we skip the tests.
|
6
|
+
#
|
7
|
+
# $TEST_STOVE_ARTIFACTORY - URL to the Chef virtual repository.
|
8
|
+
# $TEST_STOVE_ARTIFACTORY_REAL - URL to the non-virtual repository.
|
9
|
+
# $TEST_STOVE_ARTIFACTORY_API_KEY - API key to use.
|
10
|
+
|
11
|
+
describe 'artifactory integration test', artifactory_integration: true do
|
12
|
+
include RSpecCommand
|
13
|
+
let(:upload_url) { ENV['TEST_STOVE_ARTIFACTORY'] }
|
14
|
+
let(:delete_url) { ENV['TEST_STOVE_ARTIFACTORY_REAL'] }
|
15
|
+
let(:api_key) { ENV['TEST_STOVE_ARTIFACTORY_API_KEY'] }
|
16
|
+
around do |ex|
|
17
|
+
WebMock.disable!
|
18
|
+
request(:delete, "#{delete_url}/stove_integration_test")
|
19
|
+
begin
|
20
|
+
ex.run
|
21
|
+
ensure
|
22
|
+
request(:delete, "#{delete_url}/stove_integration_test")
|
23
|
+
WebMock.enable!
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def request(method, url)
|
28
|
+
uri = URI(url)
|
29
|
+
req = Net::HTTP.const_get(method.to_s.capitalize).new(uri)
|
30
|
+
req['X-Jfrog-Art-Api'] = api_key
|
31
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
32
|
+
http.request(req)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:upload) { JSON.parse(request(:get, "#{upload_url}/api/v1/cookbooks/stove_integration_test/versions/1.0.0").body) }
|
37
|
+
|
38
|
+
describe 'help output' do
|
39
|
+
command 'stove --help'
|
40
|
+
its(:stdout) { is_expected.to include('--artifactory ').and(include('--artifactory-key ')) }
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'uploading a cookbook' do
|
44
|
+
context 'with no key' do
|
45
|
+
fixture_file 'integration_cookbook'
|
46
|
+
command(nil, allow_error: true) { "stove --no-git --artifactory #{upload_url}" }
|
47
|
+
|
48
|
+
it 'fails to upload' do
|
49
|
+
expect(subject.exitstatus).to_not eq 0
|
50
|
+
expect(subject.stdout).to match /You did not specify and Artifactory API key/
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with $ARTIFACTORY_API_KEY' do
|
55
|
+
before { _environment['ARTIFACTORY_API_KEY'] = api_key }
|
56
|
+
fixture_file 'integration_cookbook'
|
57
|
+
command { "stove --no-git --artifactory #{upload_url}" }
|
58
|
+
|
59
|
+
it 'uploads the cookbook' do
|
60
|
+
expect(subject.stdout).to eq ''
|
61
|
+
expect(upload['version']).to eq '1.0.0'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'with --artifactory-key=@key' do
|
66
|
+
fixture_file 'integration_cookbook'
|
67
|
+
file('key') { api_key }
|
68
|
+
command { "stove --no-git --artifactory #{upload_url} --artifactory-key=@key" }
|
69
|
+
|
70
|
+
it 'uploads the cookbook' do
|
71
|
+
expect(subject.stdout).to eq ''
|
72
|
+
expect(upload['version']).to eq '1.0.0'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'with --artifactory-key=key' do
|
77
|
+
fixture_file 'integration_cookbook'
|
78
|
+
file('key') { api_key }
|
79
|
+
# Using allow_error here so the command isn't shown if things fail.
|
80
|
+
command(nil, allow_error: true) { "stove --no-git --artifactory #{upload_url} --artifactory-key=#{api_key}" }
|
81
|
+
|
82
|
+
it 'uploads the cookbook' do
|
83
|
+
expect(subject.stdout).to eq ''
|
84
|
+
expect(subject.exitstatus).to eq 0
|
85
|
+
expect(upload['version']).to eq '1.0.0'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'rspec'
|
2
|
+
require 'webmock/rspec'
|
3
|
+
require 'rspec_command'
|
2
4
|
require 'stove'
|
3
5
|
|
4
6
|
RSpec.configure do |config|
|
@@ -8,13 +10,17 @@ RSpec.configure do |config|
|
|
8
10
|
|
9
11
|
# Basic configuraiton
|
10
12
|
config.run_all_when_everything_filtered = true
|
11
|
-
config.filter_run(:focus)
|
13
|
+
config.filter_run(:focus) unless ENV['CI']
|
12
14
|
|
13
15
|
# Run specs in random order to surface order dependencies. If you find an
|
14
16
|
# order dependency and want to debug it, you can fix the order by providing
|
15
17
|
# the seed, which is printed after each run.
|
16
18
|
# --seed 1234
|
17
19
|
config.order = 'random'
|
20
|
+
|
21
|
+
# Don't try running the Artifactory integration tests without the config for it.
|
22
|
+
# See integration/artifactory_spec for more info.
|
23
|
+
config.filter_run_excluding(:artifactory_integration) unless ENV['TEST_STOVE_ARTIFACTORY']
|
18
24
|
end
|
19
25
|
|
20
26
|
def tmp_path
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# Can't use a double() for this because of the late-binding way StoveError
|
4
|
+
# renders the Erb content, which is after the example ends.
|
5
|
+
FakeCookbook = Struct.new(:name, :version)
|
6
|
+
|
7
|
+
describe Stove::Artifactory do
|
8
|
+
before { Stove::Config.ssl_verify = true }
|
9
|
+
|
10
|
+
describe '.upload' do
|
11
|
+
let(:cookbook) { FakeCookbook.new('testcook', '1.2.3') }
|
12
|
+
subject { described_class.upload(cookbook) }
|
13
|
+
before do
|
14
|
+
# Default configuration for the test.
|
15
|
+
Stove::Config.artifactory = 'https://artifactory.example/api/chef/chef'
|
16
|
+
Stove::Config.artifactory_key = 'secret'
|
17
|
+
# cookbook.tarball() stub.
|
18
|
+
allow(cookbook).to receive(:tarball) {|ext| StringIO.new(ext ? 'extended' : 'simple') }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'with defaults' do
|
22
|
+
it 'uploads the file' do
|
23
|
+
stub_request(:get, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook/versions/1.2.3').with(headers: {'X-Jfrog-Art-Api' => 'secret'}).to_return(body: '')
|
24
|
+
stub_request(:post, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook.tgz').with(headers: {'X-Jfrog-Art-Api' => 'secret'}, body: 'simple').to_return(status: 201)
|
25
|
+
expect { subject }.to_not raise_error
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with a 404 for an unknown cookbook' do
|
30
|
+
# This is how real Supermarket returns a non-existent cookbook so make sure it works with that too.
|
31
|
+
it 'uploads the file' do
|
32
|
+
stub_request(:get, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook/versions/1.2.3').with(headers: {'X-Jfrog-Art-Api' => 'secret'}).to_return(status: 404, body: '{some json error}')
|
33
|
+
stub_request(:post, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook.tgz').with(headers: {'X-Jfrog-Art-Api' => 'secret'}, body: 'simple').to_return(status: 201)
|
34
|
+
expect { subject }.to_not raise_error
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'with extended_metadata = true' do
|
39
|
+
subject { described_class.upload(cookbook, true) }
|
40
|
+
it 'uploads with extended metadata' do
|
41
|
+
stub_request(:get, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook/versions/1.2.3').with(headers: {'X-Jfrog-Art-Api' => 'secret'}).to_return(body: '')
|
42
|
+
stub_request(:post, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook.tgz').with(headers: {'X-Jfrog-Art-Api' => 'secret'}, body: 'extended').to_return(status: 201)
|
43
|
+
expect { subject }.to_not raise_error
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'with a colliding upload' do
|
48
|
+
it 'should raise an exception' do
|
49
|
+
stub_request(:get, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook/versions/1.2.3').with(headers: {'X-Jfrog-Art-Api' => 'secret'}).to_return(body: '{some json}')
|
50
|
+
expect { subject }.to raise_error Stove::Error::CookbookAlreadyExists
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with a failed upload' do
|
55
|
+
it 'uploads the file' do
|
56
|
+
stub_request(:get, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook/versions/1.2.3').with(headers: {'X-Jfrog-Art-Api' => 'secret'}).to_return(body: '')
|
57
|
+
stub_request(:post, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook.tgz').with(headers: {'X-Jfrog-Art-Api' => 'secret'}, body: 'simple').to_return(status: 500)
|
58
|
+
expect { subject }.to raise_error Net::HTTPFatalError
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'with a newline in the API key' do
|
63
|
+
before { Stove::Config.artifactory_key = "secret\n" }
|
64
|
+
it 'uploads the file' do
|
65
|
+
stub_request(:get, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook/versions/1.2.3').with(headers: {'X-Jfrog-Art-Api' => 'secret'}).to_return(body: '')
|
66
|
+
stub_request(:post, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook.tgz').with(headers: {'X-Jfrog-Art-Api' => 'secret'}, body: 'simple').to_return(status: 201)
|
67
|
+
expect { subject }.to_not raise_error
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
# Break encapsulation a bit to test the ssl_verify configuration.
|
74
|
+
describe '#connection' do
|
75
|
+
let(:url) { 'https://artifactory.example/api/chef/chef' }
|
76
|
+
let(:http) { double('Net::HTTP') }
|
77
|
+
# Make sure we don't use the singleton instance so this stub HTTP object
|
78
|
+
# doesn't get cached on it.
|
79
|
+
subject { described_class.send(:new).send(:connection) }
|
80
|
+
before do
|
81
|
+
allow(http).to receive(:start).and_return(http)
|
82
|
+
Stove::Config.artifactory = url
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'with an HTTPS URI' do
|
86
|
+
it 'enables TLS' do
|
87
|
+
expect(Net::HTTP).to receive(:new).with('artifactory.example', 443).and_return(http)
|
88
|
+
expect(http).to receive(:use_ssl=).with(true)
|
89
|
+
expect(subject).to eq http
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'with an HTTP URI' do
|
94
|
+
let(:url) { 'http://artifactory.example/api/chef/chef' }
|
95
|
+
it 'does not enable TLS' do
|
96
|
+
expect(Net::HTTP).to receive(:new).with('artifactory.example', 80).and_return(http)
|
97
|
+
expect(http).to_not receive(:use_ssl=)
|
98
|
+
expect(subject).to eq http
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'with Config.ssl_verify = false' do
|
103
|
+
before { Stove::Config.ssl_verify = false }
|
104
|
+
it 'sets verify mode VERIFY_NONE' do
|
105
|
+
expect(Net::HTTP).to receive(:new).with('artifactory.example', 443).and_return(http)
|
106
|
+
expect(http).to receive(:use_ssl=).with(true)
|
107
|
+
expect(http).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
108
|
+
expect(subject).to eq http
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'with $STOVE_NO_SSL_VERIFY' do
|
113
|
+
around do |ex|
|
114
|
+
old_value = ENV['STOVE_NO_SSL_VERIFY']
|
115
|
+
ENV['STOVE_NO_SSL_VERIFY'] = 'true'
|
116
|
+
begin
|
117
|
+
ex.run
|
118
|
+
ensure
|
119
|
+
ENV['STOVE_NO_SSL_VERIFY'] = old_value
|
120
|
+
end
|
121
|
+
end
|
122
|
+
it 'sets verify mode VERIFY_NONE' do
|
123
|
+
expect(Net::HTTP).to receive(:new).with('artifactory.example', 443).and_return(http)
|
124
|
+
expect(http).to receive(:use_ssl=).with(true)
|
125
|
+
expect(http).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
126
|
+
expect(subject).to eq http
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
data/stove.gemspec
CHANGED
@@ -29,4 +29,6 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.add_development_dependency 'community-zero', '~> 2.0'
|
30
30
|
spec.add_development_dependency 'rake'
|
31
31
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
32
|
+
spec.add_development_dependency 'rspec-command', '~> 1.0'
|
33
|
+
spec.add_development_dependency 'webmock', '~> 3.0'
|
32
34
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
You did not specify an Artifactory API key! You can pass the key either via a command line argument:
|
2
|
+
|
3
|
+
stove --artifactory-key sUeEpLXJvfhw3UZHbVPrGCgdou8VI6fXpD5sUHd0pKAxCmuGWNHLgerpFPkCJ2EjBBPYcM4My
|
4
|
+
|
5
|
+
Or via a file path:
|
6
|
+
|
7
|
+
stove --artifactory-key @~/.artifactory/api.key
|
8
|
+
|
9
|
+
Or via an environment variable:
|
10
|
+
|
11
|
+
export ARTIFACTORY_API_KEY=sUeEpLXJvfhw3UZHbVPrGCgdou8VI6fXpD5sUHd0pKAxCmuGWNHLgerpFPkCJ2EjBBPYcM4My
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stove
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Seth Vargo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chef-api
|
@@ -108,6 +108,34 @@ dependencies:
|
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '3.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rspec-command
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: webmock
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '3.0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '3.0'
|
111
139
|
description: A utility for releasing Chef community cookbooks
|
112
140
|
email:
|
113
141
|
- sethvargo@gmail.com
|
@@ -137,6 +165,7 @@ files:
|
|
137
165
|
- features/support/stove.pem
|
138
166
|
- features/support/stove/git.rb
|
139
167
|
- lib/stove.rb
|
168
|
+
- lib/stove/artifactory.rb
|
140
169
|
- lib/stove/cli.rb
|
141
170
|
- lib/stove/community.rb
|
142
171
|
- lib/stove/config.rb
|
@@ -150,6 +179,7 @@ files:
|
|
150
179
|
- lib/stove/mixins/optionable.rb
|
151
180
|
- lib/stove/mixins/validatable.rb
|
152
181
|
- lib/stove/packager.rb
|
182
|
+
- lib/stove/plugins/artifactory.rb
|
153
183
|
- lib/stove/plugins/base.rb
|
154
184
|
- lib/stove/plugins/community.rb
|
155
185
|
- lib/stove/plugins/git.rb
|
@@ -158,15 +188,22 @@ files:
|
|
158
188
|
- lib/stove/util.rb
|
159
189
|
- lib/stove/validator.rb
|
160
190
|
- lib/stove/version.rb
|
191
|
+
- spec/fixtures/integration_cookbook/attributes/default.rb
|
192
|
+
- spec/fixtures/integration_cookbook/metadata.rb
|
193
|
+
- spec/fixtures/integration_cookbook/recipes/default.rb
|
194
|
+
- spec/integration/artifactory_spec.rb
|
161
195
|
- spec/integration/cookbook_spec.rb
|
162
196
|
- spec/spec_helper.rb
|
163
197
|
- spec/support/generators.rb
|
198
|
+
- spec/unit/artifactory_spec.rb
|
164
199
|
- spec/unit/cookbook/metadata_spec.rb
|
165
200
|
- spec/unit/error_spec.rb
|
166
201
|
- stove.gemspec
|
167
202
|
- templates/errors/abstract_method.erb
|
203
|
+
- templates/errors/artifactory_key_validation_failed.erb
|
168
204
|
- templates/errors/community_key_validation_failed.erb
|
169
205
|
- templates/errors/community_username_validation_failed.erb
|
206
|
+
- templates/errors/cookbook_already_exists.erb
|
170
207
|
- templates/errors/git_clean_validation_failed.erb
|
171
208
|
- templates/errors/git_failed.erb
|
172
209
|
- templates/errors/git_repository_validation_failed.erb
|
@@ -209,8 +246,13 @@ test_files:
|
|
209
246
|
- features/support/env.rb
|
210
247
|
- features/support/stove.pem
|
211
248
|
- features/support/stove/git.rb
|
249
|
+
- spec/fixtures/integration_cookbook/attributes/default.rb
|
250
|
+
- spec/fixtures/integration_cookbook/metadata.rb
|
251
|
+
- spec/fixtures/integration_cookbook/recipes/default.rb
|
252
|
+
- spec/integration/artifactory_spec.rb
|
212
253
|
- spec/integration/cookbook_spec.rb
|
213
254
|
- spec/spec_helper.rb
|
214
255
|
- spec/support/generators.rb
|
256
|
+
- spec/unit/artifactory_spec.rb
|
215
257
|
- spec/unit/cookbook/metadata_spec.rb
|
216
258
|
- spec/unit/error_spec.rb
|