Skip to content

Commit c5b4ebe

Browse files
Robert Marshallbalasankarc
andcommitted
Merge branch 'use-google-storage-gem-instead-of-gcloud' into 'master'
Use google-cloud-storage gem for uploading packages to GCS Closes #8088 See merge request https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/7052 Merged-by: Robert Marshall <[email protected]> Approved-by: Ryan Egesdahl <[email protected]> Approved-by: Robert Marshall <[email protected]> Reviewed-by: Robert Marshall <[email protected]> Reviewed-by: Ryan Egesdahl <[email protected]> Co-authored-by: Balasankar "Balu" C <[email protected]>
2 parents 23db6e1 + a263339 commit c5b4ebe

File tree

5 files changed

+109
-87
lines changed

5 files changed

+109
-87
lines changed

Gemfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ gem 'gitlab'
4141
gem 'yard'
4242
gem 'toml-rb'
4343
gem 'retriable'
44-
gem "tomlib", "~> 0.6.0"
44+
gem 'tomlib', '~> 0.6.0'
45+
gem 'google-cloud-storage'
4546

4647
group :packagecloud, optional: true do
4748
gem 'package_cloud'

Gemfile.lock

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,10 @@ GEM
192192
irb (>= 1.3.6)
193193
reline (>= 0.3.1)
194194
debug_inspector (1.1.0)
195+
declarative (0.0.20)
195196
diff-lcs (1.3)
197+
digest-crc (0.6.5)
198+
rake (>= 12.0.0, < 14.0.0)
196199
docile (1.4.0)
197200
docker-api (2.0.0)
198201
excon (>= 0.47.0)
@@ -249,6 +252,40 @@ GEM
249252
rubocop-performance (~> 1.14)
250253
rubocop-rails (~> 2.15)
251254
rubocop-rspec (~> 2.12)
255+
google-apis-core (0.11.0)
256+
addressable (~> 2.5, >= 2.5.1)
257+
googleauth (>= 0.16.2, < 2.a)
258+
httpclient (>= 2.8.1, < 3.a)
259+
mini_mime (~> 1.0)
260+
representable (~> 3.0)
261+
retriable (>= 2.0, < 4.a)
262+
rexml
263+
webrick
264+
google-apis-iamcredentials_v1 (0.17.0)
265+
google-apis-core (>= 0.11.0, < 2.a)
266+
google-apis-storage_v1 (0.19.0)
267+
google-apis-core (>= 0.9.0, < 2.a)
268+
google-cloud-core (1.6.0)
269+
google-cloud-env (~> 1.0)
270+
google-cloud-errors (~> 1.0)
271+
google-cloud-env (1.6.0)
272+
faraday (>= 0.17.3, < 3.0)
273+
google-cloud-errors (1.3.1)
274+
google-cloud-storage (1.44.0)
275+
addressable (~> 2.8)
276+
digest-crc (~> 0.4)
277+
google-apis-iamcredentials_v1 (~> 0.1)
278+
google-apis-storage_v1 (~> 0.19.0)
279+
google-cloud-core (~> 1.6)
280+
googleauth (>= 0.16.2, < 2.a)
281+
mini_mime (~> 1.0)
282+
googleauth (1.7.0)
283+
faraday (>= 0.17.3, < 3.a)
284+
jwt (>= 1.4, < 3.0)
285+
memoist (~> 0.16)
286+
multi_json (~> 1.11)
287+
os (>= 0.9, < 2.0)
288+
signet (>= 0.16, < 2.a)
252289
gssapi (1.3.1)
253290
ffi (>= 1.0.1)
254291
gyoku (1.4.0)
@@ -280,6 +317,7 @@ GEM
280317
jmespath (1.6.2)
281318
json (2.6.1)
282319
json_pure (2.3.1)
320+
jwt (2.7.1)
283321
knapsack (1.17.1)
284322
rake
285323
kramdown (2.4.0)
@@ -299,10 +337,12 @@ GEM
299337
logging (2.3.0)
300338
little-plugger (~> 1.1)
301339
multi_json (~> 1.14)
340+
memoist (0.16.2)
302341
method_source (1.0.0)
303342
mime-types (3.3.1)
304343
mime-types-data (~> 3.2015)
305344
mime-types-data (3.2020.0512)
345+
mini_mime (1.1.2)
306346
minitar (0.9)
307347
minitest (5.14.4)
308348
mixlib-archive (1.1.7)
@@ -345,6 +385,7 @@ GEM
345385
wmi-lite (~> 1.0)
346386
omnibus-ctl (0.3.6)
347387
open4 (1.3.4)
388+
os (1.1.4)
348389
package_cloud (0.3.09)
349390
highline (~> 2.0.0)
350391
json_pure (~> 2.3.0)
@@ -381,6 +422,10 @@ GEM
381422
regexp_parser (2.1.1)
382423
reline (0.3.1)
383424
io-console (~> 0.5)
425+
representable (3.2.0)
426+
declarative (< 0.1.0)
427+
trailblazer-option (>= 0.1.1, < 0.2.0)
428+
uber (< 0.2.0)
384429
rest-client (2.1.0)
385430
http-accept (>= 1.7.0, < 2.0)
386431
http-cookie (>= 1.0.2, < 2.0)
@@ -450,6 +495,11 @@ GEM
450495
addressable (>= 2.3.5)
451496
faraday (>= 0.17.3, < 3)
452497
semverse (3.0.0)
498+
signet (0.17.0)
499+
addressable (~> 2.8)
500+
faraday (>= 0.17.5, < 3.a)
501+
jwt (>= 1.5, < 3.0)
502+
multi_json (~> 1.10)
453503
simplecov (0.21.2)
454504
docile (~> 1.1)
455505
simplecov-html (~> 0.11)
@@ -476,6 +526,7 @@ GEM
476526
citrus (~> 3.0, > 3.0)
477527
tomlib (0.6.0)
478528
tomlrb (1.3.0)
529+
trailblazer-option (0.1.2)
479530
train-core (3.9.2)
480531
addressable (~> 2.5)
481532
ffi (!= 1.13.0)
@@ -507,6 +558,7 @@ GEM
507558
tty-screen (~> 0.8)
508559
tzinfo (2.0.4)
509560
concurrent-ruby (~> 1.0)
561+
uber (0.1.0)
510562
unf (0.1.4)
511563
unf_ext
512564
unicode-display_width (1.8.0)
@@ -558,6 +610,7 @@ DEPENDENCIES
558610
gitlab
559611
gitlab-dangerfiles (~> 3.0)
560612
gitlab-styles (~> 9.0)
613+
google-cloud-storage
561614
http
562615
json
563616
knapsack

lib/gitlab/gcloud_helper.rb

Lines changed: 23 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,40 @@
1-
require 'retriable'
2-
require 'open3'
1+
require 'google/cloud/storage'
32
require_relative './build/info.rb'
43

54
class GCloudHelper
6-
GCSSyncError = Class.new(StandardError)
7-
SAFileNotSetError = Class.new(StandardError)
8-
SAActivationError = Class.new(StandardError)
9-
105
class << self
11-
def activate_sa!
12-
raise SAFileNotSetError, 'Service account file not set in environment!' unless sa_file
13-
14-
out, status = Open3.capture2e("gcloud auth activate-service-account --key-file #{sa_file}")
15-
raise SAActivationError, "Service account activation failed! ret=#{status.exitstatus} out=#{out}" unless status.success?
16-
end
17-
18-
def sa_file
19-
Build::Info.gcp_release_bucket_sa_file
20-
end
21-
22-
def pkgs_bucket
23-
Build::Info.gcp_release_bucket
24-
end
25-
26-
def gcs_sync!(dir)
27-
begin
28-
activate_sa!
29-
rescue SAFileNotSetError, SAActivationError => e
30-
return e.message
6+
def upload_packages_and_print_urls(dir)
7+
if sa_file.nil? || !File.exist?(sa_file)
8+
warn "Error finding service account file. Can not upload packages to bucket."
9+
return
3110
end
3211

33-
out = ""
12+
storage = Google::Cloud::Storage.new(credentials: sa_file)
13+
bucket = storage.bucket(pkgs_bucket)
14+
15+
signed_urls = []
16+
dir_path = File.absolute_path(dir)
3417

35-
Retriable.retriable(tries: 4, max_elapsed_time: 900, on: GCSSyncError) do
36-
out, status = Open3.capture2e("gsutil -o GSUtil:parallel_composite_upload_threshold=150M -m rsync -r #{dir} gs://#{pkgs_bucket}")
37-
break if status.success?
18+
puts "Syncing packages to GCS bucket."
19+
Dir.glob("#{dir_path}/*/**").each do |source|
20+
destination = source.delete_prefix("#{dir_path}/")
3821

39-
raise GCSSyncError, "Gsutil rsync failed! ret=#{status.exitstatus} out=#{out}"
22+
puts "\tUploading #{destination}"
23+
bucket.upload_file(source, destination)
24+
signed_urls << bucket.signed_url(destination, version: :v4)
4025
end
4126

42-
out
27+
puts signed_urls
4328
end
4429

45-
def signed_urls(paths)
46-
begin
47-
activate_sa!
48-
rescue SAFileNotSetError, SAActivationError => e
49-
return e.message
50-
end
30+
private
5131

52-
gs_uris = paths.map { |p| "gs://#{pkgs_bucket}/#{p}" }
53-
# 12 hours is the maximum duration allowed for a signed url that
54-
# uses a service account
55-
out, status = Open3.capture2e("gsutil signurl -r us-east1 --use-service-account -d 12h #{gs_uris.join(' ')}")
56-
return "Unable to generate signed URL for #{gs_uris.join(' ')}! ret=#{status.exitstatus} out=#{out}" unless status.success?
32+
def sa_file
33+
Build::Info.gcp_release_bucket_sa_file
34+
end
5735

58-
out
36+
def pkgs_bucket
37+
Build::Info.gcp_release_bucket
5938
end
6039
end
6140
end

lib/gitlab/tasks/build.rake

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,7 @@ namespace :build do
7474
desc "Sync packages to gcp"
7575
task :sync do
7676
Gitlab::Util.section('build:package:sync', collapsed: Build::Check.on_tag?) do
77-
puts '---- Syncing packages to GCP'
78-
puts GCloudHelper.gcs_sync!('pkg/')
79-
paths = Dir.glob('pkg/**/*').select { |f| File.file?(f) }.map { |p| p.gsub(%r[^pkg/], '') }
80-
puts GCloudHelper.signed_urls(paths)
77+
GCloudHelper.upload_packages_and_print_urls('pkg/')
8178
end
8279
end
8380

spec/lib/gitlab/gcloud_helper_spec.rb

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,56 @@
11
require 'spec_helper'
22
require 'gitlab/gcloud_helper'
33

4-
RSpec.describe GCloudHelper do
5-
let(:status_failed) { double("status", success?: false, exitstatus: 1) }
6-
let(:status_success) { double("status", success?: true, exitstatus: 0) }
4+
class StubGoogleCloudBucket
5+
def upload_file(source, destination); end
76

7+
def signed_url(path, version: :v4)
8+
"signed-#{path}"
9+
end
10+
end
11+
12+
RSpec.describe GCloudHelper do
813
before do
914
allow(ENV).to receive(:[]).and_call_original
10-
allow(ENV).to receive(:[]).with('GITLAB_COM_PKGS_SA_FILE').and_return('/path/to/sa')
1115
allow(Build::Check).to receive(:on_tag?).and_return(false)
16+
allow(Google::Cloud::Storage).to receive(:new).and_return(double(bucket: StubGoogleCloudBucket.new))
1217
end
1318

14-
describe '.gcs_sync!' do
15-
it 'exits with error when activation fails' do
16-
expect(Open3).to receive(:capture2e).once.and_return(['sa activation', status_failed])
17-
18-
expect(described_class.gcs_sync!('some-dir/')).to eq('Service account activation failed! ret=1 out=sa activation')
19-
end
20-
21-
context 'with successful activation' do
19+
describe '.upload_packages_and_print_urls' do
20+
context 'when SA file variable not defined' do
2221
before do
23-
allow(Open3).to receive(:capture2e).with('gcloud auth activate-service-account --key-file /path/to/sa').and_return(['sa activation', status_success])
24-
end
25-
26-
it 'raises an error if rsync fails after retries' do
27-
expect(Open3).to receive(:capture2e).with('gsutil -o GSUtil:parallel_composite_upload_threshold=150M -m rsync -r some-dir/ gs://gitlab-com-pkgs-builds').exactly(4).times.and_return(['gcs rsync output', status_failed])
28-
29-
expect { described_class.gcs_sync!('some-dir/') }.to raise_error(GCloudHelper::GCSSyncError)
22+
stub_env_var('GITLAB_COM_PKGS_SA_FILE', '')
3023
end
3124

32-
it 'rsyncs packages to the package bucket' do
33-
allow(Open3).to receive(:capture2e).with('gsutil -o GSUtil:parallel_composite_upload_threshold=150M -m rsync -r some-dir/ gs://gitlab-com-pkgs-builds').and_return(['gcs rsync output', status_success])
25+
it 'prints error message' do
26+
expect(described_class).to receive(:warn).with(/Error finding service account file./)
3427

35-
expect(described_class.gcs_sync!('some-dir/')).to eq('gcs rsync output')
28+
described_class.upload_packages_and_print_urls('pkg/')
3629
end
3730
end
38-
end
3931

40-
describe '.signed_urls' do
41-
it 'exits with error when activation fails' do
42-
expect(Open3).to receive(:capture2e).once.and_return(['sa activation', status_failed])
43-
44-
expect(described_class.signed_urls(%w[pkg/1 pkg/2 pkg/3])).to eq('Service account activation failed! ret=1 out=sa activation')
45-
end
46-
47-
context 'with successful activation' do
32+
context 'when file mentioned in SA variable does not exist' do
4833
before do
49-
allow(Open3).to receive(:capture2e).with('gcloud auth activate-service-account --key-file /path/to/sa').and_return(['sa activation', status_success])
34+
stub_env_var('GITLAB_COM_PKGS_SA_FILE', 'a-dummy-file')
35+
allow(::File).to receive(:exist?).with('a-dummy-file').and_return(false)
5036
end
5137

52-
it 'exits with error when signed URLs fail to generate' do
53-
allow(Open3).to receive(:capture2e).with('gsutil signurl -r us-east1 --use-service-account -d 12h gs://gitlab-com-pkgs-builds/pkg/1 gs://gitlab-com-pkgs-builds/pkg/2 gs://gitlab-com-pkgs-builds/pkg/3').once.and_return(['signurl output', status_failed])
38+
it 'prints error message' do
39+
expect(described_class).to receive(:warn).with(/Error finding service account file./)
5440

55-
expect(described_class.signed_urls(%w[pkg/1 pkg/2 pkg/3])).to eq('Unable to generate signed URL for gs://gitlab-com-pkgs-builds/pkg/1 gs://gitlab-com-pkgs-builds/pkg/2 gs://gitlab-com-pkgs-builds/pkg/3! ret=1 out=signurl output')
41+
described_class.upload_packages_and_print_urls('pkg/')
5642
end
43+
end
5744

58-
it 'generates signed urls' do
59-
allow(Open3).to receive(:capture2e).with('gsutil signurl -r us-east1 --use-service-account -d 12h gs://gitlab-com-pkgs-builds/pkg/1 gs://gitlab-com-pkgs-builds/pkg/2 gs://gitlab-com-pkgs-builds/pkg/3').once.and_return(['signurl output', status_success])
45+
context 'when service account file exists' do
46+
before do
47+
stub_env_var('GITLAB_COM_PKGS_SA_FILE', 'a-dummy-file')
48+
allow(::File).to receive(:exist?).with('a-dummy-file').and_return(true)
49+
allow(::Dir).to receive(:glob).with(/pkg/).and_return(%w[one two three])
50+
end
6051

61-
expect(described_class.signed_urls(%w[pkg/1 pkg/2 pkg/3])).to eq('signurl output')
52+
it 'prints signed URLs' do
53+
expect { described_class.upload_packages_and_print_urls('pkg/') }.to output(/signed-one\nsigned-two\nsigned-three/).to_stdout
6254
end
6355
end
6456
end

0 commit comments

Comments
 (0)