Skip to content

Commit 99bec2e

Browse files
committed
Bug 1160929 - add support for storing attachments in s3
1 parent dd7cd13 commit 99bec2e

File tree

11 files changed

+1205
-46
lines changed

11 files changed

+1205
-46
lines changed

Bugzilla/Attachment.pm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,10 @@ sub get_storage_by_name {
908908
require Bugzilla::Attachment::FileSystem;
909909
return Bugzilla::Attachment::FileSystem->new();
910910
}
911+
elsif ($name eq 's3') {
912+
require Bugzilla::Attachment::S3;
913+
return Bugzilla::Attachment::S3->new();
914+
}
911915
else {
912916
return undef;
913917
}

Bugzilla/Attachment/S3.pm

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# This Source Code Form is subject to the terms of the Mozilla Public
2+
# License, v. 2.0. If a copy of the MPL was not distributed with this
3+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
#
5+
# This Source Code Form is "Incompatible With Secondary Licenses", as
6+
# defined by the Mozilla Public License, v. 2.0.
7+
8+
package Bugzilla::Attachment::S3;
9+
use strict;
10+
use warnings;
11+
12+
use Bugzilla::Error;
13+
use Bugzilla::S3;
14+
15+
sub new {
16+
my $s3 = Bugzilla::S3->new({
17+
aws_access_key_id => Bugzilla->params->{aws_access_key_id},
18+
aws_secret_access_key => Bugzilla->params->{aws_secret_access_key},
19+
secure => 1,
20+
});
21+
return bless({
22+
s3 => $s3,
23+
bucket => $s3->bucket(Bugzilla->params->{s3_bucket}),
24+
}, shift);
25+
}
26+
27+
sub store {
28+
my ($self, $attach_id, $data) = @_;
29+
unless ($self->{bucket}->add_key($attach_id, $data)) {
30+
warn "Failed to add attachment $attach_id to S3: " . $self->{bucket}->errstr . "\n";
31+
ThrowCodeError('s3_add_failed', { attach_id => $attach_id, reason => $self->{bucket}->errstr });
32+
}
33+
}
34+
35+
sub retrieve {
36+
my ($self, $attach_id) = @_;
37+
my $response = $self->{bucket}->get_key($attach_id);
38+
if (!$response) {
39+
warn "Failed to retrieve attachment $attach_id from S3: " . $self->{bucket}->errstr . "\n";
40+
ThrowCodeError('s3_get_failed', { attach_id => $attach_id, reason => $self->{bucket}->errstr });
41+
}
42+
return $response->{value};
43+
}
44+
45+
sub remove {
46+
my ($self, $attach_id) = @_;
47+
$self->{bucket}->delete_key($attach_id)
48+
or warn "Failed to remove attachment $attach_id from S3: " . $self->{bucket}->errstr . "\n";
49+
}
50+
51+
sub exists {
52+
my ($self, $attach_id) = @_;
53+
return !!$self->{bucket}->head_key($attach_id);
54+
}
55+
56+
1;

Bugzilla/Config/Attachment.pm

Lines changed: 72 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -38,42 +38,82 @@ use Bugzilla::Config::Common;
3838
our $sortkey = 400;
3939

4040
sub get_param_list {
41-
my $class = shift;
42-
my @param_list = (
43-
{
44-
name => 'allow_attachment_display',
45-
type => 'b',
46-
default => 0
47-
},
41+
my $class = shift;
42+
my @param_list = (
43+
{
44+
name => 'allow_attachment_display',
45+
type => 'b',
46+
default => 0
47+
},
48+
{
49+
name => 'attachment_base',
50+
type => 't',
51+
default => '',
52+
checker => \&check_urlbase
53+
},
54+
{
55+
name => 'allow_attachment_deletion',
56+
type => 'b',
57+
default => 0
58+
},
59+
{
60+
name => 'maxattachmentsize',
61+
type => 't',
62+
default => '1000',
63+
checker => \&check_maxattachmentsize
64+
},
65+
{
66+
name => 'attachment_storage',
67+
type => 's',
68+
choices => ['database', 'filesystem', 's3'],
69+
default => 'database',
70+
checker => \&check_storage
71+
},
72+
{
73+
name => 's3_bucket',
74+
type => 't',
75+
default => '',
76+
},
77+
{
78+
name => 'aws_access_key_id',
79+
type => 't',
80+
default => '',
81+
},
82+
{
83+
name => 'aws_secret_access_key',
84+
type => 't',
85+
default => '',
86+
},
87+
);
88+
return @param_list;
89+
}
4890

49-
{
50-
name => 'attachment_base',
51-
type => 't',
52-
default => '',
53-
checker => \&check_urlbase
54-
},
91+
sub check_params {
92+
my ($class, $params) = @_;
93+
return unless $params->{attachment_storage} eq 's3';
5594

56-
{
57-
name => 'allow_attachment_deletion',
58-
type => 'b',
59-
default => 0
60-
},
95+
if ($params->{s3_bucket} eq ''
96+
|| $params->{aws_access_key_id} eq ''
97+
|| $params->{aws_secret_access_key} eq ''
98+
) {
99+
return "You must set s3_bucket, aws_access_key_id, and aws_secret_access_key when attachment_storage is set to S3";
100+
}
101+
return '';
102+
}
61103

62-
{
63-
name => 'maxattachmentsize',
64-
type => 't',
65-
default => '1000',
66-
checker => \&check_maxattachmentsize
67-
},
104+
sub check_storage {
105+
my ($value, $param) = (@_);
106+
my $check_multi = check_multi($value, $param);
107+
return $check_multi if $check_multi;
68108

69-
{
70-
name => 'attachment_storage',
71-
type => 's',
72-
choices => ['database', 'filesystem'],
73-
default => 'database',
74-
checker => \&check_multi
75-
} );
76-
return @param_list;
109+
if ($value eq 's3') {
110+
return Bugzilla->feature('s3')
111+
? ''
112+
: 'The perl modules required for S3 support are not installed';
113+
}
114+
else {
115+
return '';
116+
}
77117
}
78118

79119
1;

Bugzilla/Install/Requirements.pm

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,20 @@ sub OPTIONAL_MODULES {
332332
feature => ['detect_charset'],
333333
},
334334

335+
# S3 attachments
336+
{
337+
package => 'Class-Accessor-Fast',
338+
module => 'Class::Accessor::Fast',
339+
version => 0,
340+
feature => ['s3'],
341+
},
342+
{
343+
package => 'XML-Simple',
344+
module => 'XML::Simple',
345+
version => 0,
346+
feature => ['s3'],
347+
},
348+
335349
# Inbound Email
336350
{
337351
package => 'Email-MIME-Attachment-Stripper',
@@ -381,7 +395,7 @@ sub OPTIONAL_MODULES {
381395
package => 'URI-Escape',
382396
module => 'URI::Escape',
383397
version => 0,
384-
feature => ['memcached'],
398+
feature => ['memcached', 's3'],
385399
},
386400
{
387401
package => 'Cache-Memcached',

0 commit comments

Comments
 (0)