Movatterモバイル変換


[0]ホーム

URL:


Upgrade to Pro — share decks privately, control downloads, hide ads and more …
Speaker DeckSpeaker Deck
Speaker Deck

Testable Lambda: Working Effectively with Legac...

Avatar for Takuto Wada Takuto Wada
June 02, 2017

Testable Lambda: Working Effectively with Legacy Lambda

AWS Dev Day Tokyo 2017 Day4 Track1 15:20 - 16:00

#AWSSummit #testlambda

当日のセッション録画:
Testable Lambda|AWS Summit Tokyo 2017 - YouTube
https://www.youtube.com/watch?v=C0zNc4bdWhY

Avatar for Takuto Wada

Takuto Wada

June 02, 2017
Tweet

More Decks by Takuto Wada

See All by Takuto Wada

Other Decks in Programming

See All in Programming

Featured

See All Featured

Transcript

  1. 5FTUBCMF-BNCEB 8PSLJOH&GGFDUJWFMZXJUI-FHBDZ-BNCEB ࿨ా୎ਓ !U@XBEB  +VO !"84%FW%BZ5PLZP

  2. #AWSSummit #testlambda ࡱӨ0,ʢͨͩ͠ɺγϟολʔԻΛ߇͑Ίʹʣ ࢿྉө૾ެ։͋Γ ࣮گ΋େ׻ܴͰ͢

  3. ࿨ా୎ਓ JEUXBEB !U@XBEB HJUIVCUXBEB

  4. 4QFDJBM5IBOLTUP1*95" ߨԋʹࡍͯ͠1*95"༷ʹ͝ڠྗ͍͖ͨͩ·ͨ͠

  5. ۀքͰͷཱͪҐஔಠΓา͖͢Δελϯυ ΑΖ͓͘͠ئ͍͠·͢

  6. Agenda ݱঢ়֬ೝ ςελϏϦςΟΛ͚͋͜͡Δ αΠζͱϐϥϛουͱϧʔϓ ͔ͦ͜Βઌ΁

  7. l"84-BNCEBͷࣗಈςετʹؔ͢ ΔϕετϓϥΫςΟε͸·ͩͳ͍z

  8. ࠓ೔ͷ͓୊-BNCEBͷެࣜνϡʔτϦΞϧ IUUQEPDTBXTBNB[PODPNKB@KQMBNCEBMBUFTUEHXJUITFYBNQMFIUNM

  9. // dependencies var async = require('async'); var AWS = require('aws-sdk');

    var gm = require('gm') .subClass({ imageMagick: true }); // Enable ImageMagick integration. var util = require('util'); // constants var MAX_WIDTH = 100; var MAX_HEIGHT = 100; // get reference to S3 client var s3 = new AWS.S3(); exports.handler = function(event, context, callback) { // Read options from the event. console.log("Reading options from event:\n", util.inspect(event, {depth: 5})); var srcBucket = event.Records[0].s3.bucket.name; // Object key may have spaces or unicode non-ASCII characters. var srcKey = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " ")); var dstBucket = srcBucket + "resized"; var dstKey = "resized-" + srcKey; // Sanity check: validate that source and destination are different buckets. if (srcBucket == dstBucket) { callback("Source and destination buckets are the same."); return; } // Infer the image type. var typeMatch = srcKey.match(/\.([^.]*)$/); if (!typeMatch) { callback("Could not determine the image type."); return; } var imageType = typeMatch[1]; if (imageType != "jpg" && imageType != "png") { callback('Unsupported image type: ${imageType}'); return; } // Download the image from S3, transform, and upload to a different S3 bucket. async.waterfall([ function download(next) { // Download the image from S3 into a buffer. s3.getObject({ Bucket: srcBucket, Key: srcKey }, next); }, function transform(response, next) { gm(response.Body).size(function(err, size) { // Infer the scaling factor to avoid stretching the image unnaturally. var scalingFactor = Math.min( MAX_WIDTH / size.width, MAX_HEIGHT / size.height ); var width = scalingFactor * size.width; var height = scalingFactor * size.height; // Transform the image buffer in memory. this.resize(width, height) .toBuffer(imageType, function(err, buffer) { if (err) { next(err); } else { next(null, response.ContentType, buffer); } }); }); }, function upload(contentType, data, next) { // Stream the transformed image to a different S3 bucket. s3.putObject({ Bucket: dstBucket, Key: dstKey, Body: data, ContentType: contentType }, next); } ], function (err) { if (err) { console.error( 'Unable to resize ' + srcBucket + '/' + srcKey + ' and upload to ' + dstBucket + '/' + dstKey + ' due to an error: ' + err ); } else { console.log( 'Successfully resized ' + srcBucket + '/' + srcKey + ' and uploaded to ' + dstBucket + '/' + dstKey ); } callback(null, "message"); } ); }; w ߦͷίʔυɺͭͷؔ਺ w ݹ͍ίʔσΟϯάελΠϧ &4 BTZOD  w Ͳ͏΍Βσουίʔυ͕͋Δ w ؍ଌ͢Δҙຯ߹͍ͷগͳ͍໭Γ஋ w ˠࣗಈςετΛॻ͍ͯৼΔ෣͍͕มΘͬͯͳ͍ ͜ͱΛ͔֬Ίͳ͕Βɺ։ൃܧଓʹඋ͑ͯ৽͍͠ ίʔσΟϯάελΠϧ &4 1SPNJTF ʹҠ ߦ͍ͨ͠
  10. // dependencies var async = require('async'); var AWS = require('aws-sdk');

    var gm = require('gm') .subClass({ imageMagick: true }); // Enable ImageMagick integration. var util = require('util'); // constants var MAX_WIDTH = 100; var MAX_HEIGHT = 100; // get reference to S3 client var s3 = new AWS.S3(); exports.handler = function(event, context, callback) { // Read options from the event. console.log("Reading options from event:\n", util.inspect(event, {depth: 5})); var srcBucket = event.Records[0].s3.bucket.name; // Object key may have spaces or unicode non-ASCII characters. var srcKey = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " ")); var dstBucket = srcBucket + "resized"; var dstKey = "resized-" + srcKey;
  11. var dstBucket = srcBucket + "resized"; var dstKey = "resized-"

    + srcKey; // Sanity check: validate that source and destination are different buckets. if (srcBucket == dstBucket) { callback("Source and destination buckets are the same."); return; } // Infer the image type. var typeMatch = srcKey.match(/\.([^.]*)$/); if (!typeMatch) { callback("Could not determine the image type."); return; } var imageType = typeMatch[1]; if (imageType != "jpg" && imageType != "png") { callback('Unsupported image type: ${imageType}'); return; } // Download the image from S3, transform, and upload to a different S3 bucket. async.waterfall([ function download(next) { // Download the image from S3 into a buffer.
  12. // Download the image from S3, transform, and upload to

    a different S3 bucket. async.waterfall([ function download(next) { // Download the image from S3 into a buffer. s3.getObject({ Bucket: srcBucket, Key: srcKey }, next); }, function transform(response, next) { gm(response.Body).size(function(err, size) { // Infer the scaling factor to avoid stretching the image unnaturally. var scalingFactor = Math.min( MAX_WIDTH / size.width, MAX_HEIGHT / size.height ); var width = scalingFactor * size.width; var height = scalingFactor * size.height; // Transform the image buffer in memory. this.resize(width, height) .toBuffer(imageType, function(err, buffer) { if (err) { next(err);
  13. // Transform the image buffer in memory. this.resize(width, height) .toBuffer(imageType,

    function(err, buffer) { if (err) { next(err); } else { next(null, response.ContentType, buffer); } }); }); }, function upload(contentType, data, next) { // Stream the transformed image to a different S3 bucket. s3.putObject({ Bucket: dstBucket, Key: dstKey, Body: data, ContentType: contentType }, next); } ], function (err) { if (err) { console.error(
  14. Bucket: dstBucket, Key: dstKey, Body: data, ContentType: contentType }, next);

    } ], function (err) { if (err) { console.error( 'Unable to resize ' + srcBucket + '/' + srcKey + ' and upload to ' + dstBucket + '/' + dstKey + ' due to an error: ' + err ); } else { console.log( 'Successfully resized ' + srcBucket + '/' + srcKey + ' and uploaded to ' + dstBucket + '/' + dstKey ); } callback(null, "message"); } ); };
  15. ͜ͷ-BNCEB͸UFTUBCMFͩΖ͏͔

  16. ςετखॱॻWFS w 4όέοτ४උ w TPVSDFόέοτͱEFTUJOBUJPOόέοτΛ࡞੒ w TPVSDFόέοτʹαϯϓϧΦϒδΣΫτΛΞοϓϩʔυ͓ͯ͘͠ w -BNCEBؔ਺Λ࡞੒ͯ͠σϓϩΠ w

    σϓϩΠύοέʔδΛ࡞੒ w OQNJOTUBMMBTZODHN w σΟϨΫτϦؙ͝ͱ;*1ѹॖ w ࣮ߦϩʔϧΛ࡞੒ w *".ίϯιʔϧͰ<3PMF/BNF> <4FMFDU3PMF5ZQF> <"UUBDI1PMJDZ>Λઃఆ w ϩʔϧͷ"3/ΛϝϞ w -BNCEBؔ਺ΛσϓϩΠ w BXTMBNCEBDSFBUFGVODUJPO w ؔ਺ͷ"3/ΛϝϞ w ໨ࢹͰςετ w "NB[PO4αϯϓϧΠϕϯτσʔλΛϑΝΠϧʹอଘ w -BNCEB$-*JOWPLFίϚϯυΛ࣮ߦͯؔ͠਺Λݺͼग़͠ w αϜωΠϧ͕λʔήοτόέοτʹ࡞੒͞Εͨ͜ͱΛ֬ೝ w "84-BNCEBίϯιʔϧͰ-BNCEBؔ਺ͷϩάΛ֬ೝ ॴཁ࣌ؒ෼
  17. ͦ͜Ͱ4".Ͱ͢Α ৄ͘͠͸٢ా͞ΜͷηογϣϯΛ IUUQTHJUIVCDPNBXTMBCTTFSWFSMFTTBQQMJDBUJPONPEFM

  18. AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Image resize example Resources: SourceBucket:

    Type: AWS::S3::Bucket Properties: BucketName: images DestinationBucket: Type: AWS::S3::Bucket Properties: BucketName: imagesresized ProcessorFunction: Type: AWS::Serverless::Function Properties: Handler: index.handler Runtime: nodejs6.10 CodeUri: ./code Policies: AmazonS3FullAccess Events: ImageUpload: Type: S3 Properties: Bucket: !Ref SourceBucket Events: s3:ObjectCreated:* ςετखॱॻWFS w 4".ఆٛϑΝΠϧ࡞੒ w 4".σϓϩΠ༻4όέοτ४උ w -BNCEBؔ਺Λ࡞੒ͯ͠σϓϩΠ w BXTDMPVEGPSNBUJPOQBDLBHF w BXTDMPVEGPSNBUJPOEFQMPZ w BXTDMJͰςετ w BXTMPHTHFUBXTMBNCEB<MPHJE>ŠXBUDI w BXTTDQIPHFQOHTJNBHFTIPHFQOH w BXTTMTJNBHFTSFTJ[FE ॴཁ࣌ؒ෼
  19. ख࡞ۀσϓϩΠ໨ࢹςετ 4".ͰσϓϩΠ BXTDMJͰςετ σϓϩΠͳ͠Ͱɺ ϩʔΧϧͰςετ ͍ͨ͠ʂʂʂʂʂ ෼ ෼

  20. ςετࣗಈԽϐϥϛουͱΞϯνύλʔϯ IUUQTXBUJSNFMPOCMPHJOUSPEVDJOHUIFTPGUXBSFUFTUJOHJDFDSFBNDPOF

  21. Agenda ݱঢ়֬ೝ ςελϏϦςΟΛ͚͋͜͡Δ αΠζͱϐϥϛουͱϧʔϓ ͔ͦ͜Βઌ΁

  22. ϨΨγʔίʔυͱͦͷδϨϯϚ lςετ͕ͳ͍ίʔυ͸ϨΨγʔίʔυͩz lίʔυΛมߋ͢ΔͨΊʹ͸ςε τΛ੔උ͢Δඞཁ͕͋Δɻଟ͘ͷ ৔߹ɺςετΛ੔උ͢ΔͨΊʹ͸ɺ ίʔυΛมߋ͢Δඞཁ͕͋Δz

  23. ϨΨγʔίʔυͱϢχοτςετ lϦϑΝΫλϦϯά͢ΔલʹϢχοτςε τΛॻ͘ͷ͸ɺͱ͖ʹ͸ෆՄೳͰ͋Γɺ ͠͹͠͹ແҙຯͰ͋Δz

  24. ઃܭͷՄಈҬΛ֬อ͢Δ wςετ͕ͳ͍ͷ͸طʹઃܭ͕ѱ͍ஹީ wઃܭ࣮૷Λม͑Δͷ͕લఏ w࣮૷ͷςετΛॻ͔ͳ͍͜ͱ wςετ͕Χόʔ͢Δൣғʹ༡ͼΛ࣋ͨͤɺΧόʔ ൣғ಺ΛϦϑΝΫλϦϯά wঢ়گʹԠͯ͡ૈཻ͍౓ͷςετΛ࢖͍͜ͳ͢ IUUQTXXXTMJEFTIBSFOFUU@XBEBUFTUTUSBUFHZBOEUBDUJDT

  25. ࠓճ͸'BLF0CKFDU͕ΧΪ IUUQYVOJUQBUUFSOTDPN5FTU%PVCMFIUNM

  26. IUUQTHJUIVCDPNBUMBTTJBOMPDBMTUBDL

  27. IUUQTHJUIVCDPNBUMBTTJBOMPDBMTUBDL

  28. IUUQTHJUIVCDPNBUMBTTJBOMPDBMTUBDL

  29. $ SERVICES=s3 make infra . .venv/bin/activate; PYTHONPATH=. exec localstack/mock/infra.py Starting

    local dev environment. CTRL-C to quit. Starting mock S3 (port 4572)... Ready. $ aws --endpoint-url=http://localhost:4572 s3 ls $ aws --endpoint-url=http://localhost:4572 s3 mb s3://my-test-bucket make_bucket: my-test-bucket $ aws --endpoint-url=http://localhost:4572 s3 ls 2006-02-04 01:45:09 my-test-bucket $ aws --endpoint-url=http://localhost:4572 s3 cp ~/hoge.png s3://my-test-bucket upload: hoge.png to s3://my-test-bucket/hoge.png $ aws --endpoint-url=http://localhost:4572 s3 ls s3://my-test-bucket 2017-06-02 11:39:22 7538 hoge.png MPDBMTUBDLΛNBLF΍EPDLFSͰಈ͔͢ endpoint-url を指定すれば aws cli すら欺ける
  30. const assert = require('assert'); const fs = require('fs'); const path

    = require('path'); const AWS = require('aws-sdk'); const s3 = new AWS.S3({ s3ForcePathStyle: true, logger: console, endpoint: new AWS.Endpoint('http://localhost:4572') }); describe('localstack learning', () => { before(() => { return s3.createBucket({Bucket: 'test-bucket'}).promise().then(() => { return s3.putObject({ Bucket: 'test-bucket', Key: 'TQ_LOGO.png', ContentType: 'image/png', Body: fs.readFileSync(path.join(__dirname, '..', 'fixtures', 'TQ_LOGO.png')) }).promise(); }); }); it('s3.getObject', () => { return s3.getObject({Bucket: 'test-bucket', Key: 'TQ_LOGO.png'}).promise().then((res) => { assert(res); }); }); }); MPDBMTUBDLͱ1SPNJTFͷֶशςετΛॻ͍ͯΈΔ 学習テスト: ライブラリ等の 学習のみに特化したテスト
  31. const assert = require('assert'); const fs = require('fs'); const path

    = require('path'); const AWS = require('aws-sdk'); const s3 = new AWS.S3({ s3ForcePathStyle: true, logger: console, endpoint: new AWS.Endpoint('http://localhost:4572') }); describe('localstack learning', () => { before(() => { return s3.createBucket({Bucket: 'test-bucket'}).promise().then(() => { return s3.putObject({ Bucket: 'test-bucket', Key: 'TQ_LOGO.png', ContentType: 'image/png', Body: fs.readFileSync(path.join(__dirname, '..', 'fixtures', 'TQ_LOGO.png')) }).promise(); }); }); it('s3.getObject', () => { return s3.getObject({Bucket: 'test-bucket', Key: 'TQ_LOGO.png'}).promise().then((res) => { assert(res); }); }); }); endpoint を localstack に 向けるだけ! S3 のバケット URL をパス形式に変えておく (ハマりポイント)
  32. const assert = require('assert'); const fs = require('fs'); const path

    = require('path'); const AWS = require('aws-sdk'); const s3 = new AWS.S3({ s3ForcePathStyle: true, logger: console, endpoint: new AWS.Endpoint('http://localhost:4572') }); describe('localstack learning', () => { before(() => { return s3.createBucket({Bucket: 'test-bucket'}).promise().then(() => { return s3.putObject({ Bucket: 'test-bucket', Key: 'TQ_LOGO.png', ContentType: 'image/png', Body: fs.readFileSync(path.join(__dirname, '..', 'fixtures', 'TQ_LOGO.png')) }).promise(); }); }); it('s3.getObject', () => { return s3.getObject({Bucket: 'test-bucket', Key: 'TQ_LOGO.png'}).promise().then((res) => { assert(res); }); }); }); aws-sdk の Promise 機能をモリモリ試す
  33. উͯͦ͏ͳؾ͕͖ͯͨ͠

  34. ϨΨγʔίʔυͷδϨϯϚ lίʔυΛมߋ͢ΔͨΊʹ͸ςετΛ੔උ͢Δ ඞཁ͕͋Δɻଟ͘ͷ৔߹ɺςετΛ੔උ͢Δ ͨΊʹ͸ɺίʔυΛมߋ͢Δඞཁ͕͋Δz

  35. var MAX_HEIGHT = 100; // get reference to S3 client

    var s3; if (process.env.NODE_ENV === 'production') { s3 = new AWS.S3(); } else { s3 = new AWS.S3({ s3ForcePathStyle: true, logger: console, endpoint: new AWS.Endpoint('http://localhost:4572') }); } exports.handler = function(event, context, callback) { // Read options from the event. Ξϯνύλʔϯ5FTU-PHJDJO1SPEVDUJPO # " %
  36. ઀߹෦ʢ4FBNʣ l઀߹෦ʢ4FBNʣͱ͸ɺͦͷ৔ॴΛ௚઀ฤू ͠ͳͯ͘΋ɺϓϩάϥϜͷৼΔ෣͍Λม͑Δ ͜ͱͷͰ͖Δ৔ॴͰ͋Δz

  37. var AWS = require('aws-sdk'); var s3 = new AWS.S3(); var

    onObjectCreated = require('./on-object-created'); exports.handler = function(event, context, callback) { onObjectCreated({s3, event, callback}); }; ॲཧຊମΛผϞδϡʔϧʹग़͠ɺҾ਺Λ઀߹෦ʹ͢Δ ( 0 0 % // dependencies var async = require('async'); var gm = require('gm') .subClass({ imageMagick: true }); // Enable ImageMagick integration. var util = require('util'); // constants var MAX_WIDTH = 100; var MAX_HEIGHT = 100; module.exports = function({s3, event, callback}) { // Read options from the event. console.log("Reading options from event:\n", util.inspect(event, {depth: 5})); var srcBucket = event.Records[0].s3.bucket.name; ৽نϑΝΠϧPOPCKFDUDSFBUFEKT JOEFYKT
  38. const assert = require('assert'); const AWS = require('aws-sdk'); const s3

    = new AWS.S3({s3ForcePathStyle: true, endpoint: new AWS.Endpoint('http://localhost:4572')}); const onObjectCreated = require('../on-object-created'); describe('localstack based test suite for happy path cases', () => { let now, event; beforeEach(() => { now = new Date().getTime(); event = { Records: [ { s3: { bucket: { name: `test-bucket-${now}` }, object: { key: `TQ_LOGO_${now}.png` } } } ] }; return s3.createBucket({Bucket: `test-bucket-${now}`}).promise() .then(() => s3.createBucket({Bucket: `test-bucket-${now}resized`}).promise()) .then(() => s3.putObject({ Bucket: `test-bucket-${now}`, Key: `TQ_LOGO_${now}.png`, ContentType: 'image/png', Body: fs.readFileSync(path.join(__dirname, '..', 'fixtures', 'TQ_LOGO.png')) }).promise()); }); it('onObjectCreated callback', () => { return new Promise((resolve, reject) => { const callback = (err, message) => err ? reject(err) : resolve(message); onObjectCreated({s3, event, callback}); }).then((message) => { assert(/^message/.test(message)); }); }); ͜ΕͰςετ͕ॻ͚Δ
  39. const assert = require('assert'); const AWS = require('aws-sdk'); const s3

    = new AWS.S3({s3ForcePathStyle: true, endpoint: new AWS.Endpoint('http://localhost:4572')}); const onObjectCreated = require('../on-object-created'); describe('localstack based test suite for happy path cases', () => { let now, event; beforeEach(() => { now = new Date().getTime(); event = { Records: [ { s3: { bucket: { name: `test-bucket-${now}` }, object: { key: `TQ_LOGO_${now}.png` } } } ] }; return s3.createBucket({Bucket: `test-bucket-${now}`}).promise() .then(() => s3.createBucket({Bucket: `test-bucket-${now}resized`}).promise()) .then(() => s3.putObject({ Bucket: `test-bucket-${now}`, Key: `TQ_LOGO_${now}.png`, ContentType: 'image/png', Body: fs.readFileSync(path.join(__dirname, '..', 'fixtures', 'TQ_LOGO.png')) }).promise()); }); it('onObjectCreated callback', () => { return new Promise((resolve, reject) => { const callback = (err, message) => err ? reject(err) : resolve(message); onObjectCreated({s3, event, callback}); }).then((message) => { assert(/^message/.test(message)); }); }); ઀߹෦Λ׆༻ͯ͠ςετΛॻ͘
  40. Body: fs.readFileSync(path.join(__dirname, '..', 'fixtures', 'TQ_LOGO.png')) }).promise()); }); it('onObjectCreated callback', ()

    => { return new Promise((resolve, reject) => { const callback = (err, message) => err ? reject(err) : resolve(message); onObjectCreated({s3, event, callback}); }).then((message) => { assert(/^message/.test(message)); }); }); it('onObjectCreated creates and puts thumbnail into destination bucket', () => { return new Promise((resolve, reject) => { const callback = (err, message) => err ? reject(err) : resolve(message); onObjectCreated({s3, event, callback}); }) .then(() => { return s3.waitFor('objectExists', { Bucket: `test-bucket-${now}resized`, Key: `resized-TQ_LOGO_${now}.png` }).promise(); }) .then((data) => { assert(data); }); }); });
  41. ख࡞ۀσϓϩΠ໨ࢹςετ 4".ͰσϓϩΠ BXTDMJͰςετ ෼ ෼ MPDBMTUBDLΛ࢖ͬͨ ϩʔΧϧͰͷςετ ඵ

  42. Agenda ݱঢ়֬ೝ ςελϏϦςΟΛ͚͋͜͡Δ αΠζͱϐϥϛουͱϧʔϓ ͔ͦ͜Βઌ΁

  43. module.exports = function({s3, event, callback}) { // Read options from

    the event. console.log("Reading options from event:\n", util.inspect(event, {depth: 5})); var srcBucket = event.Records[0].s3.bucket.name; // Object key may have spaces or unicode non-ASCII characters. var srcKey = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " ")); var dstBucket = srcBucket + "resized"; var dstKey = "resized-" + srcKey; // Sanity check: validate that source and destination are different buckets. if (srcBucket == dstBucket) { callback("Source and destination buckets are the same."); return; } // Infer the image type. var typeMatch = srcKey.match(/\.([^.]*)$/); if (!typeMatch) { callback("Could not determine the image type."); return; } var imageType = typeMatch[1]; if (imageType != "jpg" && imageType != "png") { callback('Unsupported image type: ${imageType}'); return; } IUUQTHJUIVCDPNUXBEBDPWFSMBZFM ݱঢ়֬ೝͷͨΊΧόϨοδଌఆ
  44. IUUQTHJUIVCDPNUXBEBDPWFSMBZFM // Transform the image buffer in memory. this.resize(width, height)

    .toBuffer(imageType, function(err, buffer) { if (err) { next(err); } else { next(null, response.ContentType, buffer); } }); }); }, function upload(contentType, data, next) { // Stream the transformed image to a different S3 bucket. s3.putObject({ Bucket: dstBucket, Key: dstKey, Body: data, ContentType: contentType }, next); } ], function (err) { if (err) { console.error( 'Unable to resize ' + srcBucket + '/' + srcKey + ' and upload to ' + dstBucket + '/' + dstKey + ' due to an error: ' + err ); ྫ֎ܥ͕ςετ͞Εͯͳ͍͜ͱ͕Θ͔Δ
  45. l5FTU4J[FTzBU(PPHMF IUUQTUFTUJOHHPPHMFCMPHDPNUFTUTJ[FTIUNM

  46. medium small ઌఔॻ͍ͨ MPDBMTUBDLΛ׆༻ͨ͠ςετΛ NFEJVNαΠζͱఆٛ͢Δ MPDBMTUBDL͢Βඞཁͳ͍ ྫ֎ܥͷςετΛ TNBMMαΠζͱߟ͑Δ

  47. const assert = require('assert'); const onObjectCreated = require('../on-object-created'); describe('small sized

    test suite for exceptional cases:', () => { it('file without extension', () => { const event = { Records: [ { s3: { bucket: { name: `test-bucket` }, object: { key: `TQ_LOGO` } } } ] }; onObjectCreated({event, callback: (err, message) => { assert(err === 'Could not determine the image type.'); }}); }); it('extension is not `.png` nor `.jpg`', () => { const event = { Records: [ { s3: { bucket: { name: `test-bucket` }, object: { key: `TQ_LOGO.txt` } } } ] }; onObjectCreated({event, callback: (err, message) => { assert(err === 'Unsupported image type: txt'); }}); }); }); MPDBMTUBDL͢Β࢖Θͳ͍TNBMMUFTUͰྫ֎ܥΛςετ͢Δ
  48. IUUQTHJUIVCDPNQPXFSBTTFSUKTQPXFSBTTFSU > mocha --require intelli-espower-loader 'test/small/**/*.js' small sized test suite

    for exceptional cases: ✓ file without extension 1) extension is not `.png` nor `.jpg` 1 passing (47ms) 1 failing 1) small sized test suite for exceptional cases: extension is not `.png` nor `.jpg`: AssertionError: # test/small/exceptional_cases_test.js:25 assert(err === 'Unsupported image type: txt') | | | false "Unsupported image type: ${imageType}" --- [string] 'Unsupported image type: txt' +++ [string] err @@ -21,7 +21,16 @@ pe: -txt +${imageType} _人人人人人人人人人人_ > 突然のテスト失敗 <  ̄YYYYYYYYYY ̄
  49. IUUQTHJUIVCDPNQPXFSBTTFSUKTQPXFSBTTFSU > mocha --require intelli-espower-loader 'test/small/**/*.js' small sized test suite

    for exceptional cases: ✓ file without extension 1) extension is not `.png` nor `.jpg` 1 passing (47ms) 1 failing 1) small sized test suite for exceptional cases: extension is not `.png` nor `.jpg`: AssertionError: # test/small/exceptional_cases_test.js:25 assert(err === 'Unsupported image type: txt') | | | false "Unsupported image type: ${imageType}" --- [string] 'Unsupported image type: txt' +++ [string] err @@ -21,7 +21,16 @@ pe: -txt +${imageType} ڭ܇ςετͯ͠ͳ͍ίʔυ͸ಈ͔ͳ͍ if (imageType != "jpg" && imageType != "png") { callback('Unsupported image type: ${imageType}'); return; }
  50. medium small large σϓϩΠΛߦ͍ ຊ෺ͷ"84Λ࢖͏ςετΛ MBSHFαΠζͱߟ͑Δ

  51. AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Image resize example Resources: SourceBucket:

    Type: AWS::S3::Bucket Properties: BucketName: test-{{branchName}} DestinationBucket: Type: AWS::S3::Bucket Properties: BucketName: test-{{branchName}}resized ProcessorFunction: Type: AWS::Serverless::Function Properties: Handler: index.handler Runtime: nodejs6.10 CodeUri: ./code Policies: AmazonS3FullAccess Events: ImageUpload: Type: S3 Properties: Bucket: !Ref SourceBucket Events: s3:ObjectCreated:* ϒϥϯνຖʹҟͳΔ؀ڥΛ࡞Δ 手に馴染んだテンプレートエンジンを使って SAM 定義をブランチ毎に生成する
  52. const assert = require('assert'); const fs = require('fs'); const path

    = require('path'); const AWS = require('aws-sdk'); const s3 = new AWS.S3({ logger: console }); const execSync = require('child_process').execSync; const branch = execSync('git rev-parse --abbrev-ref @').toString().replace(/\n$/, ''); describe('large sized test suite', () => { it('happy path', () => { const now = new Date().getTime(); return Promise.resolve() .then(() => { return s3.putObject({ Bucket: `test-${branch}`, Key: `TQ_LOGO_${now}.png`, Body: fs.readFileSync(path.join(__dirname, '..', 'fixtures', 'TQ_LOGO.png')), ContentType: 'image/png' }).promise(); }) .then(() => { return s3.waitFor('objectExists', { Bucket: `test-${branch}resized`, Key: `resized-TQ_LOGO_${now}.png` }).promise(); }) .then((data) => { assert(data); }); }); }); MBSHFϒϥϯνผͷ"84؀ڥʹର͢Δςετ
  53. MBSHF NFEJVN TNBMM σϓϩΠࠐΈͰ෼ʙ෼ NT NT

  54. Agenda ݱঢ়֬ೝ ςελϏϦςΟΛ͚͋͜͡Δ αΠζͱϐϥϛουͱϧʔϓ ͔ͦ͜Βઌ΁

  55. IUUQTUIMJHIUDPNCMPHVODMFCPCUIFDMFBOBSDIJUFDUVSFIUNM ςελϏϦςΟ͕୲อ͞ΕͨͷͰɺ಺෦͸վળ͠์୊

  56. IUUQRJJUBDPN,PLVEPSJJUFNTBDGBC όάͱྫ֎Λ੾Γ෼͚Δ -BNCEB͸ϋϯυϦϯά͕ಛघ

  57. const gm = require('gm').subClass({ imageMagick: true }); const util =

    require('util'); const resize = require('./resize'); module.exports = function onObjectCreated ({s3, event, callback}) { console.log('Reading options from event:\n', util.inspect(event, {depth: 5})); const srcBucket = event.Records[0].s3.bucket.name; const srcKey = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' ')); const dstBucket = srcBucket + 'resized'; const dstKey = 'resized-' + srcKey; resize({s3, gm, srcBucket, srcKey, dstBucket, dstKey}) .then((message) => { console.log(message); callback(null, message); }) .catch((err) => { const message = `Unable to resize ${srcBucket}/${srcKey} and upload to ${dstBucket}/ ${dstKey} due to an error: ${err}`; console.error(message); callback(null, message); }); }; "84-BNCEBʹґଘͨ͠ཁૉΛઙ͍֊૚ͰҾ͖ണ͕͢
  58. ଟஈࣜΤϥʔϓϧʔϑ IUUQPSHBDIFNIBUFOBCMPHDPNFOUSZ

  59. l؂ࢹͱ͸ܧଓతͳςετͰ͋Δz IUUQEFWFMPQFSDZCP[VDPKQBSDIJWFTLB[VIPDSPOMPHGIUNM CZ,B[VIP0LV

  60. IUUQTNBSUJOGPXMFSDPNBSUJDMFTRBJOQSPEVDUJPOIUNM 2"JO1SPEVDUJPO

  61. IUUQUXBEBIBUFOBCMPHKQFOUSZEFCVHHJOHUFTUT ຊ൪؀ڥͷෆ۩߹Λςετʹࣸ͠औΔ

  62. medium small large XL ຊ෺ͷαʔϏεؒ࿈ܞΛ Ͳ͏ςετ͍͔ͯ͘͠

  63. NJDSPTFSWJDFؒͷςετ&&9- IUUQNBSUJOGPXMFSDPNBSUJDMFTNJDSPTFSWJDFUFTUJOH

  64. $POTVNFS%SJWFO$POUSBDU5FTUJOH IUUQTBTTFUTUIPVHIUXPSLTDPNBTTFUTUFDIOPMPHZSBEBSNBZFOQEG

  65. 1BDU IUUQTHJUIVCDPNSFBMFTUBUFDPNBVQBDU

  66. $POTVNFSଆΛNPDLTUVCײ֮Ͱॻ͘ IUUQEJVTDPNBVTJNQMJGZJOHNJDSPTFSWJDFUFTUJOHXJUIQBDUT

  67. ͦͷ૝ఆΛ1SPWJEFSଆͰWFSJGZ IUUQEJVTDPNBVTJNQMJGZJOHNJDSPTFSWJDFUFTUJOHXJUIQBDUT

  68. 1BDUΛ࢖ͬͨ$%$5FTUJOHͷશମ૾ IUUQUFDICMPHOFXTXFBWFSDPNXIZTIPVMEZPVVTFDPOTVNFSESJWFODPOUSBDUTGPSNJDSPTFSWJDFTJOUFHSBUJPOUFTUT

  69. ·ͱΊ w ϕετϓϥΫςΟε͸·ͩͳ͍ w ςελϏϦςΟͷΧΪ͸ϩʔΧϧ࣮ߦ w ςεταΠζΛఆٛ͠ɺ൒ܘͷҟͳΔ ϑΟʔυόοΫαΠΫϧΛܗ੒͢Δ w ӡ༻؂ࢹ΋ςετͱߟ͑Δ

    ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠

[8]ページ先頭

©2009-2025 Movatter.jp