Commit c497f95a authored by Stan Hu's avatar Stan Hu

Fix S3 object storage failing when endpoint is not specified

This reverts https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53326
since the endpoint created from the pre-signed URL is incorrect. For S3
virtual hostnames (e.g. `https://bucket.us-west-1.amazonaws.com`, the
bucket name should be ignored, so the endpoint should be
`https://us-west-1.amazonaws.com` in this case. This causes the wrong
endpoint to be used (`https://bucket.bucket.us-west-1.amazonaws.com`).

A workaround for AWS S3 users is to pass `endpoint: s3.amazonaws.com` as
a config.
parent 5bb7d95c
---
title: Fix S3 object storage failing when endpoint is not specified
merge_request: 54868
author:
type: fixed
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
module ObjectStorage module ObjectStorage
class Config class Config
include Gitlab::Utils::StrongMemoize
AWS_PROVIDER = 'AWS' AWS_PROVIDER = 'AWS'
AZURE_PROVIDER = 'AzureRM' AZURE_PROVIDER = 'AzureRM'
GOOGLE_PROVIDER = 'Google' GOOGLE_PROVIDER = 'Google'
...@@ -68,36 +66,6 @@ module ObjectStorage ...@@ -68,36 +66,6 @@ module ObjectStorage
def provider def provider
credentials[:provider].to_s credentials[:provider].to_s
end end
# This method converts fog-aws parameters to an endpoint for the
# Workhorse S3 client.
def s3_endpoint
strong_memoize(:s3_endpoint) do
# We could omit this line and let the following code handle this, but
# this will ensure that working configurations that use `endpoint`
# will continue to work.
next credentials[:endpoint] if credentials[:endpoint].present?
generate_s3_endpoint_from_credentials
end
end
def generate_s3_endpoint_from_credentials
# fog-aws has special handling of the host, region, scheme, etc:
# https://github.com/fog/fog-aws/blob/c7a11ba377a76d147861d0e921eb1e245bc11b6c/lib/fog/aws/storage.rb#L440-L449
# Rather than reimplement this, we derive it from a sample GET URL.
url = fog_connection.get_object_url(bucket, "tmp", nil)
uri = ::Addressable::URI.parse(url)
return unless uri&.scheme && uri&.host
endpoint = "#{uri.scheme}://#{uri.host}"
endpoint += ":#{uri.port}" if uri.port
endpoint
rescue ::URI::InvalidComponentError, ::Addressable::URI::InvalidURIError => e
Gitlab::ErrorTracking.track_exception(e)
nil
end
# End AWS-specific options # End AWS-specific options
# Begin Azure-specific options # Begin Azure-specific options
...@@ -123,10 +91,6 @@ module ObjectStorage ...@@ -123,10 +91,6 @@ module ObjectStorage
end end
end end
def fog_connection
@connection ||= ::Fog::Storage.new(credentials)
end
private private
# This returns a Hash of HTTP encryption headers to send along to S3. # This returns a Hash of HTTP encryption headers to send along to S3.
......
...@@ -80,7 +80,7 @@ module ObjectStorage ...@@ -80,7 +80,7 @@ module ObjectStorage
S3Config: { S3Config: {
Bucket: bucket_name, Bucket: bucket_name,
Region: credentials[:region], Region: credentials[:region],
Endpoint: config.s3_endpoint, Endpoint: credentials[:endpoint],
PathStyle: config.use_path_style?, PathStyle: config.use_path_style?,
UseIamProfile: config.use_iam_profile?, UseIamProfile: config.use_iam_profile?,
ServerSideEncryption: config.server_side_encryption, ServerSideEncryption: config.server_side_encryption,
...@@ -229,7 +229,7 @@ module ObjectStorage ...@@ -229,7 +229,7 @@ module ObjectStorage
end end
def connection def connection
config.fog_connection @connection ||= ::Fog::Storage.new(credentials)
end end
end end
end end
# frozen_string_literal: true # frozen_string_literal: true
require 'spec_helper' require 'fast_spec_helper'
require 'rspec-parameterized' require 'rspec-parameterized'
require 'fog/core'
RSpec.describe ObjectStorage::Config do RSpec.describe ObjectStorage::Config do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
...@@ -33,9 +34,7 @@ RSpec.describe ObjectStorage::Config do ...@@ -33,9 +34,7 @@ RSpec.describe ObjectStorage::Config do
} }
end end
subject do subject { described_class.new(raw_config.as_json) }
described_class.new(raw_config.as_json)
end
describe '#load_provider' do describe '#load_provider' do
before do before do
...@@ -46,10 +45,6 @@ RSpec.describe ObjectStorage::Config do ...@@ -46,10 +45,6 @@ RSpec.describe ObjectStorage::Config do
it 'registers AWS as a provider' do it 'registers AWS as a provider' do
expect(Fog.providers.keys).to include(:aws) expect(Fog.providers.keys).to include(:aws)
end end
describe '#fog_connection' do
it { expect(subject.fog_connection).to be_a_kind_of(Fog::AWS::Storage::Real) }
end
end end
context 'with Google' do context 'with Google' do
...@@ -64,10 +59,6 @@ RSpec.describe ObjectStorage::Config do ...@@ -64,10 +59,6 @@ RSpec.describe ObjectStorage::Config do
it 'registers Google as a provider' do it 'registers Google as a provider' do
expect(Fog.providers.keys).to include(:google) expect(Fog.providers.keys).to include(:google)
end end
describe '#fog_connection' do
it { expect(subject.fog_connection).to be_a_kind_of(Fog::Storage::GoogleXML::Real) }
end
end end
context 'with Azure' do context 'with Azure' do
...@@ -82,10 +73,6 @@ RSpec.describe ObjectStorage::Config do ...@@ -82,10 +73,6 @@ RSpec.describe ObjectStorage::Config do
it 'registers AzureRM as a provider' do it 'registers AzureRM as a provider' do
expect(Fog.providers.keys).to include(:azurerm) expect(Fog.providers.keys).to include(:azurerm)
end end
describe '#fog_connection' do
it { expect(subject.fog_connection).to be_a_kind_of(Fog::Storage::AzureRM::Real) }
end
end end
end end
...@@ -183,50 +170,6 @@ RSpec.describe ObjectStorage::Config do ...@@ -183,50 +170,6 @@ RSpec.describe ObjectStorage::Config do
it { expect(subject.provider).to eq('AWS') } it { expect(subject.provider).to eq('AWS') }
it { expect(subject.aws?).to be true } it { expect(subject.aws?).to be true }
it { expect(subject.google?).to be false } it { expect(subject.google?).to be false }
it 'returns the default S3 endpoint' do
subject.load_provider
expect(subject.s3_endpoint).to eq("https://test-bucket.s3.amazonaws.com")
end
describe 'with a custom endpoint' do
let(:endpoint) { 'https://my.example.com' }
before do
credentials[:endpoint] = endpoint
end
it 'returns the custom endpoint' do
subject.load_provider
expect(subject.s3_endpoint).to eq(endpoint)
end
end
context 'with custom S3 host and port' do
where(:host, :port, :scheme, :expected) do
's3.example.com' | 8080 | nil | 'https://test-bucket.s3.example.com:8080'
's3.example.com' | 443 | nil | 'https://test-bucket.s3.example.com'
's3.example.com' | 443 | "https" | 'https://test-bucket.s3.example.com'
's3.example.com' | nil | nil | 'https://test-bucket.s3.example.com'
's3.example.com' | 80 | "http" | 'http://test-bucket.s3.example.com'
's3.example.com' | "bogus" | nil | nil
end
with_them do
before do
credentials[:host] = host
credentials[:port] = port
credentials[:scheme] = scheme
subject.load_provider
end
it 'returns expected host' do
expect(subject.s3_endpoint).to eq(expected)
end
end
end
end end
context 'with Google credentials' do context 'with Google credentials' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment