Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion src/static/aws/s3.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import { generateNewImageName } from '../utils';
const mockSend = jest.fn();

jest.mock('@aws-sdk/client-s3', () => ({
S3Client: jest.fn().mockImplementation(() => ({
S3Client: jest.fn().mockImplementation((config) => ({
send: mockSend,
config,
})),
PutObjectCommand: jest.fn().mockImplementation((input) => ({ input, type: 'put' })),
GetObjectCommand: jest.fn().mockImplementation((input) => ({ input, type: 'get' })),
Expand Down Expand Up @@ -232,4 +233,34 @@ describe('AWSS3Service', () => {
expect(loggerSpy).toHaveBeenCalledWith('Failed to delete file at AWS S3 for image image.png:', error);
});
});

// S3-compatible storage — MinIO, Ceph, Garage — is normally reached
// path-style (host/bucket/key); AWS itself uses virtual-host style
// (bucket.host/key) and the SDK assumes that. Without a way to switch, a
// self-hosted deployment cannot point VRT at its own storage, and neither
// can anyone trying to reproduce a production setup locally.
describe('S3-compatible storage', () => {
const originalPathStyle = process.env.AWS_S3_FORCE_PATH_STYLE;

afterEach(() => {
process.env.AWS_S3_FORCE_PATH_STYLE = originalPathStyle;
if (originalPathStyle === undefined) delete process.env.AWS_S3_FORCE_PATH_STYLE;
});

it('addresses the bucket path-style when asked to', () => {
process.env.AWS_S3_FORCE_PATH_STYLE = 'true';

const service = new AWSS3Service();

expect((service as any).s3Client.config).toMatchObject({ forcePathStyle: true });
});

it('leaves the SDK to its own addressing by default', () => {
delete process.env.AWS_S3_FORCE_PATH_STYLE;

const service = new AWSS3Service();

expect((service as any).s3Client.config?.forcePathStyle).toBeFalsy();
});
});
});
14 changes: 12 additions & 2 deletions src/static/aws/s3.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,18 @@ export class AWSS3Service implements Static {
private s3Client: S3Client;

constructor() {
this.s3Client = new S3Client();
this.logger.log('AWS S3 service is being used for file storage.');
// S3-compatible storage — MinIO, Ceph, Garage — is normally addressed
// path-style (host/bucket/key), while AWS uses virtual-host style
// (bucket.host/key) and the SDK assumes that. It has no environment
// variable for the switch, so without this a self-hosted deployment cannot
// point VRT at its own storage, and neither can anyone standing up a
// production-shaped stack locally. The endpoint itself the SDK does read
// from AWS_ENDPOINT_URL.
const forcePathStyle = process.env.AWS_S3_FORCE_PATH_STYLE === 'true';
this.s3Client = new S3Client(forcePathStyle ? { forcePathStyle } : {});
this.logger.log(
`AWS S3 service is being used for file storage${forcePathStyle ? ' (path-style addressing)' : ''}.`
);
}

async saveImage(type: 'screenshot' | 'diff' | 'baseline', imageBuffer: Buffer): Promise<string> {
Expand Down
Loading