From 92a63ea6c95d319382d770e82d7f874e379410ab Mon Sep 17 00:00:00 2001 From: Ahmad Sherif Date: Wed, 20 Dec 2023 17:03:55 +0100 Subject: [PATCH] Implement getting extra config by means of an external command Fixes https://gitlab.com/gitlab-org/gitlab/-/issues/435184 --- Gemfile.lock | 4 ++- README.md | 19 ++++++++++++++ gitlab-exporter.gemspec | 1 + lib/gitlab_exporter/cli.rb | 33 +++++++++++++++++++++--- lib/gitlab_exporter/version.rb | 2 +- spec/cli_spec.rb | 47 ++++++++++++++++++++++++++++++++++ spec/fixtures/config.yml | 10 ++++++++ 7 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 spec/fixtures/config.yml diff --git a/Gemfile.lock b/Gemfile.lock index 9c33dca..c6f69f4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,8 +1,9 @@ PATH remote: . specs: - gitlab-exporter (14.0.0) + gitlab-exporter (14.1.0) connection_pool (= 2.2.5) + deep_merge (~> 1.2.2) faraday (~> 1.8.0) pg (= 1.5.3) puma (= 5.6.5) @@ -18,6 +19,7 @@ GEM specs: ast (2.4.1) connection_pool (2.2.5) + deep_merge (1.2.2) diff-lcs (1.5.0) faraday (1.8.0) faraday-em_http (~> 1.0) diff --git a/README.md b/README.md index f9d36be..0541702 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,25 @@ Once running, you can point your browser or curl to the following URLs: * http://localhost:9168/sidekiq * http://localhost:9168/metrics (to get all of the above combined) +If it is undesirable to have secrets (e.g. Redis password or PostgreSQL credentials) in the config file, +then you can specify an external command (with the `--extra-config-command ` flag) that outputs +such credentials in a YAML form (same structure as the config file) and it will be merged with the file configuration. + +For example: + +``` +$ vault kv get -format=yaml secrets/gitlab/gitlab-exporter +probes: + sidekiq: + opts: + redis_url: redis://hunter1@localhost:9765 + database: + opts: + connection_string: postgres://db-admin:hunter1@localhost:6543/main-db + +$ bin/gitlab-exporter web -c config/gitlab-exporter.yml --extra-config-command "vault kv get -format=yaml secrets/gitlab/gitlab-exporter" +``` + ### Database Bloat Metrics Database bloat is measured for indexes (`btree`) and/or tables diff --git a/gitlab-exporter.gemspec b/gitlab-exporter.gemspec index b305926..f9ce883 100644 --- a/gitlab-exporter.gemspec +++ b/gitlab-exporter.gemspec @@ -21,6 +21,7 @@ Gem::Specification.new do |s| s.license = "MIT" s.add_runtime_dependency "connection_pool", "2.2.5" + s.add_runtime_dependency "deep_merge", "~> 1.2.2" s.add_runtime_dependency "faraday", "~> 1.8.0" s.add_runtime_dependency "pg", "1.5.3" s.add_runtime_dependency "puma", "5.6.5" diff --git a/lib/gitlab_exporter/cli.rb b/lib/gitlab_exporter/cli.rb index 9ef6383..1314dd6 100644 --- a/lib/gitlab_exporter/cli.rb +++ b/lib/gitlab_exporter/cli.rb @@ -1,4 +1,6 @@ require "yaml" +require "deep_merge" +require "English" # TODO: Remove this once we're on Ruby 3 # https://gitlab.com/gitlab-org/gitlab/-/issues/393651 @@ -188,6 +190,10 @@ module GitLab opts.on("-c config.yml", "Monitoring config") do |val| @config_file = val end + opts.on("--extra-config-command command", "Shell command that returns YAML config to be merged + with the config file") do |val| + @extra_config_command = val + end end end @@ -195,12 +201,17 @@ module GitLab @options.help end + def merged_config + config_from_file = Utils.deep_symbolize_hash_keys(YAML.safe_load_file(@config_file, aliases: true)) + config_from_command = extra_config_from_command + + config_from_file.deep_merge!(config_from_command) + end + def run validate! - config = Utils.deep_symbolize_hash_keys(YAML.safe_load_file(@config_file, aliases: true)) - - WebExporter.setup(config) + WebExporter.setup(merged_config) WebExporter.run! end @@ -209,6 +220,22 @@ module GitLab def validate! fail InvalidCLICommand.new(help) unless @config_file end + + def run_extra_config_command + puts "Using extra config by running command `#{@extra_config_command}`" + + output = `#{@extra_config_command}` + return output if $CHILD_STATUS == 0 + + puts "ERROR: command `#{@extra_config_command}` returned a non-zero code." + exit(2) + end + + def extra_config_from_command + return {} unless @extra_config_command + + Utils.deep_symbolize_hash_keys(YAML.safe_load(run_extra_config_command)) + end end # Process runner diff --git a/lib/gitlab_exporter/version.rb b/lib/gitlab_exporter/version.rb index 2170ec0..66ec0c2 100644 --- a/lib/gitlab_exporter/version.rb +++ b/lib/gitlab_exporter/version.rb @@ -1,5 +1,5 @@ module GitLab module Exporter - VERSION = "14.0.0".freeze + VERSION = "14.1.0".freeze end end diff --git a/spec/cli_spec.rb b/spec/cli_spec.rb index 79f3558..98af582 100644 --- a/spec/cli_spec.rb +++ b/spec/cli_spec.rb @@ -1,5 +1,6 @@ require "spec_helper" require "gitlab_exporter/cli" +require "optparse" context "With valid pair of repositories" do let(:repos) { GitRepoBuilder.new } @@ -29,3 +30,49 @@ context "With valid pair of repositories" do end end end + +describe GitLab::Exporter::CLI::Server do + context "extra-config-command is passed" do + let(:argv) do + ["-c", "spec/fixtures/config.yml", "--extra-config-command", extra_config_command] + .extend(OptionParser::Arguable) + end + let(:extra_config_command) { "vault kv get top-secrets" } + let(:server_cmd) { described_class.new(argv) } + + before do + allow(server_cmd).to receive(:run_extra_config_command).and_return(<<~YAML + probes: + sidekiq: + methods: + - probe_workers + opts: + redis_url: redis://hunter1@localhost:9765 + database: + opts: + connection_string: postgres://db-admin:hunter1@localhost:6543/main-db + YAML + ) + end + + it "merges the returned YAML from the command with the passed config" do + expect(server_cmd.merged_config).to eq({ + probes: { + sidekiq: { + methods: %w[probe_stats probe_workers], + opts: { + redis_url: "redis://hunter1@localhost:9765" + } + }, + database: { + methods: ["probe_db"], + opts: { + connection_string: + "postgres://db-admin:hunter1@localhost:6543/main-db" + } + } + } + }) + end + end +end diff --git a/spec/fixtures/config.yml b/spec/fixtures/config.yml new file mode 100644 index 0000000..0537681 --- /dev/null +++ b/spec/fixtures/config.yml @@ -0,0 +1,10 @@ +probes: + sidekiq: + methods: + - probe_stats + + database: + methods: + - probe_db + opts: + connection_string: to-be-overridden -- GitLab