Slide 1

Slide 1 text

1 Consider pluggable CLI tool implementation Go Conference 2018 Autumn - @izumin5210

Slide 2

Slide 2 text

2

Slide 3

Slide 3 text

3 grapi1SFTFOUFEPO(P$PO4QpOH https://speakerdeck.com/izumin5210/grapi-bulding-json-api-server-with-grpc-gateway-for-microservices

Slide 4

Slide 4 text

grapi (Pͷ"1*TFSWFS։ൃମݧΛߴΊΔ$-* QBDLBHF 4 * https://github.com/izumin5210/grapi ** https://cloud.google.com/apis/design/

Slide 5

Slide 5 text

grapi (Pͷ"1*TFSWFS։ൃମݧΛߴΊΔ$-* QBDLBHF 4 * https://github.com/izumin5210/grapi ** https://cloud.google.com/apis/design/

Slide 6

Slide 6 text

‣ ϘΠϥϓϨʔτͷTDBGGPMEJOH  ن໿ʹଇͬͨΠϯλϑΣʔεʢQSPUPʣͱ࣮૷ʢHPʣͷςϯϓϨʔτੜ੒  (PPHMF"1*%FTJHO(VJEF ʹԊͬͨ΋ͷ͕ੜ੒͞ΕΔ grapi (Pͷ"1*TFSWFS։ൃମݧΛߴΊΔ$-* QBDLBHF 4 * https://github.com/izumin5210/grapi ** https://cloud.google.com/apis/design/

Slide 7

Slide 7 text

‣ ϘΠϥϓϨʔτͷTDBGGPMEJOH  ن໿ʹଇͬͨΠϯλϑΣʔεʢQSPUPʣͱ࣮૷ʢHPʣͷςϯϓϨʔτੜ੒  (PPHMF"1*%FTJHO(VJEF ʹԊͬͨ΋ͷ͕ੜ੒͞ΕΔ ‣ H31$*%- DPEFHFOFSBUJPO  SPVUJOH  VO NBSTIBMJOHͳͲؾʹ͠ͳͯ͘ྑ͍ɼ
 ຊ࣭ʹϑΥʔΧε͠΍͍͢։ൃମݧΛఏڙ  HSQDHBUFXBZΛ಺แ͠ɼ;ͭ͏ͷ+40/"1*TFSWFSͱͯ͠ར༻Մ  ΋ͪΖΜɼHBUFXBZͳ͠ͷH31$TFSWFSʹ΋Ͱ͖Δ grapi (Pͷ"1*TFSWFS։ൃମݧΛߴΊΔ$-* QBDLBHF 4 * https://github.com/izumin5210/grapi ** https://cloud.google.com/apis/design/

Slide 8

Slide 8 text

‣ ϘΠϥϓϨʔτͷTDBGGPMEJOH  ن໿ʹଇͬͨΠϯλϑΣʔεʢQSPUPʣͱ࣮૷ʢHPʣͷςϯϓϨʔτੜ੒  (PPHMF"1*%FTJHO(VJEFʹԊͬͨ΋ͷ͕ੜ੒͞ΕΔ ‣ H31$*%- DPEFHFOFSBUJPO  SPVUJOH  VO NBSTIBMJOHͳͲؾʹ͠ͳͯ͘ྑ͍ɼ
 ຊ࣭ʹϑΥʔΧε͠΍͍͢։ൃମݧΛఏڙ  HSQDHBUFXBZΛ಺แ͠ɼ;ͭ͏ͷ+40/"1*TFSWFSͱͯ͠ར༻Մ  ΋ͪΖΜɼHBUFXBZͳ͠ͷH31$TFSWFSʹ΋Ͱ͖Δ grapi(Pͷ"1*TFSWFS։ൃମݧΛߴΊΔ$-* QBDLBHF 5 HSBQJʹ৽ͨͳDPEFHFOFSBUPSΛೖΕͨ͘ͳͬͨ ͔ͦ͠͠Ε͸QSPUPCVG͸͔ͭ͏͚ͲH31$͸ͪΐͬͱؔ܎ͳ͍ ୯७ͳ8FCαʔό։ൃʹؔΘͬͯ͘Δ࿩Ͱ΋ͳ͍ HSBQJ͸PQFOͳ෺ͳͷͰɼ͋·Γϔϯͳ΋ͷ͸ೖΕͨ͘ͳ͍ʜ

Slide 9

Slide 9 text

‣ ϘΠϥϓϨʔτͷTDBGGPMEJOH  ن໿ʹଇͬͨΠϯλϑΣʔεʢQSPUPʣͱ࣮૷ʢHPʣͷςϯϓϨʔτੜ੒  (PPHMF"1*%FTJHO(VJEFʹԊͬͨ΋ͷ͕ੜ੒͞ΕΔ ‣ H31$*%- DPEFHFOFSBUJPO  SPVUJOH  VO NBSTIBMJOHͳͲؾʹ͠ͳͯ͘ྑ͍ɼ
 ຊ࣭ʹϑΥʔΧε͠΍͍͢։ൃମݧΛఏڙ  HSQDHBUFXBZΛ಺แ͠ɼ;ͭ͏ͷ+40/"1*TFSWFSͱͯ͠ར༻Մ  ΋ͪΖΜɼHBUFXBZͳ͠ͷH31$TFSWFSʹ΋Ͱ͖Δ grapi(Pͷ"1*TFSWFS։ൃମݧΛߴΊΔ$-* QBDLBHF 5 HSBQJʹ৽ͨͳDPEFHFOFSBUPSΛೖΕͨ͘ͳͬͨ ͔ͦ͠͠Ε͸QSPUPCVG͸͔ͭ͏͚ͲH31$͸ͪΐͬͱؔ܎ͳ͍ ୯७ͳ8FCαʔό։ൃʹؔΘͬͯ͘Δ࿩Ͱ΋ͳ͍ HSBQJ͸PQFOͳ෺ͳͷͰɼ͋·Γϔϯͳ΋ͷ͸ೖΕͨ͘ͳ͍ʜ

Slide 10

Slide 10 text

6 Consider pluggable CLI tool implementation Go Conference 2018 Autumn - @izumin5210

Slide 11

Slide 11 text

7 @izumin5210 Application Engineer, Wantedly People Wantedly, Inc.

Slide 12

Slide 12 text

2ͳͥQMVHJOػߏ͕ඞཁͳͷ͔

Slide 13

Slide 13 text

2ͳͥQMVHJOػߏ͕ඞཁͳͷ͔ "։ൃऀɾར༻ऀͲͪΒ΋)BQQZʹ͢ΔͨΊ

Slide 14

Slide 14 text

9 Users ͓٬༷ͷ੠ʢྫʣ  ʢ1PTUHSF42-༻πʔϧʹରͯ͠ʣ.Z42-ʹ΋ରԠͯ͠΄͍͠ʂ  ʢίʔυδΣωϨʔλʹରͯ͠ʣUFTUJGZͰςετ΋ੜ੒ͯ͠ʂ Author

Slide 15

Slide 15 text

9 Users ͓٬༷ͷ੠ʢྫʣ  ʢ1PTUHSF42-༻πʔϧʹରͯ͠ʣ.Z42-ʹ΋ରԠͯ͠΄͍͠ʂ  ʢίʔυδΣωϨʔλʹରͯ͠ʣUFTUJGZͰςετ΋ੜ੒ͯ͠ʂ Author Զͷ੠ʢྫʣ  ʢ1PTUHSF42-͔͠࢖Θ΁Μ͠ʜʣ  ʢAUFTUJOHA೿ͳͷͰϞνϕʔγϣϯ͕ͳ͍ʜʣ

Slide 16

Slide 16 text

Users ͓٬༷ͷ੠ʢྫʣ  ʢ1PTUHSF42-༻πʔϧʹରͯ͠ʣ.Z42-ʹ΋ରԠͯ͠΄͍͠ʂ  ʢίʔυδΣωϨʔλʹରͯ͠ʣUFTUJGZͰςετ΋ੜ੒ͯ͠ʂ 10 Author ͔ͤͬ͘ݟ͚ͭͯ΋Β͑ͨͷʹʜ  .Z42-࢖ͬͨ͜ͱͳ͍͔Β೉͍͠ʜ  ͜͜ͰରԠͪ͠Ό͏ͱɼ΄͔ͷ%#.4΋
 αϙʔτཁٻ͕དྷͪΌ͏͔΋ʜ  044CVSOPVUͦ͠͏ 6OIBQQZ Զͷ੠ʢྫʣ  ʢ1PTUHSF42-͔͠࢖Θ΁Μ͠ʜʣ  ʢAUFTUJOHA೿ͳͷͰϞνϕʔγϣϯ͕ͳ͍ʜʣ

Slide 17

Slide 17 text

Author Զͷ੠ʢྫʣ  ʢ1PTUHSF42-͔͠࢖Θ΁Μ͠ʜʣ  ʢAUFTUJOHA೿ͳͷͰϞνϕʔγϣϯ͕ͳ͍ʜʣ 11 Users ͔ͤͬ͘ྑ͍πʔϧʹग़ձͬͨͷʹʜ  ࣗ෼͕ରԠ࡞ۀͯ͠΋͍͍͚Ͳɼ
 ࡞ऀ͕.Z42-࢖Θͳ͍ͳΒෆ҆ఆ͔΋ʜ  ͔ͩΒͱ͍ͬͯࣗ෼͕ϝϯς͠ଓ͚Δͷ͔ʜʁ 6OIBQQZ ͓٬༷ͷ੠ʢྫʣ  ʢ1PTUHSF42-༻πʔϧʹରͯ͠ʣ.Z42-ʹ΋ରԠͯ͠΄͍͠ʂ  ʢίʔυδΣωϨʔλʹରͯ͠ʣUFTUJGZͰςετ΋ੜ੒ͯ͠ʂ

Slide 18

Slide 18 text

Author Զͷ੠ʢྫʣ  ʢ1PTUHSF42-͔͠࢖Θ΁Μ͠ʜʣ  ʢAUFTUJOHA೿ͳͷͰϞνϕʔγϣϯ͕ͳ͍ʜʣ 12 Users ͓٬༷ͷ੠ʢྫʣ  ʢ1PTUHSF42-༻πʔϧʹରͯ͠ʣ.Z42-ʹ΋ରԠͯ͠΄͍͠ʂ  ʢίʔυδΣωϨʔλʹରͯ͠ʣUFTUJGZͰςετ΋ੜ੒ͯ͠ʂ Ϣʔβ͕֎͔Β֦ுͰ͖ΔΑ͏ʹ͢Δ  ֎ʹ੾Γग़͞Εͨ%#.4ͷESJWFSΛݺͿ  ίʔυੜ੒ͷલޙͰ֎෦ϓϩάϥϜΛݺͿ

Slide 19

Slide 19 text

13  ຊମΞϓϦ͸ͳΔ΂͘ബ͘อͪͭͭ  ֎͔ΒॲཧΛࠩ͠ࠐΊΔιέοτ͕͋Ε͹ྑ͍ Extensibility֦ுੑͷߴ͍πʔϧ

Slide 20

Slide 20 text

13  ຊମΞϓϦ͸ͳΔ΂͘ബ͘อͪͭͭ  ֎͔ΒॲཧΛࠩ͠ࠐΊΔιέοτ͕͋Ε͹ྑ͍ Extensibility֦ுੑͷߴ͍πʔϧ ‣ ϢʔβͷΞϓϦέʔγϣϯίʔυ͔Βར༻  FH42-%SJWFS Adatabase/sql/driver.DriverA .JEEMFXBSF Ahttp.HandlerA *OUFSDFQUPS Agrpc.*InterceptorA

Slide 21

Slide 21 text

13  ຊମΞϓϦ͸ͳΔ΂͘ബ͘อͪͭͭ  ֎͔ΒॲཧΛࠩ͠ࠐΊΔιέοτ͕͋Ε͹ྑ͍ Extensibility֦ுੑͷߴ͍πʔϧ ‣ ϢʔβͷΞϓϦέʔγϣϯίʔυ͔Βར༻  FH42-%SJWFS Adatabase/sql/driver.DriverA .JEEMFXBSF Ahttp.HandlerA *OUFSDFQUPS Agrpc.*InterceptorA ‣ طଘΞϓϦέʔγϣϯʹػೳΛ֎෇͚ͯ͠ར༻  FH1SPUPDQMVHJOT 5FSSBGPSNQSPWJEFST 3BJMTHFOFSBUPST  ࠓ೔͸ͬͪ͜ͷ࿩

Slide 22

Slide 22 text

14 Plugin ExperienceϓϥάΠϯͱ%FWFMPQFS&YQFSJFODF Users Plugin Developers

Slide 23

Slide 23 text

15 Plugin ExperienceϓϥάΠϯͱ%FWFMPQFS&YQFSJFODF ‣ ϢʔβͷυϝΠϯͱπʔϧͷSFRVJSFNFOU͕ζϨͳ͍͔  FHʮΠϯϑϥπʔϧͷϓϥάΠϯػߏʹ/PEFKT͕ඞཁʯ͸ଟ෼͠ΜͲ͍ Users Plugin Developers

Slide 24

Slide 24 text

15 Plugin ExperienceϓϥάΠϯͱ%FWFMPQFS&YQFSJFODF ‣ ϢʔβͷυϝΠϯͱπʔϧͷSFRVJSFNFOU͕ζϨͳ͍͔  FHʮΠϯϑϥπʔϧͷϓϥάΠϯػߏʹ/PEFKT͕ඞཁʯ͸ଟ෼͠ΜͲ͍ ‣ ෳ਺ਓϓϩδΣΫτͰ΋ར༻Ͱ͖Δ͔  ʮਓʹΑͬͯ࢖ͬͯΔόʔδϣϯ͕ҟͳΔʯ͸஍ࠈ  Ϛγϯάϩʔόϧ͡Όͳͯ͘ϓϩδΣΫτϩʔΧϧʹΠϯετʔϧग़དྷͳ͍ͱμϝ Users Plugin Developers

Slide 25

Slide 25 text

16 Plugin ExperienceϓϥάΠϯͱ%FWFMPQFS&YQFSJFODF Users Plugin Developers ‣ ࣮૷ɾσόοά͸؆୯͔  ݩπʔϧͷίϯςΩετΛࢀরɾར༻Ͱ͖Δ͔  ϩʔΧϧͰ࡞੒தͷ΋ͷΛ࣮πʔϧ͔Βར༻Ͱ͖Δ͔  ࣮૷ͷͨΊͷϨʔϧ͸͋Δ͔

Slide 26

Slide 26 text

16 Plugin ExperienceϓϥάΠϯͱ%FWFMPQFS&YQFSJFODF Users Plugin Developers ‣ ࣮૷ɾσόοά͸؆୯͔  ݩπʔϧͷίϯςΩετΛࢀরɾར༻Ͱ͖Δ͔  ϩʔΧϧͰ࡞੒தͷ΋ͷΛ࣮πʔϧ͔Βར༻Ͱ͖Δ͔  ࣮૷ͷͨΊͷϨʔϧ͸͋Δ͔ ‣ ഑෍͸؆୯͔  FH0SHBOJ[BUJPOJOUFSOBMͳπʔϧΛOQNʹެ։͢Δͷ͸͠ΜͲ͍

Slide 27

Slide 27 text

Pluggable tools: Real-World Examples

Slide 28

Slide 28 text

‣ ମݧͷྑ͍πʔϧ  3BJMT(FOFSBUPST  DSFBUFSFBDUBQQ ‣ (PͰ࣮૷͠΍ͦ͢͏ͳπʔϧ  QSPUPD  5FSSBGPSN  LVCFDUM  42-CPJMEFS Pluggable tools3FBM8PSME&YBNQMFT 18

Slide 29

Slide 29 text

‣ ࣮૷͕࠷ߴʹΧϯλϯ  A3BJMT(FOFSBUPST#BTFAͷαϒΫϥεΛ࡞Δ  ͜ͷαϒΫϥε໊͕·Μ·αϒίϚϯυʹʢԼͷίʔυͳΒASBJMTHJOJUJBMJ[FSAʹͳΔʣ  ࣮ߦ͢ΔͱQVCMJDNFUIPET͕ఆٛ͞Εͨॱʹݺͼग़͞Ε͍ͯ͘ Ralis Generators*%4-Ͱهड़(FNpMFʹॻ͍ͯಋೖ 19 * https://guides.rubyonrails.org/generators.html#creating-your-first-generator class InitializerGenerator < Rails::Generators::Base def create_initializer_file create_file "config/initializers/initializer.rb", "# Add initialization content here" end end

Slide 30

Slide 30 text

‣ ࣮૷͕࠷ߴʹΧϯλϯ  A3BJMT(FOFSBUPST#BTFAͷαϒΫϥεΛ࡞Δ  ͜ͷαϒΫϥε໊͕·Μ·αϒίϚϯυʹʢԼͷίʔυͳΒASBJMTHJOJUJBMJ[FSAʹͳΔʣ  ࣮ߦ͢ΔͱQVCMJDNFUIPET͕ఆٛ͞Εͨॱʹݺͼग़͞Ε͍ͯ͘ ‣ ಋೖ΋ָ  3BJMTBQQMJDBUJPOىಈ࣌ʹಡ·ΕΕ͹0,  HFNԽͯ͠(JU)VCʹஔ͚͹ɼ(FNpMFʹॻ͚ͩ͘ Ralis Generators*%4-Ͱهड़(FNpMFʹॻ͍ͯಋೖ 19 * https://guides.rubyonrails.org/generators.html#creating-your-first-generator class InitializerGenerator < Rails::Generators::Base def create_initializer_file create_file "config/initializers/initializer.rb", "# Add initialization content here" end end

Slide 31

Slide 31 text

‣ ར༻͢ΔUFNQMBUFͷࠩ͠ସ͕͑؆୯  FH`npx create-react-app my-app --scripts-version=react-scripts-ts` Ͱ5ZQF4DSJQUʹͳΔ  A--script-versionA͸࠷ऴతʹAyarn addAʹ౤͛ࠐ·ΕΔͷͰɼ೚ҙͷOQNNPEVMFͷ໊લΛೖΕ͍͍ͯ create-react-app*͍·Ͳ͖ͷ+4FS͸webpack.config.jsॻ͔ͳ͍ΜͰ͢Αʁ<ཁग़య> 20 * https://github.com/facebook/create-react-app

Slide 32

Slide 32 text

‣ ར༻͢ΔUFNQMBUFͷࠩ͠ସ͕͑؆୯  FH`npx create-react-app my-app --scripts-version=react-scripts-ts` Ͱ5ZQF4DSJQUʹͳΔ  A--script-versionA͸࠷ऴతʹAyarn addAʹ౤͛ࠐ·ΕΔͷͰɼ೚ҙͷOQNNPEVMFͷ໊લΛೖΕ͍͍ͯ ‣ ಠࣗͷUFNQMBUF΋؆୯ʹ࡞ΕΔ  ಺෦తʹ͸ASFBDUTDSJQUTAͷAscripts/init.jsAΛ࣮ߦͯ͠Δ͚ͩ  ͳͷͰɼʮΦϦδφϧͷASFBDUTDSJQUTAͷJOJUΛ࣮ߦͨ͋͠ͱʹಠࣗͷॲཧΛՃ͑ΔʯΑ͏ͳ
 Areact-scripts-AΛ࡞Ε͹ྑ͍  AZBSOBEEA͕ղऍͰ͖Ε͹͍͍ͷͰɼͱΓ͋͑ͣ(JU)VCʹஔ͍ͱ͚͹ΈΜͳ࢖͑ΔʢͨͿΜʣ create-react-app*͍·Ͳ͖ͷ+4FS͸webpack.config.jsॻ͔ͳ͍ΜͰ͢Αʁ<ཁग़య> 20 * https://github.com/facebook/create-react-app

Slide 33

Slide 33 text

create-react-app*͍·Ͳ͖ͷ+4FS͸webpack.config.jsॻ͔ͳ͍ΜͰ͢Αʁ<ཁग़య> 21 https://github.com/facebook/create-react-app/blob/v2.1.1/packages/create-react-app/createReactApp.js#L305-L313 if (useYarn) { command = 'yarnpkg'; args = ['add', '--exact']; // snip. [].push.apply(args, dependencies); args.push('--cwd'); args.push(root); // snip. } else { // snip. } if (verbose) { args.push('--verbose'); } const child = spawn(command, args, { stdio: 'inherit' }); child.on('close', code => { // snip. });

Slide 34

Slide 34 text

‣ ࣮૷͸TJNQMF FBTZ   Aprotoc --foo_out=DIR input.protoAͰAprotoc-gen-fooAͱ͍͏࣮ߦϑΝΠϧΛݺͼग़͢  Ҿ਺ͷ౉͠ํ͸ಠಛ  ର৅ʹͳͬͯΔ QSPUPͳͲ͸TUEJO͔Β  ͦͷଞͷҾ਺͸A--foo_out=:DIRAͱ͢Ε͹ʢͨͩͷจࣈྻͱͯ͠ʣ౉ͬͯ͘Δ protoc*࣮ߦ࣌ͷҾ਺೉֮ͯ͑͘͠ΒΕ΁Μ 22 * https://developers.google.com/protocol-buffers/docs/reference/go-generated

Slide 35

Slide 35 text

‣ ࣮૷͸TJNQMF FBTZ   Aprotoc --foo_out=DIR input.protoAͰAprotoc-gen-fooAͱ͍͏࣮ߦϑΝΠϧΛݺͼग़͢  Ҿ਺ͷ౉͠ํ͸ಠಛ  ର৅ʹͳͬͯΔ QSPUPͳͲ͸TUEJO͔Β  ͦͷଞͷҾ਺͸A--foo_out=:DIRAͱ͢Ε͹ʢͨͩͷจࣈྻͱͯ͠ʣ౉ͬͯ͘Δ ‣ ಋೖ͸ʜ  FYFDVUBCMF͔ͭA1"5)Aʹଘࡏ͢ΔPSA--plugin=path/to/protoc-gen-fooAΛ౉ͤ͹ྑ͍  ͲͷݴޠͷίϯςΩετԽʹΑͬͯಋೖ೉қ౓͕มΘΓͦ͏ protoc*࣮ߦ࣌ͷҾ਺೉֮ͯ͑͘͠ΒΕ΁Μ 22 * https://developers.google.com/protocol-buffers/docs/reference/go-generated

Slide 36

Slide 36 text

‣ ࣮૷͸&BTZ  ϨʔϧΛෑ͍ͯ͘Ε͍ͯΔʢAgithub.com/hashicorp/terraform/pluginAʣ  ಺෦͸Agithub.com/hashicorp/go-pluginA  ϓϥάΠϯ͕αʔόɾຊମ͕ΫϥΠΞϯτʹͳͬͯnet/rpcͰ௨৴ Terraform*͔͜͜ΒΑ͏΍͘(Pͷ࿩Ͱ͢ 23 * https://www.terraform.io/docs/plugins/basics.html

Slide 37

Slide 37 text

‣ ࣮૷͸&BTZ  ϨʔϧΛෑ͍ͯ͘Ε͍ͯΔʢAgithub.com/hashicorp/terraform/pluginAʣ  ಺෦͸Agithub.com/hashicorp/go-pluginA  ϓϥάΠϯ͕αʔόɾຊମ͕ΫϥΠΞϯτʹͳͬͯnet/rpcͰ௨৴ ‣ ಋೖ͸·͋·͋  ಛఆͷσΟϨΫτϦʹFYFDVUBCMFͳ΋ͷΛஔ͚ͩ͘  ஔ͖৔͸ϚγϯશମͰͭͳͷͰɼϓϩδΣΫτ͝ͱʹόʔδϣϯม͑Δͱ͔͸Ͱ͖ͳ͍  ʢ5FSSBGPSNͷ৔߹͸ͦΜͳधཁ΋ͳ͍ͷ͔΋ʣ Terraform*͔͜͜ΒΑ͏΍͘(Pͷ࿩Ͱ͢ 23 * https://www.terraform.io/docs/plugins/basics.html

Slide 38

Slide 38 text

‣ ࣮૷͸TJNQMF FBTZ   Akubectl foo barAͰAkubectl-foo-barAΛ୳࣮ͯ͠ߦ͢Δ  ίϯςΩετͱ͔͸؀ڥม਺ʹೖ͍ͬͯΔ  (PͰ͋Ε͹ศརύοέʔδ͕ଘࡏ͢ΔʢAk8s.io/kubectl/pkg/pluginutilsA Ak8s.io/cli-runtimeAʣ kubectl*ਅͬ౰Ͱࣗવͳ΍Γํ 24 * https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/ https://github.com/kubernetes/kubernetes/blob/v1.12.2/pkg/kubectl/cmd/cmd.go

Slide 39

Slide 39 text

‣ ࣮૷͸TJNQMF FBTZ   Akubectl foo barAͰAkubectl-foo-barAΛ୳࣮ͯ͠ߦ͢Δ  ίϯςΩετͱ͔͸؀ڥม਺ʹೖ͍ͬͯΔ  (PͰ͋Ε͹ศརύοέʔδ͕ଘࡏ͢ΔʢAk8s.io/kubectl/pkg/pluginutilsA Ak8s.io/cli-runtimeAʣ ‣ ಋೖ͸·͋·ָ͋ͦ͏  &YFDVUBCMFͳϑΝΠϧஔ͚ͩ͘  ͭ͘Δͱ͖͸มͳϥϯλΠϜʹґଘͤ͞ͳ͍΄͏͕਌੾ kubectl*ਅͬ౰Ͱࣗવͳ΍Γํ 24 * https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/ https://github.com/kubernetes/kubernetes/blob/v1.12.2/pkg/kubectl/cmd/cmd.go

Slide 40

Slide 40 text

25 type defaultPluginHandler struct{} func (h *defaultPluginHandler) Execute(executablePath string, cmdArgs, environment []string) error { return syscall.Exec(executablePath, cmdArgs, environment) } func handleEndpointExtensions(pluginHandler PluginHandler, cmdArgs []string) error { remainingArgs := []string{} // all "non-flag" arguments for idx := range cmdArgs { if strings.HasPrefix(cmdArgs[idx], "-") { break } remainingArgs = append(remainingArgs, strings.Replace(cmdArgs[idx], "-", "_", -1)) } foundBinaryPath := "" for len(remainingArgs) > 0 { path, err := pluginHandler.Lookup(fmt.Sprintf("kubectl-%s", strings.Join(remainingArgs, "-"))) // snip. foundBinaryPath = path break } // snip. if err := pluginHandler.Execute(foundBinaryPath, append([]string{foundBinaryPath}, cmdArgs[len(remainingArgs):]...), os.Environ()); err != nil { return err } return nil } ࣮૷͸ૉ๿ https://github.com/kubernetes/kubernetes/blob/v1.12.2/pkg/kubectl/cmd/cmd.go

Slide 41

Slide 41 text

SQLBoiler*๻͸͜Ε͕Ұ൪޷͖Ͱ͢ 26 * https://github.com/volatiletech/sqlboiler users, err := models.Users( qm.Where("age > ?", 30), qm.Limit(5), qm.Offset(6), ).All(db, ctx)

Slide 42

Slide 42 text

‣ 8IBUJT42-#PJMFS   ίʔυੜ੒ϕʔεͷ03.  %#ͷεΩʔϚΛಡΜͰɼͦΕͬΆ͍TUSVDUͱ࣮૷Λੜ੒ͯ͘͠ΕΔ SQLBoiler*๻͸͜Ε͕Ұ൪޷͖Ͱ͢ 26 * https://github.com/volatiletech/sqlboiler users, err := models.Users( qm.Where("age > ?", 30), qm.Limit(5), qm.Offset(6), ).All(db, ctx)

Slide 43

Slide 43 text

‣ 8IBUJT42-#PJMFS   ίʔυੜ੒ϕʔεͷ03.  %#ͷεΩʔϚΛಡΜͰɼͦΕͬΆ͍TUSVDUͱ࣮૷Λੜ੒ͯ͘͠ΕΔ ‣ Ͳ͕͜QMVHHBCMFʹͳͬͯΔʁ  ཪͷ%#.4ʹͭͳ͍ͰɼεΩʔϚಡΜͰɼϑΝΠϧΛੜ੒͢Δ΍ͭ SQLBoiler*๻͸͜Ε͕Ұ൪޷͖Ͱ͢ 26 * https://github.com/volatiletech/sqlboiler users, err := models.Users( qm.Where("age > ?", 30), qm.Limit(5), qm.Offset(6), ).All(db, ctx)

Slide 44

Slide 44 text

‣ ࣮૷͕&BTZ  ATRMCPJMFS AͳFYFDVUBCMFͳϑΝΠϧΛಡΉ  ATRMCPJMEFSESJWFST*OUFSGBDFAͷ࣮૷Λ
 AESJWFST%SJWFS.BJOAʹ౉͚ͩ͢ SQLBoiler*๻͸͜Ε͕Ұ൪޷͖Ͱ͢ 27 * https://github.com/volatiletech/sqlboiler func main() { drivers.DriverMain(&driver.PostgresDriver{}) } // Interface abstracts either a side-effect imp // that is called in order to produce the data type Interface interface { // Assemble the database information into a n Assemble(config Config) (*DBInfo, error) // Templates to add/replace for generation Templates() (map[string]string, error) // Imports to merge for generation Imports() (importers.Collection, error) }

Slide 45

Slide 45 text

‣ ࣮૷͕&BTZ  ATRMCPJMFS AͳFYFDVUBCMFͳϑΝΠϧΛಡΉ  ATRMCPJMEFSESJWFST*OUFSGBDFAͷ࣮૷Λ
 AESJWFST%SJWFS.BJOAʹ౉͚ͩ͢ ‣ ಋೖ͸ʁ  A1"5)AʹFYFDVUBCMFͳϑΝΠϧΛ഑ஔ͢ΔγϦʔζ  %SJWFS΋(PQBDLBHFͳͷͰɼͱΓ͋͑ͣAHPHFUAͰ͍͍  όʔδϣϯݻఆ͸ʜ  (Pʹ΋UPPMΛϓϩδΣΫτ͝ͱʹ؅ཧ͢Δखஈ͕΄͍͠Ͱ͢Ͷ SQLBoiler*๻͸͜Ε͕Ұ൪޷͖Ͱ͢ 27 * https://github.com/volatiletech/sqlboiler func main() { drivers.DriverMain(&driver.PostgresDriver{}) } // Interface abstracts either a side-effect imp // that is called in order to produce the data type Interface interface { // Assemble the database information into a n Assemble(config Config) (*DBInfo, error) // Templates to add/replace for generation Templates() (map[string]string, error) // Imports to merge for generation Imports() (importers.Collection, error) }

Slide 46

Slide 46 text

‣ ମݧͷྑ͍πʔϧ  3BJMT(FOFSBUPST3VCZ΍͹͍  DSFBUFSFBDUBQQͺͬͱݟ࣏҆ΘΔ͍͚Ͳɼ69ͱ֦ுੑͷόϥϯε͸ૉ੖Β͍͠ ‣ (PͰ࣮૷͠΍ͦ͢͏ͳπʔϧ  QSPUPD LVCFDUMΘ͔Γ΍͍͚͢ͲϓϥάΠϯͮ͘Γ͕໘౗  5FSSBGPSNΞϓϩʔν͸͓΋͠Ζ͍͚ͲɼόʔδϣϯϩοΫ͕ඞཁͳ৔໘Ͱ೰·͍͠  42-#PJMFS(PͰUPPM؅ཧ͢Δखஈ͑͋͞Ε͹ྑͦ͞͏ʁ Pluggable tools3FBM8PSME&YBNQMFT 28

Slide 47

Slide 47 text

Implements plugin-mechanism into grapi

Slide 48

Slide 48 text

‣ HSBQJͰٻΊ͍ͨཁ݅  1MVHJO͕(PͰ͋Δඞཁੑ͸ͳ͍  ͕ɼͳΜΒ͔ͷQBDLBHFNBOBHFSʹ৐͔ͬͬͨ΄͏͕࣮૷͸ָ  HSBQJ͕࣋ͬͯΔQSPUPD࣮ߦػೳ͸ར༻͍ͨ͠  ಋೖʹ಄Λ࢖Θͳ͍ͱ͍͚ͳ͍ͷ͸ආ͚͍ͨ  όʔδϣϯϩοΫ͸ඞਢ Approach)PXUPJNQMFNFOUQMVHJONFDIBOJTJOUPHSBQJ 30

Slide 49

Slide 49 text

‣ HSBQJͰٻΊ͍ͨཁ݅  1MVHJO͕(PͰ͋Δඞཁੑ͸ͳ͍  ͕ɼͳΜΒ͔ͷQBDLBHFNBOBHFSʹ৐͔ͬͬͨ΄͏͕࣮૷͸ָ  HSBQJ͕࣋ͬͯΔQSPUPD࣮ߦػೳ͸ར༻͍ͨ͠  ಋೖʹ಄Λ࢖Θͳ͍ͱ͍͚ͳ͍ͷ͸ආ͚͍ͨ  όʔδϣϯϩοΫ͸ඞਢ Approach)PXUPJNQMFNFOUQMVHJONFDIBOJTJOUPHSBQJ 31  ݁ہ(PͰ΍Δલఏͩͱָͦ͏  42-#PJMFSͱಉ͡Α͏ͳ
 )FMQFSΛ༻ҙͯ͋͛͠Δ

Slide 50

Slide 50 text

‣ HSBQJͰٻΊ͍ͨཁ݅  1MVHJO͕(PͰ͋Δඞཁੑ͸ͳ͍  ͕ɼͳΜΒ͔ͷQBDLBHFNBOBHFSʹ৐͔ͬͬͨ΄͏͕࣮૷͸ָ  HSBQJ͕࣋ͬͯΔQSPUPD࣮ߦػೳ͸ར༻͍ͨ͠  ಋೖʹ಄Λ࢖Θͳ͍ͱ͍͚ͳ͍ͷ͸ආ͚͍ͨ  όʔδϣϯϩοΫ͸ඞਢ Approach)PXUPJNQMFNFOUQMVHJONFDIBOJTJOUPHSBQJ 32 πʔϧͷύοέʔδΛWFOEPSJOHͯ͠ɼ
 ͦΕΛ؅ཧɾϏϧυͰ͖Δ࢓૊Έ͕͋Δͱྑͦ͞͏

Slide 51

Slide 51 text

5PPMEFQFOEFODJFTΛ؅ཧɾ࣮ߦ͢Δπʔϧ github.com/izumin5210/gex5IFJNQMFNFOUBUJPOPGDMBSJGZCFTUQSBDUJDFGPSUPPMEFQFOEFODJFT 33 // Code generated by github.com/izumin5210/gex. DO NOT E // +build tools package tools // tool dependencies import ( _ "github.com/haya14busa/reviewdog/cmd/reviewdog" _ "github.com/srvc/wraperr/cmd/wraperr" _ "golang.org/x/lint/golint" )

Slide 52

Slide 52 text

5PPMEFQFOEFODJFTΛ؅ཧɾ࣮ߦ͢Δπʔϧ  ಺෦తʹ͸AEFQAAHPNPEA͍ͣΕ͔Λར༻͍ͯ͠Δ  ͷͰɼͨͩ͘͠όʔδϣϯϩοΫ͞ΕΔ github.com/izumin5210/gex5IFJNQMFNFOUBUJPOPGDMBSJGZCFTUQSBDUJDFGPSUPPMEFQFOEFODJFT 33 // Code generated by github.com/izumin5210/gex. DO NOT E // +build tools package tools // tool dependencies import ( _ "github.com/haya14busa/reviewdog/cmd/reviewdog" _ "github.com/srvc/wraperr/cmd/wraperr" _ "golang.org/x/lint/golint" )

Slide 53

Slide 53 text

5PPMEFQFOEFODJFTΛ؅ཧɾ࣮ߦ͢Δπʔϧ  ಺෦తʹ͸AEFQAAHPNPEA͍ͣΕ͔Λར༻͍ͯ͠Δ  ͷͰɼͨͩ͘͠όʔδϣϯϩοΫ͞ΕΔ  AUPPMTHPAͱ͍͏ϑΝΠϧΛੜ੒ͯ͠JNQPSU͍ͯ͠Δ  *NQPSUͯ͠ͳ͍ϑΝΠϧ͸؅ཧ͞Εͳ͍ͷͰ github.com/izumin5210/gex5IFJNQMFNFOUBUJPOPGDMBSJGZCFTUQSBDUJDFGPSUPPMEFQFOEFODJFT 33 // Code generated by github.com/izumin5210/gex. DO NOT E // +build tools package tools // tool dependencies import ( _ "github.com/haya14busa/reviewdog/cmd/reviewdog" _ "github.com/srvc/wraperr/cmd/wraperr" _ "golang.org/x/lint/golint" )

Slide 54

Slide 54 text

5PPMEFQFOEFODJFTΛ؅ཧɾ࣮ߦ͢Δπʔϧ  ಺෦తʹ͸AEFQAAHPNPEA͍ͣΕ͔Λར༻͍ͯ͠Δ  ͷͰɼͨͩ͘͠όʔδϣϯϩοΫ͞ΕΔ  AUPPMTHPAͱ͍͏ϑΝΠϧΛੜ੒ͯ͠JNQPSU͍ͯ͠Δ  *NQPSUͯ͠ͳ͍ϑΝΠϧ͸؅ཧ͞Εͳ͍ͷͰ  ΋ͱ΋ͱ͸AmockgenAͷόʔδϣϯΛݻఆͨͯ͘͠࡞ͬͨ github.com/izumin5210/gex5IFJNQMFNFOUBUJPOPGDMBSJGZCFTUQSBDUJDFGPSUPPMEFQFOEFODJFT 33 // Code generated by github.com/izumin5210/gex. DO NOT E // +build tools package tools // tool dependencies import ( _ "github.com/haya14busa/reviewdog/cmd/reviewdog" _ "github.com/srvc/wraperr/cmd/wraperr" _ "golang.org/x/lint/golint" )

Slide 55

Slide 55 text

5PPMEFQFOEFODJFTΛ؅ཧɾ࣮ߦ͢Δπʔϧ  ಺෦తʹ͸AEFQAAHPNPEA͍ͣΕ͔Λར༻͍ͯ͠Δ  ͷͰɼͨͩ͘͠όʔδϣϯϩοΫ͞ΕΔ  AUPPMTHPAͱ͍͏ϑΝΠϧΛੜ੒ͯ͠JNQPSU͍ͯ͠Δ  *NQPSUͯ͠ͳ͍ϑΝΠϧ͸؅ཧ͞Εͳ͍ͷͰ  ΋ͱ΋ͱ͸AmockgenAͷόʔδϣϯΛݻఆͨͯ͘͠࡞ͬͨ  ʢ͙͢ެࣜʹ΋ೖΔͰ͠ΐʜͱ͍͏ࢥ͍͕͋ΔͷͰബ͘Ͱ͖͍ͯΔʣ github.com/izumin5210/gex5IFJNQMFNFOUBUJPOPGDMBSJGZCFTUQSBDUJDFGPSUPPMEFQFOEFODJFT 33 // Code generated by github.com/izumin5210/gex. DO NOT E // +build tools package tools // tool dependencies import ( _ "github.com/haya14busa/reviewdog/cmd/reviewdog" _ "github.com/srvc/wraperr/cmd/wraperr" _ "golang.org/x/lint/golint" )

Slide 56

Slide 56 text

‣ HSBQJͰٻΊ͍ͨཁ݅  1MVHJO͕(PͰ͋Δඞཁੑ͸ͳ͍  ͕ɼͳΜΒ͔ͷQBDLBHFNBOBHFSʹ৐͔ͬͬͨ΄͏͕࣮૷͸ָ  HSBQJ͕࣋ͬͯΔQSPUPD࣮ߦػೳ͸ར༻͍ͨ͠  ಋೖʹ಄Λ࢖Θͳ͍ͱ͍͚ͳ͍ͷ͸ආ͚͍ͨ  όʔδϣϯϩοΫ͸ඞਢ Approach)PXUPJNQMFNFOUQMVHJONFDIBOJTJOUPHSBQJ 34 πʔϧͷύοέʔδΛWFOEPSJOHͯ͠ɼ
 ͦΕΛ؅ཧɾϏϧυͰ͖Δ࢓૊Έ͕͋Δͱྑͦ͞͏ ಺෦తʹHFYΛ͔ͭ͑͹Αͦ͞͏ʂ

Slide 57

Slide 57 text

Plugin exampleHSQBJHFODPNNBOE 35

Slide 58

Slide 58 text

Plugin exampleHSQBJHFODPNNBOE 35  γϯϓϧͳྫ

Slide 59

Slide 59 text

Plugin exampleHSQBJHFODPNNBOE 35  γϯϓϧͳྫ  ੩తϑΝΠϧʢUFNQBMUFʣͷFNCFEEJOHʹ͸WGTHFOΛ࢖ͬͨ  ಺෦తʹ͸AIUUQ'JMF4ZTUFNAʹͳΕ͹ԿͰ΋͍͍

Slide 60

Slide 60 text

Plugin exampleHSQBJHFODPNNBOE 35  γϯϓϧͳྫ  ੩తϑΝΠϧʢUFNQBMUFʣͷFNCFEEJOHʹ͸WGTHFOΛ࢖ͬͨ  ಺෦తʹ͸AIUUQ'JMF4ZTUFNAʹͳΕ͹ԿͰ΋͍͍  ੜ੒ίʔυΛTOBQTIPUUFTU͍ͯ͠Δ - github.com/bradleyjkemp/cupaloy

Slide 61

Slide 61 text

Plugin exampleHSQBJHFOUZQF 36

Slide 62

Slide 62 text

Plugin exampleHSQBJHFOUZQF 36  ͪΐͬͱ͚ͩ΍΍͍͜͠ྫ  %*ͱ͔͋Δͷ͸ɼHSBQJຊମͷQSPUPD8SBQQFSΛར༻͍ͨͨ͠Ί  ຊମ͕಺෦ͰXJSFΛ࢖͍ͬͯΔͷͰɼͦΕʹ৐͔ͬͬͨ  ͳͷͰผʹඞਢ͡Όͳ͍

Slide 63

Slide 63 text

Plugin exampleHSQBJHFOUZQF 37 func main() { buildCommand(di.NewApp).MustExecute() } func buildCommand(createApp di.CreateAppFunc, opts ...gencmd.Option) gencmd.Exec { return gencmd.New( "type", newGenerateCommand(createApp), newDestroyCommand(), opts..., ) } func newGenerateCommand(createApp di.CreateAppFunc) *gencmd.Command { var ( app *di.App

Slide 64

Slide 64 text

Plugin exampleHSQBJHFOUZQF 38 func newGenerateCommand(createApp di.CreateAppFunc) *gencmd.Command { var ( app *di.App ) return &gencmd.Command{ Use: "generate NAME", Short: "Generate a new type", Args: cobra.ExactArgs(1), TemplateFS: template.FS, ShouldInsideApp: true, PreRun: func(c *gencmd.Command, args []string) error { var err error app, err = createApp(c) return errors.WithStack(err) }, BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) { return buildParams(args[0], c.Ctx().Ctx) }, PostRun: func(c *gencmd.Command, args []string) error { return errors.WithStack(app.Protoc.Exec(context.TODO())) }, } }

Slide 65

Slide 65 text

Plugin exampleHSQBJHFOUZQF 39 func newGenerateCommand(createApp di.CreateAppFunc) *gencmd.Command { var ( app *di.App ) return &gencmd.Command{ Use: "generate NAME", Short: "Generate a new type", Args: cobra.ExactArgs(1), TemplateFS: template.FS, ShouldInsideApp: true, PreRun: func(c *gencmd.Command, args []string) error { var err error app, err = createApp(c) return errors.WithStack(err) }, BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) { return buildParams(args[0], c.Ctx().Ctx) }, PostRun: func(c *gencmd.Command, args []string) error { return errors.WithStack(app.Protoc.Exec(context.TODO())) }, } }  ґଘϞδϡʔϧͷॳظԽ  ͜͜Ͱ͸ࣗલͷ%*DPOUBJOFSΛ
 ࢖͍ͬͯΔ͕ɼ;ͭ͏͸Կ΋͠ͳ͍

Slide 66

Slide 66 text

Plugin exampleHSQBJHFOUZQF 40 func newGenerateCommand(createApp di.CreateAppFunc) *gencmd.Command { var ( app *di.App ) return &gencmd.Command{ Use: "generate NAME", Short: "Generate a new type", Args: cobra.ExactArgs(1), TemplateFS: template.FS, ShouldInsideApp: true, PreRun: func(c *gencmd.Command, args []string) error { var err error app, err = createApp(c) return errors.WithStack(err) }, BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) { return buildParams(args[0], c.Ctx().Ctx) }, PostRun: func(c *gencmd.Command, args []string) error { return errors.WithStack(app.Protoc.Exec(context.TODO())) }, } }  UFYUUFNQMBUFʹ౉Δύϥϝλ૊Έཱͯ  ϑΝΠϧͷੜ੒ɾॏෳ൑ఆͳͲ͸
 উखʹ΍ͬͯ͘ΕΔ

Slide 67

Slide 67 text

Plugin exampleHSQBJHFOUZQF 41 func newGenerateCommand(createApp di.CreateAppFunc) *gencmd.Command { var ( app *di.App ) return &gencmd.Command{ Use: "generate NAME", Short: "Generate a new type", Args: cobra.ExactArgs(1), TemplateFS: template.FS, ShouldInsideApp: true, PreRun: func(c *gencmd.Command, args []string) error { var err error app, err = createApp(c) return errors.WithStack(err) }, BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) { return buildParams(args[0], c.Ctx().Ctx) }, PostRun: func(c *gencmd.Command, args []string) error { return errors.WithStack(app.Protoc.Exec(context.TODO())) }, } }  ੜ੒ͷલޙʹ೚ҙͷॲཧΛڬΊΔ  ͜͜Ͱ͸QSPUPDΛ͍͍ײ͡ʹ
 ࣮ߦ͍ͯ͠Δ

Slide 68

Slide 68 text

‣ ςετ༻ͷNPDLTUVC͸༻ҙͯ͋͛͠Δ  ͓͠ΌΕඪ४ग़ྗͱ͔ɼࠓճͷ৔߹ͩͱQSPUPDͱ͔ ‣ &YBNQMFΛ༻ҙͯ͋͛͠Δ  ͜Ε͕Ұ൪ମݧྑ͘ͳΔؾ͕͢Δ  HSBQJͷ৔߹͸HFOFSBUPS͕͢΂ͯQMVHJOʹͳ͍ͬͯΔͷͰɼ͜Ε͕ͦͷ··FYBNQMFʹͳΔ  ͨͩυΩϡϝϯτ͕ͳ͍ͷͰʜ ‣ 1MVHJOHFOFSBUPSΛఏڙ͢Δ  50%0ͭ͗ͷϦϦʔεʹؚΊΔ TipsGPSJNQSPWJOH%FWFMPQFS&YQFSJFODF 42

Slide 69

Slide 69 text

Go Conference 2018 Autumn - @izumin5210 ConclusionΈΜͳ͕޾ͤʹͳΔπʔϧΛ࡞ΔͨΊʹ ‣ ϝΠϯ͡Όͳ͍ػೳ͸ɼQMVHJOͱͯ͠ఏڙͰ͖ΔΑ͏ʹ͢Δ  044CVSOPVU๷ࢭ  ܧଓతʹϝϯς͍ͯͨ͘͠Ίʹ΋ɼʮͻͱͭͷ͜ͱΛ͏·͘΍ΔʯΞʔΩςΫνϟ ‣ ࢖͍΍͍͢ɾ࡞Γ΍͍͢QMVHJOػߏ  Ϣʔβͷ஌ࣝɾπʔϧͷίϯςΩετʹ͋ͬͨखஈͰఏڙͰ͖ΔΑ͏ʹ  AHJUDMPOFA௚ޙͰ΋໰୊ͳ͘࢖͍࢝ΊΒΕΔΑ͏ʹ  ϓϥάΠϯΛ࡞Δਓ΋CVSOPVU͠ͳ͍Α͏ʹ