# # Copyright:: Copyright (c) 2017 GitLab Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require_relative '../settings_dsl.rb' module Services ALL_SERVICES = 'all'.freeze ALL_GROUPS = 'all-groups'.freeze SYSTEM_GROUP = 'system'.freeze DEFAULT_GROUP = 'default'.freeze class Config def self.list @services.dup end def self.service(name, **config) @services ||= {} # A service config object always needs a group array @services[name] = { groups: [] }.merge(config) end end class << self # Disables the group of services that were passed as arguments # # Excludes the groups provided in the *except* argument # System services are ignored when disabling unless *include_system* is *true*. # # @example usage # Services.disable_group('redis') # Services.disable_group('monitoring', except: ['redis', 'postgres']) # Services.disable_group(Services::ALL_GROUPS, except: 'redis') # # @param [Array] groups # @param [Array, String] except # @param [Boolean] include_system def disable_group(*groups, except: nil, include_system: false) exceptions = [except].flatten exceptions << SYSTEM_GROUP unless include_system set_service_groups_status(false, *groups, except: exceptions) end # Enables the group of services that were passed as arguments # # Excludes the groups provided in the *except* argument # # @example usage # Services.enable_group('redis') # Services.enable_group('monitoring', except: ['redis', 'postgres']) # Services.enable_group(Services::DEFAULT_GROUP, except: 'redis') # @param [Array] groups # @param [Array, String] except def enable_group(*groups, except: nil) set_service_groups_status(true, *groups, except: except) end # Disables the services that were passed as arguments # # Excludes the services provided in the *except* argument # System services are ignored when disabling unless *include_system* is *true*. # # @example usage # Services.disable('mailroom') # Services.disable(Services::ALL_SERVICES, except: ['redis', 'sentinel']) # # @param [Array] services # @param [Array, String] except # @param [Boolean] include_system def disable(*services, except: nil, include_system: false) # Automatically excludes system services unless `include_system: true` is passed exceptions = [*except] exceptions.concat(system_services) unless include_system set_services_status(false, *services, except: exceptions) end # Enables the services that were passed as arguments # # Excludes the services provided in the *except* argument # # @example usage # Services.enable('mailroom') # Services.enable(Services::ALL_SERVICES, except: ['monitoring']) # # @param [Array] services # @param [Array, String] except def enable(*services, except: nil) set_services_status(true, *services, except: except) end # Sets the enable status on the services that were passed as arguments # # Excludes the service provided in the *except* argument. # System services are ignored when disabling unless *include_system* is *true*. # # @param [Array] services # @param [Boolean] enable # @param [Array, String] except # @param [Boolean] include_system def set_status(*services, enable, except: nil, include_system: false) if enable enable(*services, except: except) else disable(*services, except: except, include_system: include_system) end end # Sets the enable status on the service groups that were passed as arguments # # Excludes the service groups provided in the *except* argument # System services are ignored when disabling unless *include_system* is *true*. # # @param [Array] groups # @param [Boolean] enable # @param [Array, String] except # @param [Boolean] include_system def set_group_status(*groups, enable, except: nil, include_system: false) if enable enable_group(*groups, except: except) else disable_group(*groups, except: except, include_system: include_system) end end # Return a list of system services # # @return [Array] list of services def system_services find_by_group(SYSTEM_GROUP) end # Find services by group # # @param [String] group # @return [Array] list of services def find_by_group(group) service_list.select { |_, metadata| metadata[:groups].include?(group) }.keys end # List known services along with its associated `groups` metadata # # @return [Hash] def service_list @service_list ||= {} end # Add services from cookbooks # # @example usage # Services.add_services('gitlab', Services::BaseServices.list) # # @param [String] cookbook # @param [Hash] services def add_services(cookbook, services) cookbook_services[cookbook] = services service_list.merge!(services) end # Reset stored cookbooks and their related services # # @note This is intended to be used in test environment only! def reset_list! @cookbook_services = nil @service_list = nil end # Return whether a service is enabled or not # # If service status is set via configuration file that takes precedence, otherwise we # use the computed value instead # # @param [String] service # @return [Boolean] whether is enabled or not def enabled?(service) return false unless exist?(service) user_config = Gitlab[service]['enable'] return user_config unless user_config.nil? service_status(service) end # Return whether a specific service exist # # @note In CE distributions, some services may not exist as they are EE only # @return [Boolean] whether service exist or not def exist?(service) !service_list[service].nil? end private def cookbook_services @cookbook_services ||= {} end # Return whether service is enabled or not # # @param [String] service # @return [Boolean] whether enabled or not def service_status(service) Gitlab[:node].read(*service_attribute_path(service), 'enable') end # Set service enabled/disabled status # # @param [String] service # @param [Boolean] enabled def set_service_status(service, enabled) Gitlab[:node].write(:default, *service_attribute_path(service), 'enable', enabled) end # Return internal path to manipulate service attributes # # @param [String] service_name # @return [Array] def service_attribute_path(service_name) service = SettingsDSL::Utils.node_attribute_key(service_name) return ['monitoring', service] if Gitlab[:node]['monitoring']&.attribute?(service) return [service] if Gitlab[:node].attribute?(service) ['gitlab', service] end # Set status for a list of services considering provided exceptions # # @param [Boolean] enable # @param [Array] services # @param [Array, String] except def set_services_status(enable, *services, except: nil) exceptions = [*except] service_list.each do |name, _| # Skip if service is in exceptions list next if exceptions.include?(name) # Skip if service does not *exist?* next unless exist?(name) # Set the service enable config if: # The current service was requested to be set # OR # ALL_SERVICES was requested, so we are setting them all set_service_status(name, enable) if services.include?(name) || services.include?(ALL_SERVICES) end end # Set status for services related with provided groups considering provided exceptions # # @param [Boolean] enable # @param [Array] groups # @param [Array, String] except def set_service_groups_status(enable, *groups, except: nil) exceptions = [*except] service_list.each do |name, metadata| # Skip if service is in exceptions list next if (exceptions & metadata[:groups]).any? # Skip if service does not *exist?* next unless exist?(name) # Find the matching groups among our passed arguments and our current service's groups matching_groups = groups & metadata[:groups] # Set the service enable config if: # The current service has matching groups that were requested to be set # OR # ALL_GROUPS was requested, so we are setting them all set_service_status(name, enable) if matching_groups.any? || groups.include?(ALL_GROUPS) end end end end