From 6701429839de8cc3aeb91b989928c73ccf3b7e21 Mon Sep 17 00:00:00 2001 From: ace-n Date: Fri, 21 Jun 2019 16:45:44 -0700 Subject: [PATCH 01/17] System tests + Node 8 upgrade --- .kokoro/build.sh | 5 + functions/imagemagick/index.js | 163 +++++++--------- functions/imagemagick/package.json | 10 +- functions/imagemagick/test/index.test.js | 234 +++++++++-------------- 4 files changed, 173 insertions(+), 239 deletions(-) diff --git a/.kokoro/build.sh b/.kokoro/build.sh index 8b91dc6512..be5cb71b53 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -44,9 +44,14 @@ export SENDGRID_API_KEY=$(cat $KOKORO_GFILE_DIR/secrets-sendgrid-api-key.txt) # Configure GCF variables export FUNCTIONS_TOPIC=integration-tests-instance export FUNCTIONS_BUCKET=$GCLOUD_PROJECT + +# functions/speech-to-speech export OUTPUT_BUCKET=$FUNCTIONS_BUCKET export SUPPORTED_LANGUAGE_CODES="en,es" +# functions/imagemagick +export BLURRED_BUCKET_NAME=$GCLOUD_PROJECT-functions + # Configure IoT variables export NODEJS_IOT_EC_PUBLIC_KEY=${KOKORO_GFILE_DIR}/ec_public.pem export NODEJS_IOT_RSA_PRIVATE_KEY=${KOKORO_GFILE_DIR}/rsa_private.pem diff --git a/functions/imagemagick/index.js b/functions/imagemagick/index.js index 859e1a550e..12af3c8685 100644 --- a/functions/imagemagick/index.js +++ b/functions/imagemagick/index.js @@ -30,115 +30,94 @@ const {BLURRED_BUCKET_NAME} = process.env; // [START functions_imagemagick_analyze] // Blurs uploaded images that are flagged as Adult or Violence. -exports.blurOffensiveImages = event => { - const object = event.data || event; // Node 6: event.data === Node 8+: event - - // Exit if this is a deletion or a deploy event. - if (object.resourceState === 'not_exists') { - console.log('This is a deletion event.'); - return; - } else if (!object.name) { - console.log('This is a deploy event.'); - return; - } +exports.blurOffensiveImages = async event => { + const object = event; const file = storage.bucket(object.bucket).file(object.name); const filePath = `gs://${object.bucket}/${object.name}`; - // Ignore already-blurred files (to prevent re-invoking this function) - if (file.name.startsWith('blurred-')) { - console.log(`The image ${file.name} is already blurred.`); - return; - } - console.log(`Analyzing ${file.name}.`); - return client - .safeSearchDetection(filePath) - .catch(err => { - console.error(`Failed to analyze ${file.name}.`, err); - return Promise.reject(err); - }) - .then(([result]) => { - const detections = result.safeSearchAnnotation; - - if ( - detections.adult === 'VERY_LIKELY' || - detections.violence === 'VERY_LIKELY' - ) { - console.log( - `The image ${file.name} has been detected as inappropriate.` - ); - return blurImage(file, BLURRED_BUCKET_NAME); - } else { - console.log(`The image ${file.name} has been detected as OK.`); - } - }); + try { + const [result] = await client.safeSearchDetection(filePath); + const detections = result.safeSearchAnnotation; + + if ( + detections.adult === 'VERY_LIKELY' || + detections.violence === 'VERY_LIKELY' + ) { + console.log(`The image ${file.name} has been detected as inappropriate.`); + return blurImage(file, BLURRED_BUCKET_NAME); + } else { + console.log(`The image ${file.name} has been detected as OK.`); + } + } catch (err) { + console.error(`Failed to analyze ${file.name}.`, err); + return Promise.reject(err); + } }; // [END functions_imagemagick_analyze] // [START functions_imagemagick_blur] // Blurs the given file using ImageMagick, and uploads it to another bucket. -function blurImage(file, blurredBucketName) { +const blurImage = async (file, blurredBucketName) => { const tempLocalPath = `/tmp/${path.parse(file.name).base}`; // Download file from bucket. - return file - .download({destination: tempLocalPath}) - .catch(err => { - console.error('Failed to download file.', err); - return Promise.reject(err); - }) - .then(() => { - console.log( - `Image ${file.name} has been downloaded to ${tempLocalPath}.` - ); - - // Blur the image using ImageMagick. - return new Promise((resolve, reject) => { - gm(tempLocalPath) - .blur(0, 16) - .write(tempLocalPath, (err, stdout) => { - if (err) { - console.error('Failed to blur image.', err); - reject(err); - } else { - resolve(stdout); - } - }); - }); - }) - .then(() => { - console.log(`Image ${file.name} has been blurred.`); - - // Upload result to a different bucket, to avoid re-triggering this function. - // You can also re-upload it to the same bucket + tell your Cloud Function to - // ignore files marked as blurred (e.g. those with a "blurred" prefix) - const blurredBucket = storage.bucket(blurredBucketName); - - // Upload the Blurred image back into the bucket. - return blurredBucket - .upload(tempLocalPath, {destination: file.name}) - .catch(err => { - console.error('Failed to upload blurred image.', err); - return Promise.reject(err); - }); - }) - .then(() => { - console.log( - `Blurred image has been uploaded to: gs://${blurredBucketName}/${file.name}` - ); - - // Delete the temporary file. - return new Promise((resolve, reject) => { - fs.unlink(tempLocalPath, err => { + try { + await file.download({destination: tempLocalPath}); + + console.log(`Image ${file.name} has been downloaded to ${tempLocalPath}.`); + } catch (err) { + console.error('Failed to download file.', err); + return Promise.reject(err); + } + + try { + // Blur the image using ImageMagick. + await new Promise((resolve, reject) => { + gm(tempLocalPath) + .blur(0, 16) + .write(tempLocalPath, (err, stdout) => { if (err) { - reject(err); + console.error('Failed to blur image.', err); + return reject(err); } else { - resolve(); + resolve(stdout); } }); - }); }); -} + console.log(`Image ${file.name} has been blurred.`); + } catch (err) { + console.error('Failed to blur image.', err); + return Promise.reject(err); + } + + // Upload result to a different bucket, to avoid re-triggering this function. + // You can also re-upload it to the same bucket + tell your Cloud Function to + // ignore files marked as blurred (e.g. those with a "blurred" prefix) + const blurredBucket = storage.bucket(blurredBucketName); + + // Upload the Blurred image back into the bucket. + try { + await blurredBucket.upload(tempLocalPath, {destination: file.name}); + console.log( + `Blurred image has been uploaded to: gs://${blurredBucketName}/${file.name}` + ); + } catch (err) { + console.error('Failed to upload blurred image.', err); + return Promise.reject(err); + } + + // Delete the temporary file. + return new Promise((resolve, reject) => { + fs.unlink(tempLocalPath, err => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); +}; // [END functions_imagemagick_blur] diff --git a/functions/imagemagick/package.json b/functions/imagemagick/package.json index 616f6dfe79..f4d2779530 100644 --- a/functions/imagemagick/package.json +++ b/functions/imagemagick/package.json @@ -20,13 +20,21 @@ "gm": "^1.23.1" }, "devDependencies": { + "@google-cloud/functions-framework": "^1.1.1", "@google-cloud/nodejs-repo-tools": "^3.3.0", + "child-process-promise": "^2.2.1", "mocha": "^6.0.0", "proxyquire": "^2.1.0", + "request": "^2.88.0", + "requestretry": "^4.0.0", "sinon": "^7.0.0" }, "cloud-repo-tools": { "requiresKeyFile": true, - "requiresProjectId": true + "requiresProjectId": true, + "requiredEnvVars": [ + "FUNCTIONS_BUCKET", + "BLURRED_BUCKET_NAME" + ] } } diff --git a/functions/imagemagick/test/index.test.js b/functions/imagemagick/test/index.test.js index 9d4e7c4501..97d8017300 100644 --- a/functions/imagemagick/test/index.test.js +++ b/functions/imagemagick/test/index.test.js @@ -15,169 +15,111 @@ 'use strict'; -const proxyquire = require('proxyquire').noCallThru(); -const sinon = require('sinon'); const assert = require('assert'); const tools = require('@google-cloud/nodejs-repo-tools'); -const vision = require('@google-cloud/vision').v1p1beta1; - -const bucketName = 'my-bucket'; -const blurredBucketName = 'my-blurred-bucket'; -const defaultFileName = 'image.jpg'; - -process.env.BLURRED_BUCKET_NAME = blurredBucketName; - -let VisionStub = sinon.stub(vision, 'ImageAnnotatorClient'); -VisionStub.returns({ - safeSearchDetection: sinon.stub().returns( - Promise.resolve([ - { - safeSearchAnnotation: { - adult: 'VERY_LIKELY', - violence: 'VERY_LIKELY', - }, - }, - ]) - ), +const execPromise = require('child-process-promise').exec; +const path = require('path'); +const {Storage} = require('@google-cloud/storage'); + +const storage = new Storage(); + +let requestRetry = require('requestretry'); +requestRetry = requestRetry.defaults({ + retryStrategy: requestRetry.RetryStrategies.NetworkError, + method: 'POST', + json: true, + url: 'http://localhost:8080/blurOffensiveImages', }); -function getSample(filename) { - const file = { - getMetadata: sinon.stub().returns(Promise.resolve([{}])), - setMetadata: sinon.stub().returns(Promise.resolve()), - download: sinon.stub().returns(Promise.resolve()), - bucket: bucketName, - name: filename, - }; - const bucket = { - file: sinon.stub().returns(file), - upload: sinon.stub().returns(Promise.resolve()), - }; - file.bucket = bucket; - const storageMock = { - bucket: sinon.stub().returns(bucket), - }; - const StorageMock = sinon.stub().returns(storageMock); +const BUCKET_NAME = process.env.FUNCTIONS_BUCKET; +const {BLURRED_BUCKET_NAME} = process.env; - const gmMock = sinon.stub().returns({ - blur: sinon.stub().returnsThis(), - write: sinon.stub().yields(), - }); - gmMock.subClass = sinon.stub().returnsThis(); +const safeFileName = 'bicycle.jpg'; +const offensiveFileName = 'zombie.jpg'; - const fsMock = { - unlink: sinon.stub().yields(), - }; +const cwd = path.join(__dirname, '..'); + +const blurredBucket = storage.bucket(BLURRED_BUCKET_NAME); - return { - program: proxyquire('../', { - '@google-cloud/storage': {Storage: StorageMock}, - gm: gmMock, - fs: fsMock, - }), - mocks: { - fs: fsMock, - gm: gmMock, - storage: storageMock, - bucket, - file, - }, +describe('functions/imagemagick tests', () => { + const startFF = () => { + return execPromise( + `functions-framework --target=blurOffensiveImages --signature-type=event`, + {timeout: 10000, shell: true, cwd} + ); }; -} -beforeEach(tools.stubConsole); -afterEach(tools.restoreConsole); + const stopFF = async ffProc => { + try { + return await ffProc; + } catch (err) { + // Timeouts always cause errors on Linux, so catch them + if (err.name && err.name === 'ChildProcessError') { + return; + } + + throw err; + } + }; -it('blurOffensiveImages does nothing on delete', async () => { - await getSample(defaultFileName).program.blurOffensiveImages({ - data: {resourceState: 'not_exists'}, - }); - assert.strictEqual(console.log.callCount, 1); - assert.deepStrictEqual(console.log.getCall(0).args, [ - 'This is a deletion event.', - ]); -}); + beforeEach(tools.stubConsole); + afterEach(tools.restoreConsole); -it('blurOffensiveImages does nothing on deploy', async () => { - await getSample(defaultFileName).program.blurOffensiveImages({data: {}}); - assert.strictEqual(console.log.callCount, 1); - assert.deepStrictEqual(console.log.getCall(0).args, [ - 'This is a deploy event.', - ]); -}); + it('blurOffensiveImages detects safe images using Cloud Vision', async () => { + const ffProc = startFF(); -it('blurOffensiveImages blurs unblurred images (Node 6 syntax)', async () => { - const sample = getSample(defaultFileName); - await sample.program.blurOffensiveImages({ - data: {bucket: bucketName, name: defaultFileName}, - }); - assert.strictEqual(console.log.callCount, 5); - assert.deepStrictEqual(console.log.getCall(0).args, [ - `Analyzing ${sample.mocks.file.name}.`, - ]); - assert.deepStrictEqual(console.log.getCall(1).args, [ - `The image ${sample.mocks.file.name} has been detected as inappropriate.`, - ]); - assert.deepStrictEqual(console.log.getCall(2).args, [ - `Image ${sample.mocks.file.name} has been downloaded to /tmp/${sample.mocks.file.name}.`, - ]); - assert.deepStrictEqual(console.log.getCall(3).args, [ - `Image ${sample.mocks.file.name} has been blurred.`, - ]); - assert.deepStrictEqual(console.log.getCall(4).args, [ - `Blurred image has been uploaded to: gs://${blurredBucketName}/${sample.mocks.file.name}`, - ]); -}); + await requestRetry({ + body: { + data: { + bucket: BUCKET_NAME, + name: safeFileName, + }, + }, + }); -it('blurOffensiveImages blurs unblurred images (Node 8 syntax)', async () => { - const sample = getSample(defaultFileName); + const {stdout} = await stopFF(ffProc); - await sample.program.blurOffensiveImages({ - bucket: bucketName, - name: defaultFileName, + assert.ok( + stdout.includes(`The image ${safeFileName} has been detected as OK.`) + ); }); - assert.strictEqual(console.log.callCount, 5); - assert.deepStrictEqual(console.log.getCall(0).args, [ - `Analyzing ${sample.mocks.file.name}.`, - ]); - assert.deepStrictEqual(console.log.getCall(1).args, [ - `The image ${sample.mocks.file.name} has been detected as inappropriate.`, - ]); - assert.deepStrictEqual(console.log.getCall(2).args, [ - `Image ${sample.mocks.file.name} has been downloaded to /tmp/${sample.mocks.file.name}.`, - ]); - assert.deepStrictEqual(console.log.getCall(3).args, [ - `Image ${sample.mocks.file.name} has been blurred.`, - ]); - assert.deepStrictEqual(console.log.getCall(4).args, [ - `Blurred image has been uploaded to: gs://${blurredBucketName}/${sample.mocks.file.name}`, - ]); -}); -it('blurOffensiveImages ignores safe images', async () => { - VisionStub.restore(); - VisionStub = sinon.stub(vision, 'ImageAnnotatorClient'); - VisionStub.returns({ - safeSearchDetection: sinon.stub().returns( - Promise.resolve([ - { - safeSearchAnnotation: { - adult: 'VERY_UNLIKELY', - violence: 'VERY_UNLIKELY', - }, + it('blurOffensiveImages successfully blurs offensive images', async () => { + const ffProc = startFF(); + + await requestRetry({ + body: { + data: { + bucket: BUCKET_NAME, + name: offensiveFileName, }, - ]) - ), + }, + }); + + const {stdout, stderr} = await stopFF(ffProc); + console.info(stdout, stderr); + + assert.ok(stdout.includes(`Image ${offensiveFileName} has been blurred.`)); + assert.ok( + stdout.includes( + `Blurred image has been uploaded to: gs://${BLURRED_BUCKET_NAME}/${offensiveFileName}` + ) + ); + + assert.ok( + storage + .bucket(BLURRED_BUCKET_NAME) + .file(offensiveFileName) + .exists(), + 'File uploaded' + ); }); - const sample = getSample(defaultFileName); - await sample.program.blurOffensiveImages({ - data: {bucket: bucketName, name: defaultFileName}, + + after(async () => { + try { + await blurredBucket.file(offensiveFileName).delete(); + } catch (err) { + console.log('Error deleting uploaded file:', err); + } }); - assert.strictEqual(console.log.callCount, 2); - assert.deepStrictEqual(console.log.getCall(0).args, [ - `Analyzing ${sample.mocks.file.name}.`, - ]); - assert.deepStrictEqual(console.log.getCall(1).args, [ - `The image ${sample.mocks.file.name} has been detected as OK.`, - ]); }); From 927f2147cf442381d9a514a187fba8cc9d218cad Mon Sep 17 00:00:00 2001 From: ace-n Date: Thu, 27 Jun 2019 16:40:24 -0700 Subject: [PATCH 02/17] Fix test failure --- functions/imagemagick/test/index.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/functions/imagemagick/test/index.test.js b/functions/imagemagick/test/index.test.js index 97d8017300..a42c226a1e 100644 --- a/functions/imagemagick/test/index.test.js +++ b/functions/imagemagick/test/index.test.js @@ -55,7 +55,8 @@ describe('functions/imagemagick tests', () => { } catch (err) { // Timeouts always cause errors on Linux, so catch them if (err.name && err.name === 'ChildProcessError') { - return; + const {stdout, stderr} = ffProc; + return {stdout, stderr}; } throw err; From cf24c57ad5d5aea6cd0c44712d3104dbb1e148d6 Mon Sep 17 00:00:00 2001 From: ace-n Date: Fri, 28 Jun 2019 00:39:34 -0700 Subject: [PATCH 03/17] Fix tests, take 2 --- functions/imagemagick/package.json | 2 +- functions/imagemagick/test/index.test.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/functions/imagemagick/package.json b/functions/imagemagick/package.json index f4d2779530..474775650e 100644 --- a/functions/imagemagick/package.json +++ b/functions/imagemagick/package.json @@ -12,7 +12,7 @@ "node": ">=8.0.0" }, "scripts": { - "test": "mocha test/*.test.js --timeout=20000" + "test": "mocha test/*.test.js --timeout=20000 --exit" }, "dependencies": { "@google-cloud/storage": "^2.3.3", diff --git a/functions/imagemagick/test/index.test.js b/functions/imagemagick/test/index.test.js index a42c226a1e..2865e754b5 100644 --- a/functions/imagemagick/test/index.test.js +++ b/functions/imagemagick/test/index.test.js @@ -55,7 +55,7 @@ describe('functions/imagemagick tests', () => { } catch (err) { // Timeouts always cause errors on Linux, so catch them if (err.name && err.name === 'ChildProcessError') { - const {stdout, stderr} = ffProc; + const {stdout, stderr} = ffProc.childProcess; return {stdout, stderr}; } @@ -98,7 +98,6 @@ describe('functions/imagemagick tests', () => { }); const {stdout, stderr} = await stopFF(ffProc); - console.info(stdout, stderr); assert.ok(stdout.includes(`Image ${offensiveFileName} has been blurred.`)); assert.ok( From 0d0ab0bf5a87601ea9e5edbdc2d87e416ead9c87 Mon Sep 17 00:00:00 2001 From: ace-n Date: Fri, 28 Jun 2019 01:07:11 -0700 Subject: [PATCH 04/17] DEBUG commit --- functions/imagemagick/test/index.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/functions/imagemagick/test/index.test.js b/functions/imagemagick/test/index.test.js index 2865e754b5..93eb711ffe 100644 --- a/functions/imagemagick/test/index.test.js +++ b/functions/imagemagick/test/index.test.js @@ -53,6 +53,8 @@ describe('functions/imagemagick tests', () => { try { return await ffProc; } catch (err) { + console.info('DBG ERR', err); + console.info('DBG PROC', ffProc); // Timeouts always cause errors on Linux, so catch them if (err.name && err.name === 'ChildProcessError') { const {stdout, stderr} = ffProc.childProcess; From 99a29b3558cd6d942bd863c9ebaa9e181f255e3a Mon Sep 17 00:00:00 2001 From: ace-n Date: Fri, 28 Jun 2019 10:28:24 -0700 Subject: [PATCH 05/17] Use separate ports for FF instances --- functions/imagemagick/test/index.test.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/functions/imagemagick/test/index.test.js b/functions/imagemagick/test/index.test.js index 93eb711ffe..4616eaf02a 100644 --- a/functions/imagemagick/test/index.test.js +++ b/functions/imagemagick/test/index.test.js @@ -28,7 +28,6 @@ requestRetry = requestRetry.defaults({ retryStrategy: requestRetry.RetryStrategies.NetworkError, method: 'POST', json: true, - url: 'http://localhost:8080/blurOffensiveImages', }); const BUCKET_NAME = process.env.FUNCTIONS_BUCKET; @@ -42,9 +41,9 @@ const cwd = path.join(__dirname, '..'); const blurredBucket = storage.bucket(BLURRED_BUCKET_NAME); describe('functions/imagemagick tests', () => { - const startFF = () => { + const startFF = port => { return execPromise( - `functions-framework --target=blurOffensiveImages --signature-type=event`, + `functions-framework --target=blurOffensiveImages --signature-type=event --port=${port}`, {timeout: 10000, shell: true, cwd} ); }; @@ -53,8 +52,6 @@ describe('functions/imagemagick tests', () => { try { return await ffProc; } catch (err) { - console.info('DBG ERR', err); - console.info('DBG PROC', ffProc); // Timeouts always cause errors on Linux, so catch them if (err.name && err.name === 'ChildProcessError') { const {stdout, stderr} = ffProc.childProcess; @@ -69,9 +66,11 @@ describe('functions/imagemagick tests', () => { afterEach(tools.restoreConsole); it('blurOffensiveImages detects safe images using Cloud Vision', async () => { - const ffProc = startFF(); + const PORT = 8080; + const ffProc = startFF(PORT); await requestRetry({ + url: `https://localhost:${PORT}/blurOffensiveImages`, body: { data: { bucket: BUCKET_NAME, @@ -88,9 +87,11 @@ describe('functions/imagemagick tests', () => { }); it('blurOffensiveImages successfully blurs offensive images', async () => { - const ffProc = startFF(); + const PORT = 8081; + const ffProc = startFF(PORT); await requestRetry({ + url: `https://localhost:${PORT}/blurOffensiveImages`, body: { data: { bucket: BUCKET_NAME, @@ -99,7 +100,7 @@ describe('functions/imagemagick tests', () => { }, }); - const {stdout, stderr} = await stopFF(ffProc); + const {stdout} = await stopFF(ffProc); assert.ok(stdout.includes(`Image ${offensiveFileName} has been blurred.`)); assert.ok( From b8064e709c37003059c69bbceb990de1d3f40fc8 Mon Sep 17 00:00:00 2001 From: ace-n Date: Fri, 28 Jun 2019 13:25:56 -0700 Subject: [PATCH 06/17] Add DEBUGs back + increase timeouts --- functions/imagemagick/test/index.test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/functions/imagemagick/test/index.test.js b/functions/imagemagick/test/index.test.js index 4616eaf02a..6678a78e60 100644 --- a/functions/imagemagick/test/index.test.js +++ b/functions/imagemagick/test/index.test.js @@ -44,7 +44,7 @@ describe('functions/imagemagick tests', () => { const startFF = port => { return execPromise( `functions-framework --target=blurOffensiveImages --signature-type=event --port=${port}`, - {timeout: 10000, shell: true, cwd} + {timeout: 15000, shell: true, cwd} ); }; @@ -52,6 +52,9 @@ describe('functions/imagemagick tests', () => { try { return await ffProc; } catch (err) { + console.info('DBG ERR', err); + console.info('DBG PROC', ffProc); + // Timeouts always cause errors on Linux, so catch them if (err.name && err.name === 'ChildProcessError') { const {stdout, stderr} = ffProc.childProcess; From c70e2babf7be49dfdbbbe60b9293196d182dcd89 Mon Sep 17 00:00:00 2001 From: ace-n Date: Fri, 28 Jun 2019 13:45:43 -0700 Subject: [PATCH 07/17] https -> http --- functions/imagemagick/test/index.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/functions/imagemagick/test/index.test.js b/functions/imagemagick/test/index.test.js index 6678a78e60..582cb045d3 100644 --- a/functions/imagemagick/test/index.test.js +++ b/functions/imagemagick/test/index.test.js @@ -73,7 +73,7 @@ describe('functions/imagemagick tests', () => { const ffProc = startFF(PORT); await requestRetry({ - url: `https://localhost:${PORT}/blurOffensiveImages`, + url: `http://localhost:${PORT}/blurOffensiveImages`, body: { data: { bucket: BUCKET_NAME, @@ -94,7 +94,7 @@ describe('functions/imagemagick tests', () => { const ffProc = startFF(PORT); await requestRetry({ - url: `https://localhost:${PORT}/blurOffensiveImages`, + url: `http://localhost:${PORT}/blurOffensiveImages`, body: { data: { bucket: BUCKET_NAME, From 636ded9107b4865c0dc47776f3569c1dfed822d5 Mon Sep 17 00:00:00 2001 From: ace-n Date: Tue, 2 Jul 2019 11:56:40 -0700 Subject: [PATCH 08/17] Fix stdout bug --- functions/imagemagick/test/index.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/imagemagick/test/index.test.js b/functions/imagemagick/test/index.test.js index 582cb045d3..b8c509370e 100644 --- a/functions/imagemagick/test/index.test.js +++ b/functions/imagemagick/test/index.test.js @@ -57,7 +57,7 @@ describe('functions/imagemagick tests', () => { // Timeouts always cause errors on Linux, so catch them if (err.name && err.name === 'ChildProcessError') { - const {stdout, stderr} = ffProc.childProcess; + const {stdout, stderr} = err; return {stdout, stderr}; } From cc66c33525de8d53322d1fe5316f1db6fb67bc57 Mon Sep 17 00:00:00 2001 From: ace-n Date: Tue, 2 Jul 2019 16:49:54 -0700 Subject: [PATCH 09/17] Another DEBUG commit --- functions/imagemagick/test/index.test.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/functions/imagemagick/test/index.test.js b/functions/imagemagick/test/index.test.js index b8c509370e..e794f96130 100644 --- a/functions/imagemagick/test/index.test.js +++ b/functions/imagemagick/test/index.test.js @@ -58,6 +58,12 @@ describe('functions/imagemagick tests', () => { // Timeouts always cause errors on Linux, so catch them if (err.name && err.name === 'ChildProcessError') { const {stdout, stderr} = err; + + console.log('DBG2 Keys', Object.getOwnPropertyNames(err)); + + console.log('DBG2 S/O', stdout); + console.log('DBG2 T/O', typeof stdout); + return {stdout, stderr}; } From 65b8fa23a2fccaf3c9a57a0b7ab1fa369800e32a Mon Sep 17 00:00:00 2001 From: ace-n Date: Tue, 2 Jul 2019 17:09:46 -0700 Subject: [PATCH 10/17] Update client library version --- functions/imagemagick/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/imagemagick/package.json b/functions/imagemagick/package.json index 474775650e..e134f2387a 100644 --- a/functions/imagemagick/package.json +++ b/functions/imagemagick/package.json @@ -16,7 +16,7 @@ }, "dependencies": { "@google-cloud/storage": "^2.3.3", - "@google-cloud/vision": "^0.25.0", + "@google-cloud/vision": "^1.1.3", "gm": "^1.23.1" }, "devDependencies": { From be6893c6b614b053e5c2bcb4b30ee6b39b9d4d57 Mon Sep 17 00:00:00 2001 From: ace-n Date: Wed, 10 Jul 2019 17:29:37 -0700 Subject: [PATCH 11/17] DEBUG: limit retry delay + add stderr log --- functions/imagemagick/test/index.test.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/functions/imagemagick/test/index.test.js b/functions/imagemagick/test/index.test.js index e794f96130..05c87c6f56 100644 --- a/functions/imagemagick/test/index.test.js +++ b/functions/imagemagick/test/index.test.js @@ -28,6 +28,7 @@ requestRetry = requestRetry.defaults({ retryStrategy: requestRetry.RetryStrategies.NetworkError, method: 'POST', json: true, + retryDelay: 1000, }); const BUCKET_NAME = process.env.FUNCTIONS_BUCKET; @@ -88,7 +89,9 @@ describe('functions/imagemagick tests', () => { }, }); - const {stdout} = await stopFF(ffProc); + const {stdout, stderr} = await stopFF(ffProc); + + console.error('STDERR A', stderr); assert.ok( stdout.includes(`The image ${safeFileName} has been detected as OK.`) @@ -109,7 +112,9 @@ describe('functions/imagemagick tests', () => { }, }); - const {stdout} = await stopFF(ffProc); + const {stdout, stderr} = await stopFF(ffProc); + + console.error('STDERR B', stderr); assert.ok(stdout.includes(`Image ${offensiveFileName} has been blurred.`)); assert.ok( From b48a34aaf1ab5e8c065068120a38d280c4ac3478 Mon Sep 17 00:00:00 2001 From: ace-n Date: Wed, 10 Jul 2019 17:58:11 -0700 Subject: [PATCH 12/17] Fix safeSearchAnnotation being null on safe images --- functions/imagemagick/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/imagemagick/index.js b/functions/imagemagick/index.js index 12af3c8685..0fe572d5b3 100644 --- a/functions/imagemagick/index.js +++ b/functions/imagemagick/index.js @@ -40,7 +40,7 @@ exports.blurOffensiveImages = async event => { try { const [result] = await client.safeSearchDetection(filePath); - const detections = result.safeSearchAnnotation; + const detections = result.safeSearchAnnotation || {}; if ( detections.adult === 'VERY_LIKELY' || From 729fc25902bfb7bb13b826dd289708f29c7292d4 Mon Sep 17 00:00:00 2001 From: ace-n Date: Mon, 15 Jul 2019 12:07:35 -0700 Subject: [PATCH 13/17] Remove DEBUG statements --- functions/imagemagick/test/index.test.js | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/functions/imagemagick/test/index.test.js b/functions/imagemagick/test/index.test.js index 05c87c6f56..9619acef62 100644 --- a/functions/imagemagick/test/index.test.js +++ b/functions/imagemagick/test/index.test.js @@ -53,18 +53,9 @@ describe('functions/imagemagick tests', () => { try { return await ffProc; } catch (err) { - console.info('DBG ERR', err); - console.info('DBG PROC', ffProc); - // Timeouts always cause errors on Linux, so catch them if (err.name && err.name === 'ChildProcessError') { const {stdout, stderr} = err; - - console.log('DBG2 Keys', Object.getOwnPropertyNames(err)); - - console.log('DBG2 S/O', stdout); - console.log('DBG2 T/O', typeof stdout); - return {stdout, stderr}; } @@ -89,9 +80,7 @@ describe('functions/imagemagick tests', () => { }, }); - const {stdout, stderr} = await stopFF(ffProc); - - console.error('STDERR A', stderr); + const {stdout} = await stopFF(ffProc); assert.ok( stdout.includes(`The image ${safeFileName} has been detected as OK.`) @@ -112,9 +101,7 @@ describe('functions/imagemagick tests', () => { }, }); - const {stdout, stderr} = await stopFF(ffProc); - - console.error('STDERR B', stderr); + const {stdout} = await stopFF(ffProc); assert.ok(stdout.includes(`Image ${offensiveFileName} has been blurred.`)); assert.ok( From 4a5a985d47a8235545144d5670723c704d16d688 Mon Sep 17 00:00:00 2001 From: ace-n Date: Wed, 17 Jul 2019 12:39:48 -0700 Subject: [PATCH 14/17] Rework logs --- functions/imagemagick/index.js | 18 +++++++++--------- functions/imagemagick/test/index.test.js | 8 +++----- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/functions/imagemagick/index.js b/functions/imagemagick/index.js index 0fe572d5b3..a9f0671856 100644 --- a/functions/imagemagick/index.js +++ b/functions/imagemagick/index.js @@ -46,10 +46,10 @@ exports.blurOffensiveImages = async event => { detections.adult === 'VERY_LIKELY' || detections.violence === 'VERY_LIKELY' ) { - console.log(`The image ${file.name} has been detected as inappropriate.`); + console.log(`Detected ${file.name} as inappropriate.`); return blurImage(file, BLURRED_BUCKET_NAME); } else { - console.log(`The image ${file.name} has been detected as OK.`); + console.log(`Detected ${file.name} as OK.`); } } catch (err) { console.error(`Failed to analyze ${file.name}.`, err); @@ -67,9 +67,9 @@ const blurImage = async (file, blurredBucketName) => { try { await file.download({destination: tempLocalPath}); - console.log(`Image ${file.name} has been downloaded to ${tempLocalPath}.`); + console.log(`Downloaded ${file.name} to ${tempLocalPath}.`); } catch (err) { - console.error('Failed to download file.', err); + console.error('File download failed.', err); return Promise.reject(err); } @@ -80,16 +80,16 @@ const blurImage = async (file, blurredBucketName) => { .blur(0, 16) .write(tempLocalPath, (err, stdout) => { if (err) { - console.error('Failed to blur image.', err); + console.error('Image blurring failed.', err); return reject(err); } else { resolve(stdout); } }); }); - console.log(`Image ${file.name} has been blurred.`); + console.log(`Blurred image: ${file.name}`); } catch (err) { - console.error('Failed to blur image.', err); + console.error('Image blurring failed.', err); return Promise.reject(err); } @@ -102,10 +102,10 @@ const blurImage = async (file, blurredBucketName) => { try { await blurredBucket.upload(tempLocalPath, {destination: file.name}); console.log( - `Blurred image has been uploaded to: gs://${blurredBucketName}/${file.name}` + `Uploaded blurred image to: gs://${blurredBucketName}/${file.name}` ); } catch (err) { - console.error('Failed to upload blurred image.', err); + console.error('Unable to upload blurred image.', err); return Promise.reject(err); } diff --git a/functions/imagemagick/test/index.test.js b/functions/imagemagick/test/index.test.js index 9619acef62..b4c93a741e 100644 --- a/functions/imagemagick/test/index.test.js +++ b/functions/imagemagick/test/index.test.js @@ -82,9 +82,7 @@ describe('functions/imagemagick tests', () => { const {stdout} = await stopFF(ffProc); - assert.ok( - stdout.includes(`The image ${safeFileName} has been detected as OK.`) - ); + assert.ok(stdout.includes(`Detected ${safeFileName} as OK.`)); }); it('blurOffensiveImages successfully blurs offensive images', async () => { @@ -103,10 +101,10 @@ describe('functions/imagemagick tests', () => { const {stdout} = await stopFF(ffProc); - assert.ok(stdout.includes(`Image ${offensiveFileName} has been blurred.`)); + assert.ok(stdout.includes(`Blurred image: ${offensiveFileName}`)); assert.ok( stdout.includes( - `Blurred image has been uploaded to: gs://${BLURRED_BUCKET_NAME}/${offensiveFileName}` + `Uploaded blurred image to: gs://${BLURRED_BUCKET_NAME}/${offensiveFileName}` ) ); From 12c8c69fead203d81354c61829ebbf52b1b7a9a1 Mon Sep 17 00:00:00 2001 From: ace-n Date: Thu, 18 Jul 2019 13:10:01 -0700 Subject: [PATCH 15/17] Address comments, pt 2 --- functions/imagemagick/index.js | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/functions/imagemagick/index.js b/functions/imagemagick/index.js index a9f0671856..96cac4666c 100644 --- a/functions/imagemagick/index.js +++ b/functions/imagemagick/index.js @@ -75,21 +75,12 @@ const blurImage = async (file, blurredBucketName) => { try { // Blur the image using ImageMagick. - await new Promise((resolve, reject) => { - gm(tempLocalPath) - .blur(0, 16) - .write(tempLocalPath, (err, stdout) => { - if (err) { - console.error('Image blurring failed.', err); - return reject(err); - } else { - resolve(stdout); - } - }); - }); + await util.promisify( + gm(tempLocalPath).blur(0, 16).write(tempLocalPath) + ); console.log(`Blurred image: ${file.name}`); } catch (err) { - console.error('Image blurring failed.', err); + console.error(`Failed to blur image ${file.name}`, err); return Promise.reject(err); } @@ -99,25 +90,16 @@ const blurImage = async (file, blurredBucketName) => { const blurredBucket = storage.bucket(blurredBucketName); // Upload the Blurred image back into the bucket. + const gcsPath = `gs://${blurredBucketName}/${file.name}`; try { await blurredBucket.upload(tempLocalPath, {destination: file.name}); - console.log( - `Uploaded blurred image to: gs://${blurredBucketName}/${file.name}` - ); + console.log(`Uploaded blurred image to: ${gcsPath}`); } catch (err) { - console.error('Unable to upload blurred image.', err); + console.error(`Unable to upload blurred image to ${gcsPath}:`, err); return Promise.reject(err); } // Delete the temporary file. - return new Promise((resolve, reject) => { - fs.unlink(tempLocalPath, err => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); + return util.promisify(fs.unlink(tempLocalPath)); }; // [END functions_imagemagick_blur] From 3b000f39533bcdda9101a93d41236a131a0515e2 Mon Sep 17 00:00:00 2001 From: ace-n Date: Mon, 22 Jul 2019 23:41:55 -0700 Subject: [PATCH 16/17] Fix lint typo --- functions/imagemagick/index.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/functions/imagemagick/index.js b/functions/imagemagick/index.js index 96cac4666c..79790e82aa 100644 --- a/functions/imagemagick/index.js +++ b/functions/imagemagick/index.js @@ -18,6 +18,7 @@ // [START functions_imagemagick_setup] const gm = require('gm').subClass({imageMagick: true}); const fs = require('fs'); +const {promisify} = require('util'); const path = require('path'); const {Storage} = require('@google-cloud/storage'); const storage = new Storage(); @@ -75,8 +76,10 @@ const blurImage = async (file, blurredBucketName) => { try { // Blur the image using ImageMagick. - await util.promisify( - gm(tempLocalPath).blur(0, 16).write(tempLocalPath) + await promisify( + gm(tempLocalPath) + .blur(0, 16) + .write(tempLocalPath) ); console.log(`Blurred image: ${file.name}`); } catch (err) { @@ -100,6 +103,6 @@ const blurImage = async (file, blurredBucketName) => { } // Delete the temporary file. - return util.promisify(fs.unlink(tempLocalPath)); + return promisify(fs.unlink(tempLocalPath)); }; // [END functions_imagemagick_blur] From be2e537cd3579df83e3759bb9012a62545dbc9b9 Mon Sep 17 00:00:00 2001 From: ace-n Date: Wed, 24 Jul 2019 15:26:30 -0700 Subject: [PATCH 17/17] Remove util.promisify, as gm doesn't support it --- functions/imagemagick/index.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/functions/imagemagick/index.js b/functions/imagemagick/index.js index 79790e82aa..4a64e62667 100644 --- a/functions/imagemagick/index.js +++ b/functions/imagemagick/index.js @@ -74,18 +74,19 @@ const blurImage = async (file, blurredBucketName) => { return Promise.reject(err); } - try { - // Blur the image using ImageMagick. - await promisify( - gm(tempLocalPath) - .blur(0, 16) - .write(tempLocalPath) - ); - console.log(`Blurred image: ${file.name}`); - } catch (err) { - console.error(`Failed to blur image ${file.name}`, err); - return Promise.reject(err); - } + await new Promise((resolve, reject) => { + gm(tempLocalPath) + .blur(0, 16) + .write(tempLocalPath, (err, stdout) => { + if (err) { + console.error('Failed to blur image.', err); + reject(err); + } else { + console.log(`Blurred image: ${file.name}`); + resolve(stdout); + } + }); + }); // Upload result to a different bucket, to avoid re-triggering this function. // You can also re-upload it to the same bucket + tell your Cloud Function to