lib/gitlab/qa/runtime/omnibus_configuration.rb



# frozen_string_literal: true

require 'active_support/core_ext/object/blank'

module Gitlab
  module QA
    module Runtime
      class OmnibusConfiguration
        # @param prefixed_config The configuration to be prefixed to the new configuration
        def initialize(prefixed_config = nil)
          @config = ["# Generated by GitLab QA Omnibus Configurator at #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"]

          return unless prefixed_config

          # remove generation statement if it exists within the prefixed configuration that was passed
          # and insert the rest AFTER the very first generation statement
          config_to_insert = prefixed_config.to_s
          generation_regexp = /# Generated by GitLab QA Omnibus Configurator.*\n/
          config_to_insert = config_to_insert.gsub(generation_regexp, '') if config_to_insert.match?(generation_regexp)
          @config.insert(1, config_to_insert)
        end

        def to_s
          sanitize!.join("\n")
        end

        def configuration
          raise NotImplementedError
        end

        # Before hook for any additional configuration
        # This would usually be a container that needs to be running
        # @return Any instance of [Gitlab::QA::Component::Base]
        def prepare; end

        # Commands to execute before GitLab boots
        def exec_commands
          []
        end

        # Ensures no duplicate entries and sanitizes configurations
        # @raise RuntimeError if competing configurations exist
        # rubocop:disable Metrics/AbcSize
        def sanitize!
          sanitized = @config.map do |config|
            next config if config.start_with?('#') || config.match(/\w+\(/) # allow for comments and method invocations

            # sometimes "=" is part of a Hash. Only split based on the first "="
            k, v = config.split("=", 2)
            # make sure each config is well-formed
            # e.g., gitlab_rails['packages_enabled'] = true
            #   NOT gitlab_rails['packages_enabled']=true

            v.nil? ? k.strip : "#{k.strip} = #{v.strip.tr('"', "'")}".strip
          end.uniq

          errors = []

          # check for duplicates
          duplicate_keys = []
          duplicates = sanitized.reject do |n|
            key = n.split('=').first
            duplicate_keys << key unless duplicate_keys.include?(key)
          end

          duplicates.each { |duplicate| errors << "Duplicate entry found: `#{duplicate}`" }

          raise "Errors exist within the Omnibus Configuration!\n#{errors.join(',')}" if errors.any?

          @config = sanitized
        end
        # rubocop:enable Metrics/AbcSize

        def <<(config)
          @config << config.strip unless config.strip.empty?
        end
      end
    end
  end
end