lib/release_tools/release_managers/client.rb (108 lines of code) (raw):

# frozen_string_literal: true module ReleaseTools module ReleaseManagers class Client include ::SemanticLogger::Loggable SyncError = Class.new(StandardError) UserNotFoundError = Class.new(SyncError) UnauthorizedError = Class.new(SyncError) GITLAB_API_ENDPOINT = 'https://gitlab.com/api/v4' DEV_API_ENDPOINT = 'https://dev.gitlab.org/api/v4' OPS_API_ENDPOINT = 'https://ops.gitlab.net/api/v4' MASTER_ACCESS = 40 attr_reader :sync_errors, :target # Initialize a GitLab API client specific to Release Manager tasks # # target - Target :production, :dev or :ops environment (default: :production) def initialize(target = :production) @sync_errors = [] @target = target case target when :dev @group = 'gitlab/release/managers' @client = Gitlab.client( endpoint: DEV_API_ENDPOINT, private_token: ENV.fetch('RELEASE_BOT_DEV_TOKEN', nil) ) when :ops @group = 'gitlab-org/release/managers' @client = Gitlab.client( endpoint: OPS_API_ENDPOINT, private_token: ENV.fetch('RELEASE_BOT_OPS_TOKEN', nil) ) else @target = :production @group = 'gitlab-org/release/managers' @client = Gitlab.client( endpoint: GITLAB_API_ENDPOINT, private_token: ENV.fetch('RELEASE_BOT_PRODUCTION_TOKEN', nil) ) end end def members client.group_members(group) end def sync_membership(usernames) logger.info('Syncing membership', target: target) usernames.map!(&:downcase) existing = member_usernames to_add = usernames - existing to_remove = existing - usernames to_add.each do |username| track_sync_errors { add_member(username) } end to_remove.each do |username| track_sync_errors { remove_member(username) } end rescue Gitlab::Error::Unauthorized sync_errors << UnauthorizedError.new("Unauthorized") rescue Gitlab::Error::Forbidden sync_errors << UnauthorizedError.new('Insufficient permissions') end def join(username) track_sync_errors do if member_usernames.include?(username) logger.warn('User is already part of the group', username: username) return end add_member(username) end end def leave(username) track_sync_errors do unless member_usernames.include?(username) logger.warn('User is not part of the group', username: username) return end remove_member(username) end end def get_user(username) user = client .users(username: username) .first user || raise(UserNotFoundError, "#{username} not found") end private attr_reader :client, :group def member_usernames members.collect { |member| member.username.downcase } end def add_member(username) user = get_user(username) logger.info('Adding user to group', user: user.username, group: group) client.add_group_member(group, user.id, MASTER_ACCESS) rescue Gitlab::Error::Conflict => ex raise SyncError.new(ex) unless ex.message.include?('Member already exists') rescue Gitlab::Error::BadRequest => ex # Ignore when a new member has greater permissions via group inheritance raise SyncError.new(ex) unless ex.message.include?('should be greater than or equal to') end def remove_member(username) user = get_user(username) logger.info('Removing user from group', user: username, group: group) client.remove_group_member(group, user.id) end def track_sync_errors yield rescue SyncError => ex sync_errors << ex end end end end