-
-
Notifications
You must be signed in to change notification settings - Fork 480
Make accessKey and secretKey optional and add support to use IRSA Iam role #1716
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
0a9223b
0733671
2c72299
bf8b27d
ae6b38b
331781d
8d10813
157f687
5161271
94c9c8a
54b36f8
600374a
350e181
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -5,6 +5,7 @@ import {IncomingMessage} from 'http'; | |||||||||||||||
import * as fse from 'fs-extra'; | ||||||||||||||||
import * as minio from 'minio'; | ||||||||||||||||
import * as stream from 'node:stream'; | ||||||||||||||||
import {IamAwsProvider} from 'minio/dist/main/IamAwsProvider.js'; | ||||||||||||||||
|
||||||||||||||||
// The minio-js v8.0.0 typings are sometimes incorrect. Here are some workarounds. | ||||||||||||||||
interface MinIOClient extends | ||||||||||||||||
|
@@ -45,6 +46,8 @@ type RemoveObjectsResponse = null | undefined | { | |||||||||||||||
* will work with MinIO and other S3-compatible storage. | ||||||||||||||||
*/ | ||||||||||||||||
export class MinIOExternalStorage implements ExternalStorage { | ||||||||||||||||
private _s3: MinIOClient; | ||||||||||||||||
|
||||||||||||||||
// Specify bucket to use, and optionally the max number of keys to request | ||||||||||||||||
// in any call to listObjectVersions (used for testing) | ||||||||||||||||
constructor( | ||||||||||||||||
|
@@ -53,13 +56,17 @@ export class MinIOExternalStorage implements ExternalStorage { | |||||||||||||||
endPoint: string, | ||||||||||||||||
port?: number, | ||||||||||||||||
useSSL?: boolean, | ||||||||||||||||
accessKey: string, | ||||||||||||||||
secretKey: string, | ||||||||||||||||
accessKey?: string, | ||||||||||||||||
secretKey?: string, | ||||||||||||||||
credentialsProvider?: IamAwsProvider, | ||||||||||||||||
region: string | ||||||||||||||||
}, | ||||||||||||||||
private _batchSize?: number, | ||||||||||||||||
private _s3 = new minio.Client(options) as unknown as MinIOClient | ||||||||||||||||
private _batchSize?: number | ||||||||||||||||
) { | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing validation to ensure that either credentials (accessKey/secretKey) or authAutoDetect is provided. The code should validate that at least one authentication method is configured to prevent runtime failures.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||
if (!options.accessKey || !options.secretKey) { | ||||||||||||||||
options.credentialsProvider = new IamAwsProvider({}); | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How does this work? If the id/secret are set, and autodetect is also set, will the id/secret be used? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like if autodetect is set, id/secret will be ignored. Initially, my thought was to not have Let me know your thoughts. |
||||||||||||||||
} | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I imagine an administrator having written a typo in the env variables for either the access key and the secret key. I think they would be confused by having switched implicitly to the IRSA authentication method. Also it seems that it is not the only alternative to exist to authenticate with MinIO, currently there also exists What do you think of something like the following instead:
Also you may indeed instantiate directly a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it probably should be similar to official sdk that just goes through the credential provider chain without having to explicitly specify what method we wanna use. I believe the minio client kind of does that here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. Therefore I think you should probably detect the presence of the env variables that should be set for the IRSA, and if they are missing, then switch back to the classic auth method with credentials. Please note that I am not an authority on this project, I'll let Grist Labs review your work on their side. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a env var |
||||||||||||||||
this._s3 = new minio.Client(options) as unknown as MinIOClient; | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
public async exists(key: string, snapshotId?: string) { | ||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,11 +40,11 @@ export function checkMinIOExternalStorage() { | |
const useSSL = settings.flag('useSsl').read({ | ||
envVar: ['GRIST_DOCS_MINIO_USE_SSL'], | ||
}).getAsBool(); | ||
const accessKey = settings.flag('accessKey').requireString({ | ||
const accessKey = settings.flag('accessKey').readString({ | ||
envVar: ['GRIST_DOCS_MINIO_ACCESS_KEY'], | ||
censor: true, | ||
}); | ||
const secretKey = settings.flag('secretKey').requireString({ | ||
const secretKey = settings.flag('secretKey').readString({ | ||
envVar: ['GRIST_DOCS_MINIO_SECRET_KEY'], | ||
censor: true, | ||
}); | ||
Comment on lines
+43
to
50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since accessKey and secretKey are now optional, there should be validation logic to ensure that when authAutoDetect is false, both accessKey and secretKey are provided. Currently, the function could return undefined values for these required credentials. Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,18 +29,33 @@ describe("MinIOExternalStorage", function () { | |
sandbox.restore(); | ||
}); | ||
|
||
// Extend MinIOExternalStorage to allow injecting a mock client for testing | ||
class TestMinIOExternalStorage extends MinIOExternalStorage { | ||
constructor( | ||
bucket: string, | ||
options: any, | ||
batchSize?: number, | ||
mockClient?: any | ||
) { | ||
super(bucket, options, batchSize); | ||
if (mockClient) { | ||
(this as any)._s3 = mockClient; | ||
|
||
} | ||
} | ||
} | ||
|
||
describe('upload()', function () { | ||
const filename = "some-filename"; | ||
let filestream: fse.ReadStream; | ||
let s3: sinon.SinonStubbedInstance<minio.Client>; | ||
let extStorage: MinIOExternalStorage; | ||
let extStorage: TestMinIOExternalStorage; | ||
|
||
beforeEach(function () { | ||
filestream = new stream.Readable() as any; | ||
sandbox.stub(fse, "lstat").resolves({} as any); | ||
sandbox.stub(fse, "createReadStream").withArgs(filename).returns(filestream as any); | ||
s3 = sandbox.createStubInstance(minio.Client); | ||
extStorage = new MinIOExternalStorage( | ||
extStorage = new TestMinIOExternalStorage( | ||
dummyBucket, | ||
dummyOptions, | ||
undefined, | ||
|
@@ -96,7 +111,7 @@ describe("MinIOExternalStorage", function () { | |
|
||
s3.listObjects.returns(fakeStream); | ||
|
||
const extStorage = new MinIOExternalStorage(dummyBucket, dummyOptions, 42, s3 as any); | ||
const extStorage = new TestMinIOExternalStorage(dummyBucket, dummyOptions, 42, s3 as any); | ||
const result = await extStorage.versions(key); | ||
|
||
assert.deepEqual(result, []); | ||
|
@@ -122,7 +137,7 @@ describe("MinIOExternalStorage", function () { | |
]); | ||
|
||
s3.listObjects.returns(fakeStream); | ||
const extStorage = new MinIOExternalStorage(dummyBucket, dummyOptions, 42, s3 as any); | ||
const extStorage = new TestMinIOExternalStorage(dummyBucket, dummyOptions, 42, s3 as any); | ||
// when | ||
const result = await extStorage.versions(key); | ||
// then | ||
|
@@ -155,7 +170,7 @@ describe("MinIOExternalStorage", function () { | |
let {fakeStream} = makeFakeStream(objectsFromS3); | ||
|
||
s3.listObjects.returns(fakeStream); | ||
const extStorage = new MinIOExternalStorage(dummyBucket, dummyOptions, 42, s3 as any); | ||
const extStorage = new TestMinIOExternalStorage(dummyBucket, dummyOptions, 42, s3 as any); | ||
|
||
// when | ||
const result = await extStorage.versions(key); | ||
|
@@ -193,7 +208,7 @@ describe("MinIOExternalStorage", function () { | |
.returns(fakeStream as any) | ||
.callsFake(() => fakeStream.emit('error', error)); | ||
s3.listObjects.returns(fakeStream); | ||
const extStorage = new MinIOExternalStorage(dummyBucket, dummyOptions, 42, s3 as any); | ||
const extStorage = new TestMinIOExternalStorage(dummyBucket, dummyOptions, 42, s3 as any); | ||
|
||
// when | ||
const result = extStorage.versions(key); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imports should be ordered alphabetically. The ".js" presumably isn't needed? Also if the import could be just from 'minio' that would be more consistent than reaching into the specific package layout (minio/dist/main). But sometimes packages are wonky, so, fine if unavoidable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Not a js/ts expert)
I was not able to import from minio directly, looks like it does not export
IamAwsProvider
.Also, when remove
.js
and just useimport {IamAwsProvider} from 'minio/dist/main/IamAwsProvider;
I get the below error.