diff options
author | Stan Hu <stanhu@gmail.com> | 2018-09-07 22:30:53 +0000 |
---|---|---|
committer | Stan Hu <stanhu@gmail.com> | 2018-09-07 22:30:53 +0000 |
commit | 2cf0298fa5d7c4da8bed44200359dd69a6a08eca (patch) | |
tree | c2cf728f776f60f8bae83a418d5a2c2faac66324 | |
parent | 4f0a474cb88f7edae994e82d2ca977b1e6ed2e47 (diff) | |
parent | 524d523ed654469858a7a482d56f4d4661c61036 (diff) | |
download | gitlab-shell-2cf0298fa5d7c4da8bed44200359dd69a6a08eca.tar.gz |
Merge branch 'ash.mckenzie/custom-action-support' into 'master'v8.3.0
Custom Action support
See merge request gitlab-org/gitlab-shell!215
-rw-r--r-- | lib/action.rb | 4 | ||||
-rw-r--r-- | lib/action/custom.rb | 129 | ||||
-rw-r--r-- | lib/gitlab_access_status.rb | 21 | ||||
-rw-r--r-- | lib/gitlab_net.rb | 17 | ||||
-rw-r--r-- | lib/gitlab_shell.rb | 55 | ||||
-rw-r--r-- | lib/http_codes.rb | 3 | ||||
-rw-r--r-- | lib/http_helper.rb | 2 | ||||
-rw-r--r-- | spec/action/custom_spec.rb | 136 | ||||
-rw-r--r-- | spec/gitlab_access_spec.rb | 2 | ||||
-rw-r--r-- | spec/gitlab_config_spec.rb | 15 | ||||
-rw-r--r-- | spec/gitlab_keys_spec.rb | 2 | ||||
-rw-r--r-- | spec/gitlab_shell_spec.rb | 58 | ||||
-rw-r--r-- | spec/vcr_cassettes/custom-action-not-ok-json.yml | 40 | ||||
-rw-r--r-- | spec/vcr_cassettes/custom-action-not-ok-not-json.yml | 40 | ||||
-rw-r--r-- | spec/vcr_cassettes/custom-action-ok-not-json.yml | 51 | ||||
-rw-r--r-- | spec/vcr_cassettes/custom-action-ok.yml | 99 |
16 files changed, 618 insertions, 56 deletions
diff --git a/lib/action.rb b/lib/action.rb new file mode 100644 index 0000000..28c1c14 --- /dev/null +++ b/lib/action.rb @@ -0,0 +1,4 @@ +require_relative 'action/custom' + +module Action +end diff --git a/lib/action/custom.rb b/lib/action/custom.rb new file mode 100644 index 0000000..ba0e650 --- /dev/null +++ b/lib/action/custom.rb @@ -0,0 +1,129 @@ +require 'base64' + +require_relative '../http_helper' + +module Action + class Custom + include HTTPHelper + + class BaseError < StandardError; end + class MissingPayloadError < BaseError; end + class MissingAPIEndpointsError < BaseError; end + class MissingDataError < BaseError; end + class UnsuccessfulError < BaseError; end + + NO_MESSAGE_TEXT = 'No message'.freeze + DEFAULT_HEADERS = { 'Content-Type' => 'application/json' }.freeze + + def initialize(gl_id, payload) + @gl_id = gl_id + @payload = payload + end + + def execute + validate! + result = process_api_endpoints + + if result && HTTP_SUCCESS_CODES.include?(result.code) + result + else + raise_unsuccessful!(result) + end + end + + private + + attr_reader :gl_id, :payload + + def process_api_endpoints + output = '' + resp = nil + + data_with_gl_id = data.merge('gl_id' => gl_id) + + api_endpoints.each do |endpoint| + url = "#{base_url}#{endpoint}" + json = { 'data' => data_with_gl_id, 'output' => output } + + resp = post(url, {}, headers: DEFAULT_HEADERS, options: { json: json }) + return resp unless HTTP_SUCCESS_CODES.include?(resp.code) + + begin + body = JSON.parse(resp.body) + rescue JSON::ParserError + raise UnsuccessfulError, 'Response was not valid JSON' + end + + print_flush(body['result']) + + # In the context of the git push sequence of events, it's necessary to read + # stdin in order to capture output to pass onto subsequent commands + output = read_stdin + end + + resp + end + + def base_url + config.gitlab_url + end + + def data + @data ||= payload['data'] + end + + def api_endpoints + data['api_endpoints'] + end + + def config + @config ||= GitlabConfig.new + end + + def api + @api ||= GitlabNet.new + end + + def read_stdin + Base64.encode64($stdin.read) + end + + def print_flush(str) + return false unless str + print(Base64.decode64(str)) + STDOUT.flush + end + + def validate! + validate_payload! + validate_data! + validate_api_endpoints! + end + + def validate_payload! + raise MissingPayloadError if !payload.is_a?(Hash) || payload.empty? + end + + def validate_data! + raise MissingDataError unless data.is_a?(Hash) + end + + def validate_api_endpoints! + raise MissingAPIEndpointsError if !api_endpoints.is_a?(Array) || + api_endpoints.empty? + end + + def raise_unsuccessful!(result) + message = begin + body = JSON.parse(result.body) + message = body['message'] + message = Base64.decode64(body['result']) if !message && body['result'] && !body['result'].empty? + message ? message : NO_MESSAGE_TEXT + rescue JSON::ParserError + NO_MESSAGE_TEXT + end + + raise UnsuccessfulError, "#{message} (#{result.code})" + end + end +end diff --git a/lib/gitlab_access_status.rb b/lib/gitlab_access_status.rb index 00ea8de..8483863 100644 --- a/lib/gitlab_access_status.rb +++ b/lib/gitlab_access_status.rb @@ -1,10 +1,16 @@ require 'json' +require_relative 'http_codes' class GitAccessStatus - attr_reader :message, :gl_repository, :gl_id, :gl_username, :gitaly, :git_protocol, :git_config_options + include HTTPCodes - def initialize(status, message, gl_repository:, gl_id:, gl_username:, gitaly:, git_protocol:, git_config_options:) + attr_reader :message, :gl_repository, :gl_id, :gl_username, :gitaly, :git_protocol, :git_config_options, :payload + + def initialize(status, status_code, message, gl_repository: nil, gl_id: nil, + gl_username: nil, gitaly: nil, git_protocol: nil, + git_config_options: nil, payload: nil) @status = status + @status_code = status_code @message = message @gl_repository = gl_repository @gl_id = gl_id @@ -12,21 +18,28 @@ class GitAccessStatus @git_config_options = git_config_options @gitaly = gitaly @git_protocol = git_protocol + @payload = payload end - def self.create_from_json(json) + def self.create_from_json(json, status_code) values = JSON.parse(json) new(values["status"], + status_code, values["message"], gl_repository: values["gl_repository"], gl_id: values["gl_id"], gl_username: values["gl_username"], git_config_options: values["git_config_options"], gitaly: values["gitaly"], - git_protocol: values["git_protocol"]) + git_protocol: values["git_protocol"], + payload: values["payload"]) end def allowed? @status end + + def custom_action? + @status_code == HTTP_MULTIPLE_CHOICES + end end diff --git a/lib/gitlab_net.rb b/lib/gitlab_net.rb index 5af2da6..57ae452 100644 --- a/lib/gitlab_net.rb +++ b/lib/gitlab_net.rb @@ -3,10 +3,8 @@ require 'openssl' require 'json' require_relative 'gitlab_config' -require_relative 'gitlab_logger' require_relative 'gitlab_access' require_relative 'gitlab_lfs_authentication' -require_relative 'httpunix' require_relative 'http_helper' class GitlabNet # rubocop:disable Metrics/ClassLength @@ -35,18 +33,11 @@ class GitlabNet # rubocop:disable Metrics/ClassLength url = "#{internal_api_endpoint}/allowed" resp = post(url, params) - case resp.code.to_s - when HTTP_SUCCESS, HTTP_UNAUTHORIZED, HTTP_NOT_FOUND - GitAccessStatus.create_from_json(resp.body) + case resp.code + when HTTP_SUCCESS, HTTP_MULTIPLE_CHOICES, HTTP_UNAUTHORIZED, HTTP_NOT_FOUND + GitAccessStatus.create_from_json(resp.body, resp.code) else - GitAccessStatus.new(false, - 'API is not accessible', - gl_repository: nil, - gl_id: nil, - gl_username: nil, - git_config_options: nil, - gitaly: nil, - git_protocol: nil) + GitAccessStatus.new(false, resp.code, 'API is not accessible') end end diff --git a/lib/gitlab_shell.rb b/lib/gitlab_shell.rb index 4d0b26f..79af861 100644 --- a/lib/gitlab_shell.rb +++ b/lib/gitlab_shell.rb @@ -5,16 +5,22 @@ require 'pathname' require_relative 'gitlab_net' require_relative 'gitlab_metrics' +require_relative 'action' class GitlabShell # rubocop:disable Metrics/ClassLength class AccessDeniedError < StandardError; end class DisallowedCommandError < StandardError; end class InvalidRepositoryPathError < StandardError; end + GIT_UPLOAD_PACK_COMMAND = 'git-upload-pack' + GIT_RECEIVE_PACK_COMMAND = 'git-receive-pack' + GIT_UPLOAD_ARCHIVE_COMMAND = 'git-upload-archive' + GIT_LFS_AUTHENTICATE_COMMAND = 'git-lfs-authenticate' + GITALY_COMMANDS = { - 'git-upload-pack' => File.join(ROOT_PATH, 'bin', 'gitaly-upload-pack'), - 'git-upload-archive' => File.join(ROOT_PATH, 'bin', 'gitaly-upload-archive'), - 'git-receive-pack' => File.join(ROOT_PATH, 'bin', 'gitaly-receive-pack') + GIT_UPLOAD_PACK_COMMAND => File.join(ROOT_PATH, 'bin', 'gitaly-upload-pack'), + GIT_UPLOAD_ARCHIVE_COMMAND => File.join(ROOT_PATH, 'bin', 'gitaly-upload-archive'), + GIT_RECEIVE_PACK_COMMAND => File.join(ROOT_PATH, 'bin', 'gitaly-receive-pack') }.freeze GIT_COMMANDS = (GITALY_COMMANDS.keys + ['git-lfs-authenticate']).freeze @@ -45,8 +51,17 @@ class GitlabShell # rubocop:disable Metrics/ClassLength args = Shellwords.shellwords(origin_cmd) args = parse_cmd(args) + access_status = nil + if GIT_COMMANDS.include?(args.first) - GitlabMetrics.measure('verify-access') { verify_access } + access_status = GitlabMetrics.measure('verify-access') { verify_access } + + @gl_repository = access_status.gl_repository + @git_protocol = ENV['GIT_PROTOCOL'] + @gitaly = access_status.gitaly + @username = access_status.gl_username + @git_config_options = access_status.git_config_options + @gl_id = access_status.gl_id if defined?(@who) elsif !defined?(@gl_id) # We're processing an API command like 2fa_recovery_codes, but # don't have a @gl_id yet, that means we're in the "username" @@ -55,6 +70,13 @@ class GitlabShell # rubocop:disable Metrics/ClassLength user end + if @command == GIT_RECEIVE_PACK_COMMAND && access_status.custom_action? + # If the response from /api/v4/allowed is a HTTP 300, we need to perform + # a Custom Action and therefore should return and not call process_cmd() + # + return process_custom_action(access_status) + end + process_cmd(args) true @@ -63,17 +85,19 @@ class GitlabShell # rubocop:disable Metrics/ClassLength false rescue AccessDeniedError => ex $logger.warn('Access denied', command: origin_cmd, user: log_username) - $stderr.puts "GitLab: #{ex.message}" false rescue DisallowedCommandError $logger.warn('Denied disallowed command', command: origin_cmd, user: log_username) - $stderr.puts "GitLab: Disallowed command" false rescue InvalidRepositoryPathError $stderr.puts "GitLab: Invalid repository path" false + rescue Action::Custom::BaseError => ex + $logger.warn('Custom action error', command: origin_cmd, user: log_username) + $stderr.puts "GitLab: #{ex.message}" + false end protected @@ -94,14 +118,14 @@ class GitlabShell # rubocop:disable Metrics/ClassLength raise DisallowedCommandError unless GIT_COMMANDS.include?(@command) case @command - when 'git-lfs-authenticate' + when GIT_LFS_AUTHENTICATE_COMMAND raise DisallowedCommandError unless args.count >= 2 @repo_name = args[1] case args[2] when 'download' - @git_access = 'git-upload-pack' + @git_access = GIT_UPLOAD_PACK_COMMAND when 'upload' - @git_access = 'git-receive-pack' + @git_access = GIT_RECEIVE_PACK_COMMAND else raise DisallowedCommandError end @@ -118,14 +142,11 @@ class GitlabShell # rubocop:disable Metrics/ClassLength raise AccessDeniedError, status.message unless status.allowed? - @gl_repository = status.gl_repository - @git_protocol = ENV['GIT_PROTOCOL'] - @gitaly = status.gitaly - @username = status.gl_username - @git_config_options = status.git_config_options - if defined?(@who) - @gl_id = status.gl_id - end + status + end + + def process_custom_action(access_status) + Action::Custom.new(@gl_id, access_status.payload).execute end def process_cmd(args) diff --git a/lib/http_codes.rb b/lib/http_codes.rb index 24b3b5a..5f79095 100644 --- a/lib/http_codes.rb +++ b/lib/http_codes.rb @@ -1,5 +1,8 @@ module HTTPCodes HTTP_SUCCESS = '200'.freeze + HTTP_CREATED = '201'.freeze + HTTP_MULTIPLE_CHOICES = '300'.freeze HTTP_UNAUTHORIZED = '401'.freeze HTTP_NOT_FOUND = '404'.freeze + HTTP_SUCCESS_CODES = [HTTP_SUCCESS, HTTP_CREATED, HTTP_MULTIPLE_CHOICES].freeze end diff --git a/lib/http_helper.rb b/lib/http_helper.rb index 1e75833..55c504c 100644 --- a/lib/http_helper.rb +++ b/lib/http_helper.rb @@ -1,4 +1,6 @@ require_relative 'http_codes' +require_relative 'httpunix' +require_relative 'gitlab_logger' module HTTPHelper include HTTPCodes diff --git a/spec/action/custom_spec.rb b/spec/action/custom_spec.rb new file mode 100644 index 0000000..e85df71 --- /dev/null +++ b/spec/action/custom_spec.rb @@ -0,0 +1,136 @@ +require_relative '../spec_helper' +require_relative '../../lib/action/custom' + +describe Action::Custom do + let(:repo_name) { 'gitlab-ci.git' } + let(:gl_id) { 'key-1' } + let(:secret) { "0a3938d9d95d807e94d937af3a4fbbea" } + let(:base_url) { 'http://localhost:3000' } + + subject { described_class.new(gl_id, payload) } + + describe '#execute' do + context 'with an empty payload' do + let(:payload) { {} } + + it 'raises a MissingPayloadError exception' do + expect { subject.execute }.to raise_error(Action::Custom::MissingPayloadError) + end + end + + context 'with api_endpoints defined' do + before do + allow(subject).to receive(:base_url).and_return(base_url) + allow(subject).to receive(:secret_token).and_return(secret) + allow($stdin).to receive(:read).and_return('') + end + + context 'that are valid' do + let(:payload) do + { + 'action' => 'geo_proxy_to_primary', + 'data' => { + 'api_endpoints' => %w{/api/v4/fake/info_refs /api/v4/fake/push}, + 'gl_username' => 'user1', + 'primary_repo' => 'http://localhost:3001/user1/repo1.git' + } + } + end + + context 'and responds correctly' do + it 'returns an instance of Net::HTTPCreated' do + VCR.use_cassette("custom-action-ok") do + expect(subject.execute).to be_instance_of(Net::HTTPCreated) + end + end + end + + context 'but responds incorrectly' do + it 'raises an UnsuccessfulError exception' do + VCR.use_cassette("custom-action-ok-not-json") do + expect { + subject.execute + }.to raise_error(Action::Custom::UnsuccessfulError, 'Response was not valid JSON') + end + end + end + end + + context 'that are invalid' do + context 'where api_endpoints gl_id is missing' do + let(:payload) do + { + 'action' => 'geo_proxy_to_primary', + 'data' => { + 'gl_username' => 'user1', + 'primary_repo' => 'http://localhost:3001/user1/repo1.git' + } + } + end + + it 'raises a MissingAPIEndpointsError exception' do + expect { subject.execute }.to raise_error(Action::Custom::MissingAPIEndpointsError) + end + end + + context 'where api_endpoints are empty' do + let(:payload) do + { + 'action' => 'geo_proxy_to_primary', + 'data' => { + 'api_endpoints' => [], + 'gl_username' => 'user1', + 'primary_repo' => 'http://localhost:3001/user1/repo1.git' + } + } + end + + it 'raises a MissingAPIEndpointsError exception' do + expect { subject.execute }.to raise_error(Action::Custom::MissingAPIEndpointsError) + end + end + + context 'where data gl_id is missing' do + let(:payload) { { 'api_endpoints' => %w{/api/v4/fake/info_refs /api/v4/fake/push} } } + + it 'raises a MissingDataError exception' do + expect { subject.execute }.to raise_error(Action::Custom::MissingDataError) + end + end + + context 'where API endpoints are bad' do + let(:payload) do + { + 'action' => 'geo_proxy_to_primary', + 'data' => { + 'api_endpoints' => %w{/api/v4/fake/info_refs_bad /api/v4/fake/push_bad}, + 'gl_username' => 'user1', + 'primary_repo' => 'http://localhost:3001/user1/repo1.git' + } + } + end + + context 'and response is JSON' do + it 'raises an UnsuccessfulError exception' do + VCR.use_cassette("custom-action-not-ok-json") do + expect { + subject.execute + }.to raise_error(Action::Custom::UnsuccessfulError, 'You cannot perform write operations on a read-only instance (403)') + end + end + end + + context 'and response is not JSON' do + it 'raises an UnsuccessfulError exception' do + VCR.use_cassette("custom-action-not-ok-not-json") do + expect { + subject.execute + }.to raise_error(Action::Custom::UnsuccessfulError, 'No message (403)') + end + end + end + end + end + end + end +end diff --git a/spec/gitlab_access_spec.rb b/spec/gitlab_access_spec.rb index a4f633d..c9922dd 100644 --- a/spec/gitlab_access_spec.rb +++ b/spec/gitlab_access_spec.rb @@ -8,6 +8,7 @@ describe GitlabAccess do let(:api) do double(GitlabNet).tap do |api| allow(api).to receive(:check_access).and_return(GitAccessStatus.new(true, + HTTPCodes::HTTP_SUCCESS, 'ok', gl_repository: 'project-1', gl_id: 'user-123', @@ -45,6 +46,7 @@ describe GitlabAccess do before do allow(api).to receive(:check_access).and_return(GitAccessStatus.new( false, + HTTPCodes::HTTP_UNAUTHORIZED, 'denied', gl_repository: nil, gl_id: nil, diff --git a/spec/gitlab_config_spec.rb b/spec/gitlab_config_spec.rb index c262116..d12b657 100644 --- a/spec/gitlab_config_spec.rb +++ b/spec/gitlab_config_spec.rb @@ -3,29 +3,34 @@ require_relative '../lib/gitlab_config' describe GitlabConfig do let(:config) { GitlabConfig.new } + let(:config_data) { {} } - describe :gitlab_url do + before { expect(YAML).to receive(:load_file).and_return(config_data) } + + describe '#gitlab_url' do let(:url) { 'http://test.com' } + subject { config.gitlab_url } - before { config.send(:config)['gitlab_url'] = url } + + before { config_data['gitlab_url'] = url } it { is_expected.not_to be_empty } it { is_expected.to eq(url) } context 'remove trailing slashes' do - before { config.send(:config)['gitlab_url'] = url + '//' } + before { config_data['gitlab_url'] = url + '//' } it { is_expected.to eq(url) } end end - describe :audit_usernames do + describe '#audit_usernames' do subject { config.audit_usernames } it("returns false by default") { is_expected.to eq(false) } end - describe :log_format do + describe '#log_format' do subject { config.log_format } it 'returns "text" by default' do diff --git a/spec/gitlab_keys_spec.rb b/spec/gitlab_keys_spec.rb index 87492d8..f86d00d 100644 --- a/spec/gitlab_keys_spec.rb +++ b/spec/gitlab_keys_spec.rb @@ -307,7 +307,7 @@ describe GitlabKeys do key.send :lock, 1 do sleep 2 end - end.to raise_error + end.to raise_error(Timeout::Error, 'execution expired') end it "should actually lock file" do diff --git a/spec/gitlab_shell_spec.rb b/spec/gitlab_shell_spec.rb index 135a474..77fb6cd 100644 --- a/spec/gitlab_shell_spec.rb +++ b/spec/gitlab_shell_spec.rb @@ -22,23 +22,26 @@ describe GitlabShell do let(:git_config_options) { ['receive.MaxInputSize=10000'] } - let(:gitaly_check_access) { GitAccessStatus.new( - true, - 'ok', - gl_repository: gl_repository, - gl_id: gl_id, - gl_username: gl_username, - git_config_options: git_config_options, - gitaly: { 'repository' => { 'relative_path' => repo_name, 'storage_name' => 'default'} , 'address' => 'unix:gitaly.socket' }, - git_protocol: git_protocol - ) - } + let(:gitaly_check_access) do + GitAccessStatus.new( + true, + HTTPCodes::HTTP_SUCCESS, + 'ok', + gl_repository: gl_repository, + gl_id: gl_id, + gl_username: gl_username, + git_config_options: git_config_options, + gitaly: { 'repository' => { 'relative_path' => repo_name, 'storage_name' => 'default'} , 'address' => 'unix:gitaly.socket' }, + git_protocol: git_protocol + ) + end let(:api) do double(GitlabNet).tap do |api| allow(api).to receive(:discover).and_return({ 'name' => 'John Doe', 'username' => 'testuser' }) allow(api).to receive(:check_access).and_return(GitAccessStatus.new( true, + HTTPCodes::HTTP_SUCCESS, 'ok', gl_repository: gl_repository, gl_id: gl_id, @@ -68,13 +71,13 @@ describe GitlabShell do allow_any_instance_of(GitlabConfig).to receive(:audit_usernames).and_return(false) end - describe :initialize do + describe '#initialize' do let(:ssh_cmd) { 'git-receive-pack' } it { expect(subject.gl_id).to eq gl_id } end - describe :parse_cmd do + describe '#parse_cmd' do describe 'git' do context 'w/o namespace' do let(:ssh_args) { %w(git-upload-pack gitlab-ci.git) } @@ -161,7 +164,7 @@ describe GitlabShell do end end - describe :exec do + describe '#exec' do let(:gitaly_message) do JSON.dump( 'repository' => { 'relative_path' => repo_name, 'storage_name' => 'default' }, @@ -253,6 +256,29 @@ describe GitlabShell do user_string = "user with id #{gl_id}" expect($logger).to receive(:info).with(message, command: "gitaly-receive-pack unix:gitaly.socket #{gitaly_message}", user: user_string) end + + context 'with a custom action' do + let(:fake_payload) { { 'api_endpoints' => [ '/fake/api/endpoint' ], 'data' => {} } } + let(:custom_action_gitlab_access_status) do + GitAccessStatus.new( + true, + HTTPCodes::HTTP_MULTIPLE_CHOICES, + 'Multiple Choices', + payload: fake_payload + ) + end + let(:action_custom) { double(Action::Custom) } + + before do + allow(api).to receive(:check_access).and_return(custom_action_gitlab_access_status) + end + + it "should not process the command" do + expect(subject).to_not receive(:process_cmd).with(%w(git-receive-pack gitlab-ci.git)) + expect(Action::Custom).to receive(:new).with(gl_id, fake_payload).and_return(action_custom) + expect(action_custom).to receive(:execute) + end + end end context 'gitaly-receive-pack' do @@ -410,7 +436,7 @@ describe GitlabShell do end end - describe :validate_access do + describe '#validate_access' do let(:ssh_cmd) { "git-upload-pack gitlab-ci.git" } describe 'check access with api' do @@ -442,7 +468,7 @@ describe GitlabShell do end end - describe :api do + describe '#api' do let(:shell) { GitlabShell.new(gl_id) } subject { shell.send :api } diff --git a/spec/vcr_cassettes/custom-action-not-ok-json.yml b/spec/vcr_cassettes/custom-action-not-ok-json.yml new file mode 100644 index 0000000..e50719d --- /dev/null +++ b/spec/vcr_cassettes/custom-action-not-ok-json.yml @@ -0,0 +1,40 @@ +--- +http_interactions: +- request: + method: post + uri: http://localhost:3000/api/v4/fake/info_refs_bad + body: + encoding: UTF-8 + string: '{"data":{"gl_username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git","gl_id":"key-11"},"output":"","secret_token":"0a3938d9d95d807e94d937af3a4fbbea\n"}' + headers: + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Host: + - localhost + response: + status: + code: 403 + message: Forbidden + headers: + Date: + - Fri, 20 Jul 2018 06:54:21 GMT + Connection: + - close + Content-Type: + - application/json + X-Request-Id: + - ea0644ac-e1ad-45f6-aa72-cc7910274318 + X-Runtime: + - '1.236672' + body: + encoding: UTF-8 + string: '{"message":"You cannot perform write operations on a read-only instance"}' + http_version: + recorded_at: Fri, 20 Jul 2018 06:54:21 GMT +recorded_with: VCR 2.4.0 diff --git a/spec/vcr_cassettes/custom-action-not-ok-not-json.yml b/spec/vcr_cassettes/custom-action-not-ok-not-json.yml new file mode 100644 index 0000000..b60a93a --- /dev/null +++ b/spec/vcr_cassettes/custom-action-not-ok-not-json.yml @@ -0,0 +1,40 @@ +--- +http_interactions: +- request: + method: post + uri: http://localhost:3000/api/v4/fake/info_refs_bad + body: + encoding: UTF-8 + string: '{"data":{"gl_username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git","gl_id":"key-11"},"output":"","secret_token":"0a3938d9d95d807e94d937af3a4fbbea\n"}' + headers: + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Host: + - localhost + response: + status: + code: 403 + message: Forbidden + headers: + Date: + - Fri, 20 Jul 2018 06:54:21 GMT + Connection: + - close + Content-Type: + - application/json + X-Request-Id: + - ea0644ac-e1ad-45f6-aa72-cc7910274318 + X-Runtime: + - '1.236672' + body: + encoding: UTF-8 + string: '""' + http_version: + recorded_at: Fri, 20 Jul 2018 06:54:21 GMT +recorded_with: VCR 2.4.0 diff --git a/spec/vcr_cassettes/custom-action-ok-not-json.yml b/spec/vcr_cassettes/custom-action-ok-not-json.yml new file mode 100644 index 0000000..3bfb390 --- /dev/null +++ b/spec/vcr_cassettes/custom-action-ok-not-json.yml @@ -0,0 +1,51 @@ +--- +http_interactions: +- request: + method: post + uri: http://localhost:3000/api/v4/fake/info_refs + body: + encoding: UTF-8 + string: '{"data":{"gl_username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git","gl_id":"key-1"},"output":"","secret_token":"0a3938d9d95d807e94d937af3a4fbbea"}' + headers: + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Host: + - localhost + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 20 Jul 2018 06:18:58 GMT + Connection: + - close + X-Frame-Options: + - SAMEORIGIN + X-Content-Type-Options: + - nosniff + Content-Type: + - application/json + Content-Length: + - '172' + Vary: + - Origin + Etag: + - W/"7d01e1e3dbcbe7cca9607461352f8244" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 03afa234-b6be-49ab-9392-4aa35c5dee25 + X-Runtime: + - '1.436040' + body: + encoding: UTF-8 + string: '""' + http_version: + recorded_at: Fri, 20 Jul 2018 06:18:58 GMT diff --git a/spec/vcr_cassettes/custom-action-ok.yml b/spec/vcr_cassettes/custom-action-ok.yml new file mode 100644 index 0000000..a057441 --- /dev/null +++ b/spec/vcr_cassettes/custom-action-ok.yml @@ -0,0 +1,99 @@ +--- +http_interactions: +- request: + method: post + uri: http://localhost:3000/api/v4/fake/info_refs + body: + encoding: UTF-8 + string: '{"data":{"gl_username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git","gl_id":"key-1"},"output":"","secret_token":"0a3938d9d95d807e94d937af3a4fbbea"}' + headers: + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Host: + - localhost + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 20 Jul 2018 06:18:58 GMT + Connection: + - close + X-Frame-Options: + - SAMEORIGIN + X-Content-Type-Options: + - nosniff + Content-Type: + - application/json + Content-Length: + - '172' + Vary: + - Origin + Etag: + - W/"7d01e1e3dbcbe7cca9607461352f8244" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 03afa234-b6be-49ab-9392-4aa35c5dee25 + X-Runtime: + - '1.436040' + body: + encoding: UTF-8 + string: '{"result":"info_refs-result"}' + http_version: + recorded_at: Fri, 20 Jul 2018 06:18:58 GMT +- request: + method: post + uri: http://localhost:3000/api/v4/fake/push + body: + encoding: UTF-8 + string: '{"data":{"gl_username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git","gl_id":"key-1"},"output":"info_refs-result","secret_token":"0a3938d9d95d807e94d937af3a4fbbea"}' + headers: + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Host: + - localhost + response: + status: + code: 201 + message: Created + headers: + Date: + - Fri, 20 Jul 2018 06:19:08 GMT + Connection: + - close + X-Frame-Options: + - SAMEORIGIN + X-Content-Type-Options: + - nosniff + Content-Type: + - application/json + Content-Length: + - '13' + Vary: + - Origin + Cache-Control: + - no-cache + X-Request-Id: + - 0c6894ac-7f8e-4cdb-871f-4cb64d3731ca + X-Runtime: + - '0.786754' + body: + encoding: UTF-8 + string: '{"result":"push-result"}' + http_version: + recorded_at: Fri, 20 Jul 2018 06:19:08 GMT +recorded_with: VCR 2.4.0 |