From 7b6d720a2a483a02cf7713fd0b9141db32b96ea0 Mon Sep 17 00:00:00 2001 From: Vilnius Ramanauskas Date: Wed, 17 Jul 2024 15:46:55 +0300 Subject: [PATCH] New flow handling --- REFERENCE.md | 12 +-- .../functions/gitlab_ci_runner/register.rb | 8 +- .../gitlab_ci_runner/register_to_file.rb | 8 +- .../functions/gitlab_ci_runner/unregister.rb | 2 +- .../gitlab_ci_runner/unregister_from_file.rb | 3 +- lib/puppet_x/gitlab/runner.rb | 6 ++ manifests/runner.pp | 6 +- metadata.json | 5 +- spec/functions/register_spec.rb | 28 +++++-- spec/functions/register_to_file_spec.rb | 83 ++++++++++++++----- spec/unit/puppet_x/gitlab/runner_spec.rb | 23 ++++- types/register.pp | 2 +- 12 files changed, 139 insertions(+), 47 deletions(-) diff --git a/REFERENCE.md b/REFERENCE.md index 70af003..337e991 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -34,7 +34,7 @@ * [`Gitlab_ci_runner::Keyserver`](#Gitlab_ci_runner--Keyserver): Type to match repo_keyserver Regex from: https://github.com/puppetlabs/puppetlabs-apt/blob/main/manifests/key.pp * [`Gitlab_ci_runner::Log_format`](#Gitlab_ci_runner--Log_format): Gitlab Runner log format configuration * [`Gitlab_ci_runner::Log_level`](#Gitlab_ci_runner--Log_level): Gitlab Runner log level configuration -* [`Gitlab_ci_runner::Register`](#Gitlab_ci_runner--Register): A struct of all possible additionl options for gitlab_ci_runner::register +* [`Gitlab_ci_runner::Register`](#Gitlab_ci_runner--Register): A struct of all possible additional options for gitlab_ci_runner::register * [`Gitlab_ci_runner::Register_parameters`](#Gitlab_ci_runner--Register_parameters): A enum containing a possible keys used for Gitlab runner registrations * [`Gitlab_ci_runner::Session_server`](#Gitlab_ci_runner--Session_server): Gitlab Runner session_server configuration @@ -503,7 +503,7 @@ puppet apply -e "notice(gitlab_ci_runner::register('https://gitlab.com', 'regist A function that registers a Gitlab runner on a Gitlab instance. Be careful, this will be triggered on noop runs as well! -Returns: `Struct[{ id => Integer[1], token => String[1], }]` Returns a hash with the runner id and authentcation token +Returns: `Struct[{ id => Integer[1], token => String[1], }]` Returns a hash with the runner id and authentication token ##### Examples @@ -673,7 +673,7 @@ puppet apply -e "notice(gitlab_ci_runner::unregister('https://gitlab.com', 'runn A function that unregisters a Gitlab runner from a Gitlab instance. Be careful, this will be triggered on noop runs as well! -Returns: `Struct[{ status => Enum['success'], }]` Returns a hash with the runner id and authentcation token +Returns: `Struct[{ status => Enum['success'], }]` Returns status ##### Examples @@ -724,7 +724,7 @@ file { '/etc/gitlab-runner/auth-token-testrunner': A function that unregisters a Gitlab runner from a Gitlab instance, if the local token is there. This is meant to be used in conjunction with the gitlab_ci_runner::register_to_file function. -Returns: `Any` +Returns: `String` Returns status ##### Examples @@ -747,7 +747,7 @@ The url to your Gitlab instance. Please only provide the host part (e.g https:// Data type: `String[1]` -The name of the runner. Use as identifier for the retrived auth token. +The name of the runner. Use as identifier for the retrieved auth token. ##### `proxy` @@ -784,7 +784,7 @@ Alias of `Enum['debug', 'info', 'warn', 'error', 'fatal', 'panic']` ### `Gitlab_ci_runner::Register` -A struct of all possible additionl options for gitlab_ci_runner::register +A struct of all possible additional options for gitlab_ci_runner::register Alias of diff --git a/lib/puppet/functions/gitlab_ci_runner/register.rb b/lib/puppet/functions/gitlab_ci_runner/register.rb index be4fed0..59c3342 100644 --- a/lib/puppet/functions/gitlab_ci_runner/register.rb +++ b/lib/puppet/functions/gitlab_ci_runner/register.rb @@ -8,7 +8,7 @@ # @param token Registration token. # @param additional_options A hash with all additional configuration options for that runner # @param ca_file An absolute path to a trusted certificate authority file. - # @return [Struct[{ id => Integer[1], token => String[1], }]] Returns a hash with the runner id and authentcation token + # @return [Struct[{ id => Integer[1], token => String[1], }]] Returns a hash with the runner id and authentication token # @example Using it as a replacement for the Bolt 'register_runner' task # puppet apply -e "notice(gitlab_ci_runner::register('https://gitlab.com', 'registration-token'))" # @@ -21,7 +21,11 @@ end def register(url, token, additional_options = {}, ca_file = nil) - PuppetX::Gitlab::Runner.register(url, additional_options.merge('token' => token), ca_file: ca_file) + if token.start_with?('glrt-') + PuppetX::Gitlab::Runner.verify(url, token, ca_file: ca_file) + else + PuppetX::Gitlab::Runner.register(url, additional_options.merge('registration-token' => token), ca_file: ca_file) + end rescue Net::HTTPError => e raise "Gitlab runner failed to register: #{e.message}" end diff --git a/lib/puppet/functions/gitlab_ci_runner/register_to_file.rb b/lib/puppet/functions/gitlab_ci_runner/register_to_file.rb index 8e48a17..c79ed17 100644 --- a/lib/puppet/functions/gitlab_ci_runner/register_to_file.rb +++ b/lib/puppet/functions/gitlab_ci_runner/register_to_file.rb @@ -51,7 +51,13 @@ def register_to_file(url, regtoken, runner_name, additional_options = {}, proxy # will be returned unmodified. regtoken = call_function('unwrap', regtoken) - authtoken = PuppetX::Gitlab::Runner.register(url, additional_options.merge('token' => regtoken), proxy, ca_file)['token'] + # Combine options based on the token + if regtoken.start_with?('glrt-') + PuppetX::Gitlab::Runner.verify(url, regtoken, proxy, ca_file) + authtoken = regtoken + else + authtoken = PuppetX::Gitlab::Runner.register(url, additional_options.merge('registration-token' => regtoken), proxy, ca_file)['token'] + end # If this function is used as a Deferred function the Gitlab Runner config dir # will not exist on the first run, because the package isn't installed yet. diff --git a/lib/puppet/functions/gitlab_ci_runner/unregister.rb b/lib/puppet/functions/gitlab_ci_runner/unregister.rb index 3a85b75..ea789dc 100644 --- a/lib/puppet/functions/gitlab_ci_runner/unregister.rb +++ b/lib/puppet/functions/gitlab_ci_runner/unregister.rb @@ -8,7 +8,7 @@ # @param url The url to your Gitlab instance. Please only provide the host part (e.g https://gitlab.com) # @param token Runners authentication token. # @param ca_file An absolute path to a trusted certificate authority file. - # @return [Struct[{ id => Integer[1], token => String[1], }]] Returns a hash with the runner id and authentcation token + # @return [Struct[{ status => String[1], }]] Returns status # @example Using it as a replacement for the Bolt 'unregister_runner' task # puppet apply -e "notice(gitlab_ci_runner::unregister('https://gitlab.com', 'runner-auth-token'))" # diff --git a/lib/puppet/functions/gitlab_ci_runner/unregister_from_file.rb b/lib/puppet/functions/gitlab_ci_runner/unregister_from_file.rb index d0c9ebe..e807d7c 100644 --- a/lib/puppet/functions/gitlab_ci_runner/unregister_from_file.rb +++ b/lib/puppet/functions/gitlab_ci_runner/unregister_from_file.rb @@ -6,9 +6,10 @@ # This is meant to be used in conjunction with the gitlab_ci_runner::register_to_file function. Puppet::Functions.create_function(:'gitlab_ci_runner::unregister_from_file') do # @param url The url to your Gitlab instance. Please only provide the host part (e.g https://gitlab.com) - # @param runner_name The name of the runner. Use as identifier for the retrived auth token. + # @param runner_name The name of the runner. Use as identifier for the retrieved auth token. # @param proxy HTTP proxy to use when unregistering # @param ca_file An absolute path to a trusted certificate authority file. + # @return [String] Returns status # @example Using it as a Deferred function with a file resource # file { '/etc/gitlab-runner/auth-token-testrunner': # file => absent, diff --git a/lib/puppet_x/gitlab/runner.rb b/lib/puppet_x/gitlab/runner.rb index bd55ef9..a06887c 100644 --- a/lib/puppet_x/gitlab/runner.rb +++ b/lib/puppet_x/gitlab/runner.rb @@ -70,6 +70,12 @@ def self.register(host, options, proxy = nil, ca_file = nil) PuppetX::Gitlab::APIClient.post(url, options, proxy, ca_file) end + def self.verify(host, token, proxy = nil, ca_file = nil) + url = "#{host}/api/v4/runners/verify" + Puppet.info "Verifying gitlab runner with #{host}" + PuppetX::Gitlab::APIClient.post(url, { 'token' => token }, proxy, ca_file) + end + def self.unregister(host, options, proxy = nil, ca_file = nil) url = "#{host}/api/v4/runners" Puppet.info "Unregistering gitlab runner with #{host}" diff --git a/manifests/runner.pp b/manifests/runner.pp index 631724f..5d43b07 100644 --- a/manifests/runner.pp +++ b/manifests/runner.pp @@ -84,12 +84,14 @@ default => $config, } - if $_config['registration-token'] { + if $_config['registration-token'] or $_config['token'] { $register_additional_options = $config .filter |$item| { $item[0] =~ Gitlab_ci_runner::Register_parameters } # Get all items use for the registration process .reduce({}) |$memo, $item| { $memo + { regsubst($item[0], '-', '_', 'G') => $item[1] } } # Ensure all keys use '_' instead of '-' - $deferred_call = Deferred('gitlab_ci_runner::register_to_file', [$_config['url'], $_config['registration-token'], $_config['name'], $register_additional_options, $http_proxy, $ca_file]) + $token = pick($_config['token'], $_config['registration-token']) + + $deferred_call = Deferred('gitlab_ci_runner::register_to_file', [$_config['url'], $token, $_config['name'], $register_additional_options, $http_proxy, $ca_file]) # Remove registration-token and add a 'token' key to the config with a Deferred function to get it. $__config = ($_config - (Array(Gitlab_ci_runner::Register_parameters) + 'registration-token')) + { 'token' => $deferred_call } diff --git a/metadata.json b/metadata.json index 4aa713f..52c7716 100644 --- a/metadata.json +++ b/metadata.json @@ -1,6 +1,6 @@ { "name": "puppet-gitlab_ci_runner", - "version": "5.1.1-rc0", + "version": "5.2.0-rc0", "author": "Vox Pupuli", "summary": "Installation and configuration of Gitlab CI Runner", "license": "Apache-2.0", @@ -64,7 +64,8 @@ { "operatingsystem": "Debian", "operatingsystemrelease": [ - "11" + "11", + "12" ] }, { diff --git a/spec/functions/register_spec.rb b/spec/functions/register_spec.rb index 3609e3b..bd45d96 100644 --- a/spec/functions/register_spec.rb +++ b/spec/functions/register_spec.rb @@ -6,6 +6,7 @@ describe 'gitlab_ci_runner::register' do let(:url) { 'https://gitlab.example.org' } let(:regtoken) { 'registration-token' } + let(:auth_token) { 'glrt-authentication-token' } let(:return_hash) do { 'id' => 1234, @@ -20,17 +21,28 @@ it { is_expected.to run.with_params('https://gitlab.com', 1234).and_raise_error(ArgumentError) } it { is_expected.to run.with_params('https://gitlab.com', 'registration-token', project: 1234).and_raise_error(ArgumentError) } - it "calls 'PuppetX::Gitlab::Runner.register'" do - allow(PuppetX::Gitlab::Runner).to receive(:register).with(url, { 'token' => regtoken }, ca_file: nil).and_return(return_hash) + context 'with registration token' do + it "calls 'PuppetX::Gitlab::Runner.register'" do + allow(PuppetX::Gitlab::Runner).to receive(:register).with(url, { 'registration-token' => regtoken }, ca_file: nil).and_return(return_hash) - is_expected.to run.with_params(url, regtoken).and_return(return_hash) - expect(PuppetX::Gitlab::Runner).to have_received(:register) + is_expected.to run.with_params(url, regtoken).and_return(return_hash) + expect(PuppetX::Gitlab::Runner).to have_received(:register) + end + + it "passes additional args to 'PuppetX::Gitlab::Runner.register'" do + allow(PuppetX::Gitlab::Runner).to receive(:register).with(url, { 'registration-token' => regtoken, 'active' => false }, ca_file: nil).and_return(return_hash) + + is_expected.to run.with_params(url, regtoken, 'active' => false).and_return(return_hash) + expect(PuppetX::Gitlab::Runner).to have_received(:register) + end end - it "passes additional args to 'PuppetX::Gitlab::Runner.register'" do - allow(PuppetX::Gitlab::Runner).to receive(:register).with(url, { 'token' => regtoken, 'active' => false }, ca_file: nil).and_return(return_hash) + context 'with authentication token' do + it "calls 'PuppetX::Gitlab::Runner.verify'" do + allow(PuppetX::Gitlab::Runner).to receive(:verify).with(url, auth_token, ca_file: nil).and_return(return_hash) - is_expected.to run.with_params(url, regtoken, 'active' => false).and_return(return_hash) - expect(PuppetX::Gitlab::Runner).to have_received(:register) + is_expected.to run.with_params(url, auth_token).and_return(return_hash) + expect(PuppetX::Gitlab::Runner).to have_received(:verify) + end end end diff --git a/spec/functions/register_to_file_spec.rb b/spec/functions/register_to_file_spec.rb index eb20b18..d86085c 100644 --- a/spec/functions/register_to_file_spec.rb +++ b/spec/functions/register_to_file_spec.rb @@ -5,7 +5,12 @@ describe 'gitlab_ci_runner::register_to_file' do let(:url) { 'https://gitlab.example.org' } - let(:regtoken) { 'registration-token' } + let(:token) do + { + reg_token: 'registration-token', + auth_token: 'glrt-authentication-token' + } + end let(:runner_name) { 'testrunner' } let(:filename) { "/etc/gitlab-runner/auth-token-#{runner_name}" } let(:return_hash) do @@ -30,42 +35,81 @@ allow(File).to receive(:read).with(filename).and_return(return_hash['token']) end - it { is_expected.to run.with_params(url, regtoken, runner_name).and_return(return_hash['token']) } + it { is_expected.to run.with_params(url, token[:reg_token], runner_name).and_return(return_hash['token']) } + it { is_expected.to run.with_params(url, token[:auth_token], runner_name).and_return(return_hash['token']) } end - context "retrieves from Gitlab and writes auth token to file if it doesn't exist" do + context 'retrieves from Gitlab and writes auth token to file if it doesn\'t exist' do before do - allow(PuppetX::Gitlab::Runner).to receive(:register).with(url, { 'token' => regtoken }, nil, nil).and_return(return_hash) allow(File).to receive(:exist?).and_call_original allow(File).to receive(:exist?).with(File.dirname(filename)).and_return(true) - allow(File).to receive(:write).with(filename, return_hash['token']) allow(File).to receive(:chmod).with(0o400, filename) end - it { is_expected.to run.with_params(url, regtoken, runner_name).and_return(return_hash['token']) } - - context 'with existing file ca_file option' do + context 'with registration token' do before do - allow(PuppetX::Gitlab::Runner).to receive(:register).with(url, { 'token' => regtoken }, nil, '/tmp').and_return(return_hash) + allow(PuppetX::Gitlab::Runner).to receive(:register).with(url, { 'registration-token' => token[:reg_token] }, nil, nil).and_return(return_hash) + allow(File).to receive(:write).with(filename, return_hash['token']) end - it { is_expected.to run.with_params(url, regtoken, runner_name, {}, nil, '/tmp').and_return(return_hash['token']) } - end + it { is_expected.to run.with_params(url, token[:reg_token], runner_name).and_return(return_hash['token']) } - context 'with non existent ca_file option' do - before do - allow(PuppetX::Gitlab::Runner).to receive(:register).with(url, { 'token' => regtoken }, nil, '/path/to/ca_file').and_return(return_hash) + context 'with existing file ca_file option' do + before do + allow(PuppetX::Gitlab::Runner).to receive(:register).with(url, { 'registration-token' => token[:reg_token] }, nil, '/tmp').and_return(return_hash) + end + + it { is_expected.to run.with_params(url, token[:reg_token], runner_name, {}, nil, '/tmp').and_return(return_hash['token']) } end - it { is_expected.to run.with_params(url, regtoken, runner_name, {}, nil, '/path/to/ca_file').and_return('Specified CA file doesn\'t exist, not attempting to create authtoken') } + context 'with non existent ca_file option' do + before do + allow(PuppetX::Gitlab::Runner).to receive(:register).with(url, { 'registration-token' => token[:reg_token] }, nil, '/path/to/ca_file').and_return(return_hash) + end + + it { is_expected.to run.with_params(url, token[:reg_token], runner_name, {}, nil, '/path/to/ca_file').and_return('Specified CA file doesn\'t exist, not attempting to create authtoken') } + end + + context 'with sensitive token value' do + before do + allow(PuppetX::Gitlab::Runner).to receive(:register).with(url, { 'registration-token' => token[:reg_token] }, nil, '/tmp').and_return(return_hash) + end + + it { is_expected.to run.with_params(url, sensitive(token[:reg_token]), runner_name, {}, nil, '/tmp').and_return(return_hash['token']) } + end end - context 'with sensitive token value' do + context 'with authentication token' do before do - allow(PuppetX::Gitlab::Runner).to receive(:register).with(url, { 'token' => regtoken }, nil, '/tmp').and_return(return_hash) + allow(PuppetX::Gitlab::Runner).to receive(:verify).with(url, token[:auth_token], nil, nil).and_return(return_hash.merge('token' => token[:auth_token])) + allow(File).to receive(:write).with(filename, token[:auth_token]) + end + + it { is_expected.to run.with_params(url, token[:auth_token], runner_name).and_return(token[:auth_token]) } + + context 'with existing file ca_file option' do + before do + allow(PuppetX::Gitlab::Runner).to receive(:verify).with(url, token[:auth_token], nil, '/tmp').and_return(return_hash.merge('token' => token[:auth_token])) + end + + it { is_expected.to run.with_params(url, token[:auth_token], runner_name, {}, nil, '/tmp').and_return(token[:auth_token]) } end - it { is_expected.to run.with_params(url, sensitive(regtoken), runner_name, {}, nil, '/tmp').and_return(return_hash['token']) } + context 'with non existent ca_file option' do + before do + allow(PuppetX::Gitlab::Runner).to receive(:verify).with(url, token[:auth_token], nil, '/path/to/ca_file').and_return(return_hash.merge('token' => token[:auth_token])) + end + + it { is_expected.to run.with_params(url, token[:auth_token], runner_name, {}, nil, '/path/to/ca_file').and_return('Specified CA file doesn\'t exist, not attempting to create authtoken') } + end + + context 'with sensitive token value' do + before do + allow(PuppetX::Gitlab::Runner).to receive(:verify).with(url, token[:auth_token], nil, '/tmp').and_return(return_hash.merge('token' => token[:auth_token])) + end + + it { is_expected.to run.with_params(url, sensitive(token[:auth_token]), runner_name, {}, nil, '/tmp').and_return(token[:auth_token]) } + end end end @@ -75,6 +119,7 @@ allow(Puppet.settings).to receive(:[]).with(:noop).and_return(true) end - it { is_expected.to run.with_params(url, regtoken, runner_name).and_return('DUMMY-NOOP-TOKEN') } + it { is_expected.to run.with_params(url, token[:reg_token], runner_name).and_return('DUMMY-NOOP-TOKEN') } + it { is_expected.to run.with_params(url, token[:auth_token], runner_name).and_return('DUMMY-NOOP-TOKEN') } end end diff --git a/spec/unit/puppet_x/gitlab/runner_spec.rb b/spec/unit/puppet_x/gitlab/runner_spec.rb index 0fecb62..a75f9c4 100644 --- a/spec/unit/puppet_x/gitlab/runner_spec.rb +++ b/spec/unit/puppet_x/gitlab/runner_spec.rb @@ -94,26 +94,41 @@ module PuppetX::Gitlab before do PuppetX::Gitlab::APIClient. stub(:post). - with('https://gitlab.example.org/api/v4/runners', { token: 'registrationtoken' }, nil, nil). + with('https://gitlab.example.org/api/v4/runners', { 'registration-token' => 'registrationtoken' }, nil, nil). and_return('id' => 1234, 'token' => '1234567890abcd') end - let(:response) { described_class.register('https://gitlab.example.org', { token: 'registrationtoken' }, nil, nil) } + let(:response) { described_class.register('https://gitlab.example.org', { 'registration-token' => 'registrationtoken' }, nil, nil) } it 'returns a token' do expect(response['token']).to eq('1234567890abcd') end end + describe 'self.verify' do + before do + PuppetX::Gitlab::APIClient. + stub(:post). + with('https://gitlab.example.org/api/v4/runners/verify', { 'token' => 'glrt-authentication-token' }, nil, nil). + and_return('id' => 1234, 'token' => 'glrt-authentication-token') + end + + let(:response) { described_class.verify('https://gitlab.example.org', 'glrt-authentication-token', nil, nil) } + + it 'returns the authentication token' do + expect(response['token']).to eq('glrt-authentication-token') + end + end + describe 'self.unregister' do before do PuppetX::Gitlab::APIClient. stub(:delete). - with('https://gitlab.example.org/api/v4/runners', { token: '1234567890abcd' }, nil, nil). + with('https://gitlab.example.org/api/v4/runners', { 'token' => '1234567890abcd' }, nil, nil). and_return({}) end - let(:response) { described_class.unregister('https://gitlab.example.org', { token: '1234567890abcd' }, nil, nil) } + let(:response) { described_class.unregister('https://gitlab.example.org', { 'token' => '1234567890abcd' }, nil, nil) } it 'returns an empty hash' do expect(response).to eq({}) diff --git a/types/register.pp b/types/register.pp index 09bc9de..4b56839 100644 --- a/types/register.pp +++ b/types/register.pp @@ -1,4 +1,4 @@ -# @summary A struct of all possible additionl options for gitlab_ci_runner::register +# @summary A struct of all possible additional options for gitlab_ci_runner::register type Gitlab_ci_runner::Register = Struct[{ Optional[description] => String[1], Optional[info] => Hash[String[1],String[1]],