1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
# frozen_string_literal: true
module ContainerRegistry
class Event
include Gitlab::Utils::StrongMemoize
ALLOWED_ACTIONS = %w[push delete].freeze
PUSH_ACTION = 'push'
DELETE_ACTION = 'delete'
EVENT_TRACKING_CATEGORY = 'container_registry:notification'
EVENT_PREFIX = 'i_container_registry'
ALLOWED_ACTOR_TYPES = %w[
personal_access_token
build
gitlab_or_ldap
deploy_token
].freeze
TRACKABLE_ACTOR_EVENTS = %w[
push_tag
delete_tag
push_repository
delete_repository
create_repository
].freeze
attr_reader :event
def initialize(event)
@event = event
end
def supported?
action.in?(ALLOWED_ACTIONS)
end
def handle!
update_project_statistics
end
def track!
tracking_action = "#{action}_#{tracked_target}"
if target_repository? && action_push? && !container_repository_exists?
tracking_action = "create_repository"
end
::Gitlab::Tracking.event(EVENT_TRACKING_CATEGORY, tracking_action)
if manifest_delete_event?
::Gitlab::UsageDataCounters::ContainerRegistryEventCounter.count("#{EVENT_PREFIX}_delete_manifest")
else
event = usage_data_event_for(tracking_action)
::Gitlab::UsageDataCounters::HLLRedisCounter.track_event(event, values: originator.id) if event
end
end
private
def tracked_target
target_tag? ? :tag : :repository
end
def target_tag?
# There is no clear indication in the event structure when we delete a top-level manifest
# except existence of "tag" key
event['target'].has_key?('tag')
end
def target_digest?
event['target'].has_key?('digest')
end
def target_repository?
!target_tag? && event['target'].has_key?('repository')
end
def action
event['action']
end
def action_push?
PUSH_ACTION == action
end
def action_delete?
DELETE_ACTION == action
end
def container_repository_exists?
return unless container_registry_path
ContainerRepository.exists_by_path?(container_registry_path)
end
def container_registry_path
strong_memoize(:container_registry_path) do
path = event.dig('target', 'repository')
next unless path
ContainerRegistry::Path.new(path)
end
end
def project
container_registry_path&.repository_project
end
# counter name for unique user tracking (for MAU)
def usage_data_event_for(tracking_action)
return unless originator
return unless TRACKABLE_ACTOR_EVENTS.include?(tracking_action)
"#{EVENT_PREFIX}_#{tracking_action}_#{originator_suffix}"
end
def originator
return unless ALLOWED_ACTOR_TYPES.include?(originator_type)
origin_id = get_origin_id(originator_type)
return unless origin_id
origin_class.find(origin_id)
end
strong_memoize_attr :originator
def originator_suffix
originator.is_a?(DeployToken) ? 'deploy_token' : 'user'
end
def originator_type
event.dig('actor', 'user_type')
end
def origin_class
deploy_token?(originator_type) ? DeployToken : User
end
def get_origin_id(originator_type)
encoded_user_jwt = event.dig('actor', 'user')
return unless encoded_user_jwt
key = deploy_token?(originator_type) ? 'deploy_token_id' : 'user_id'
user_info = decode_user_info(encoded_user_jwt)
user_info&.dig('user_info', key)
end
def decode_user_info(encoded_user_jwt)
registry_key_file = File.read(Gitlab.config.registry.key)
registry_key = OpenSSL::PKey::RSA.new(registry_key_file)
JSONWebToken::RSAToken.decode(encoded_user_jwt, registry_key).first
rescue Errno::ENOENT, JWT::VerificationError, JWT::DecodeError, JWT::ExpiredSignature, JWT::ImmatureSignature
nil
end
def deploy_token?(originator_type)
originator_type == 'deploy_token'
end
def manifest_delete_event?
action_delete? && target_digest?
end
def update_project_statistics
return unless supported?
return unless target_tag? || manifest_delete_event?
return unless project
Rails.cache.delete(project.root_ancestor.container_repositories_size_cache_key)
ProjectCacheWorker.perform_async(project.id, [], [:container_registry_size])
end
end
end
::ContainerRegistry::Event.prepend_mod_with('ContainerRegistry::Event')
|