diff options
Diffstat (limited to 'app/services/google_cloud')
3 files changed, 92 insertions, 1 deletions
diff --git a/app/services/google_cloud/create_cloudsql_instance_service.rb b/app/services/google_cloud/create_cloudsql_instance_service.rb index f7fca277c52..8d040c6c908 100644 --- a/app/services/google_cloud/create_cloudsql_instance_service.rb +++ b/app/services/google_cloud/create_cloudsql_instance_service.rb @@ -11,7 +11,7 @@ module GoogleCloud trigger_instance_setup_worker success rescue Google::Apis::Error => err - error(err.to_json) + error(err.message) end private diff --git a/app/services/google_cloud/enable_cloudsql_service.rb b/app/services/google_cloud/enable_cloudsql_service.rb index a466b2f3696..e4a411d0fab 100644 --- a/app/services/google_cloud/enable_cloudsql_service.rb +++ b/app/services/google_cloud/enable_cloudsql_service.rb @@ -12,6 +12,8 @@ module GoogleCloud end success({ gcp_project_ids: unique_gcp_project_ids }) + rescue Google::Apis::Error => err + error(err.message) end private diff --git a/app/services/google_cloud/fetch_google_ip_list_service.rb b/app/services/google_cloud/fetch_google_ip_list_service.rb new file mode 100644 index 00000000000..f7739971603 --- /dev/null +++ b/app/services/google_cloud/fetch_google_ip_list_service.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +module GoogleCloud + class FetchGoogleIpListService + include BaseServiceUtility + + GOOGLE_IP_RANGES_URL = 'https://www.gstatic.com/ipranges/cloud.json' + RESPONSE_BODY_LIMIT = 1.megabyte + EXPECTED_CONTENT_TYPE = 'application/json' + + IpListNotRetrievedError = Class.new(StandardError) + + def execute + # Prevent too many workers from hitting the same HTTP endpoint + if ::Gitlab::ApplicationRateLimiter.throttled?(:fetch_google_ip_list, scope: nil) + return error("#{self.class} was rate limited") + end + + subnets = fetch_and_update_cache! + + Gitlab::AppJsonLogger.info(class: self.class.name, + message: 'Successfully retrieved Google IP list', + subnet_count: subnets.count) + + success({ subnets: subnets }) + rescue IpListNotRetrievedError => err + Gitlab::ErrorTracking.log_exception(err) + error('Google IP list not retrieved') + end + + private + + # Attempts to retrieve and parse the list of IPs from Google. Updates + # the internal cache so that the data is accessible. + # + # Returns an array of IPAddr objects consisting of subnets. + def fetch_and_update_cache! + parsed_response = fetch_google_ip_list + + parse_google_prefixes(parsed_response).tap do |subnets| + ::ObjectStorage::CDN::GoogleIpCache.update!(subnets) + end + end + + def fetch_google_ip_list + response = Gitlab::HTTP.get(GOOGLE_IP_RANGES_URL, follow_redirects: false, allow_local_requests: false) + + validate_response!(response) + + response.parsed_response + end + + def validate_response!(response) + raise IpListNotRetrievedError, "response was #{response.code}" unless response.code == 200 + raise IpListNotRetrievedError, "response was nil" unless response.body + + parsed_response = response.parsed_response + + unless response.content_type == EXPECTED_CONTENT_TYPE && parsed_response.is_a?(Hash) + raise IpListNotRetrievedError, "response was not JSON" + end + + if response.body&.bytesize.to_i > RESPONSE_BODY_LIMIT + raise IpListNotRetrievedError, "response was too large: #{response.body.bytesize}" + end + + prefixes = parsed_response['prefixes'] + + raise IpListNotRetrievedError, "JSON was type #{prefixes.class}, expected Array" unless prefixes.is_a?(Array) + raise IpListNotRetrievedError, "#{GOOGLE_IP_RANGES_URL} did not return any IP ranges" if prefixes.empty? + + response.parsed_response + end + + def parse_google_prefixes(parsed_response) + ranges = parsed_response['prefixes'].map do |prefix| + ip_range = prefix['ipv4Prefix'] || prefix['ipv6Prefix'] + + next unless ip_range + + IPAddr.new(ip_range) + end.compact + + raise IpListNotRetrievedError, "#{GOOGLE_IP_RANGES_URL} did not return any IP ranges" if ranges.empty? + + ranges + end + end +end |