Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
サーバーレス x IoT 〜我々はどういった課題に直面してそれをどのように解決したのか〜
Search
Koji Nakayama
April 06, 2018
0
1.3k
サーバーレス x IoT 〜我々はどういった課題に直面してそれをどのように解決したのか〜
AWS Deep Night Part.2の登壇資料です。
Koji Nakayama
April 06, 2018
Tweet
Share
More Decks by Koji Nakayama
See All by Koji Nakayama
Software Testing in AWS IoT with The Power of Python
knakayama
0
1.5k
The ~~Ten~~ Three Most Critical Security Risks in Serverless Architectures
knakayama
2
1.4k
Bloxが切り開くECSの世界
knakayama
1
1.1k
AWS Serverless Application Modelのデプロイ戦略
knakayama
4
2.3k
github-classmethod-study-20170426
knakayama
1
3.6k
サーバレスアーキテクチャはじめの一歩
knakayama
1
1.4k
Featured
See All Featured
Unsuck your backbone
ammeep
669
57k
Testing 201, or: Great Expectations
jmmastey
40
7.1k
The Art of Programming - Codeland 2020
erikaheidi
53
13k
Designing for humans not robots
tammielis
250
25k
Building a Modern Day E-commerce SEO Strategy
aleyda
38
7k
Scaling GitHub
holman
458
140k
How GitHub (no longer) Works
holman
311
140k
A designer walks into a library…
pauljervisheath
205
24k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
132
33k
The World Runs on Bad Software
bkeepers
PRO
65
11k
Side Projects
sachag
452
42k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
365
25k
Transcript
ςЄϝЄϹφ x IoT 뺶౯̸΅Ϳ͚͜͵抓氂ፗᶎͭͼ ͳΨͿ΄Ξ͜ᥴ䷥ͭ͵΄͡뺶 AWS Deep Night Part.2 2018/04/05
Ӿઊ ଛလ 1
ᛔ૩奧Օ • Ӿઊ ଛလ • μ϶φϮϊϐϖ ϯϝαϸίϤϷςЄϠφ᮱ • AWSϊϷϲЄτϴЀίЄκϓμϕ •
ςЄϝЄςαϖεЀυϘί • GitHub: knakayama • 奺䵉 • ηЀϤϹςЄϝ΄晁አ3ଙ • AWS 2ଙ 2
ψϐτϴЀٖͺ͚ͼ • ᛔړ͢樛Υͼ͚Ρໜկͽ咲ኞͭ͵抓氂;ͳ΄ᥴ䷥ොဩͺ͚ͼ扖 ͭΔͯ • ᇙAWS IoT;ςЄϝЄϹφίϤϷξЄτϴЀϢζЄθφͭͼ͚ Δͯ • IoTϔϝαφ΄ϜЄϖγδίΞΠٖ΅扖ͭΔͱΩ̵;͚͜͡ͽ
͚ͣ ! • 揾ාل樄ͭΔͯ 3
ίυδЀύ 1. Ϳ͚͜͵ದ悬Ψֵͼ͚Ρ΄͡ 2. ίϤϷ͡ΟIoTϔϝαφ΄֢०䤂ͯΡ㺔氂 3. ᭗Ꭳͭ͵͚樌ᵍ͢ͰΡ㺔氂 4. AWS IoT΄ϓφϕͿͯ͜Ρ͡㺔氂
4
ίυδЀύ 1. Ϳ͚͜͵ದ悬Ψֵͼ͚Ρ΄͡ 2. ίϤϷ͡ΟIoTϔϝαφ΄֢०䤂ͯΡ㺔氂 3. ᭗Ꭳͭ͵͚樌ᵍ͢ͰΡ㺔氂 4. AWS IoT΄ϓφϕͿͯ͜Ρ͡㺔氂
5
ίЄκϓμώϰ༷ᥝ • ᓕቘᘏአϧЄυ΅CloudFront + S3ͽSPA̵ εЀϖϳЄσ΅ϯϝαϸίϤϷ • ϝϐμεЀϖAPI΅API Gateway +
Lambda + DynamoDB • Cognito + API Gateway΄θφόϭηЄϊ϶ ασΨڥአͭ͵扯戣/扯ݢ • IoTϔϝαφ΅AWS IoTϔЄόΨPublish̵ Device Shadow奺ኧͽϔϝαφΨ֢ • Kinesis Data Streams + LambdaΨڥአͭͼ S3ړຉአϔЄόכਂ 6
Θͭͪ͜奧ՕͯΡ; • Lambda樛හ΄϶Ѐόαϭ΅Python • 䯤౮ᓕቘ΅CloudFormation/AWS SAM • CI/CDCircleCIΨڥአͭͼ͚Ρ1 • Lambda樛හ΄Ϻνᓕቘ΅Logentries2
• ϮϕϷμφ΅Datadogͽ哶憙 2 https://dev.classmethod.jp/devops/managing-logentries-token-with-terraform-and-ssm-parameter-store/ 1 https://dev.classmethod.jp/server-side/serverless/practical-ci-cd-with-aws-sam-circleci-and-localstack/ 7
ίυδЀύ 1. Ϳ͚͜͵ದ悬Ψֵͼ͚Ρ΄͡ 2. ίϤϷ͡ΟIoTϔϝαφ΄֢०䤂ͯΡ㺔氂 3. ᭗Ꭳͭ͵͚樌ᵍ͢ͰΡ㺔氂 4. AWS IoT΄ϓφϕͿͯ͜Ρ͡㺔氂
8
Device Shadow;΅ "ϔϝαφ΄匍ࣁ΄ᇫ䙪ఘ䁭΄כਂ;ݐ ֵአͫΡJSONϖκϲϮЀϕͽ̶ͯ"3 "AWS μ϶γϖͽ΄ϔϝαφ΄姆ᤒ匍ΨݢᚆͭΔ̶ͯๅෛͫ͵ᇫ䙪 ఘ䁭ΨϔϝαφτϰϖγϞϣϷϐτϲͭͼͥ͠;̵ϔϝαφ΅ള姆䦒 ᛔΟ΄ᇫ䙪Ψݶ๗ͫͱΡͩ;͢ͽͣΔ̶ͯΔ͵̵ϔϝαφ΅匍ࣁ΄ᇫ 䙪ΨίϤϷξЄτϴЀΚ㳨΄ϔϝαφአτϰϖγϞϣϷϐτϲͯΡ ͩ;ΘͽͣΔ̶ͯ"3
3 https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/what-is-aws-iot.html 9
ΡΑͿ뺶 10
ᥝͯΡ • AWS IoTӤਂࣁͯΡ 㫪మጱ ϔϝαφ • ϳЄσ憙ᅩ͡Ο憎Ρ;ͩ΄ϔϝαφ΅㶨ΡJSON • ίϤϷ
- IoTϔϝαφ樌΄᭗מ΅Device ShadowΨլՕͭͼᤈ͜ • ӞᛱጱIoTϔϝαφ - Device Shadow樌΅MQTT over TLS̵ί ϤϷ - Device Shadow樌΅HTTPS 11
匍ࣁ΄ᇫ䙪Ψᭆמ • IoTϔϝαφ͢匍ࣁ΄ᇫ䙪( reported )Ψ ᭆמ • ֺ͞ΆՔ΅櫮რ͢ͺ͚ͼ͚ • ϕϡϐμ:
$aws/things/<thingName>/ shadow/update { "reported": { "power": "off" } } 12
͘ΡΏͣᇫ䙪Ψᭆמ • ίϤϷ͡Ο͘ΡΏͣᇫ䙪( desired )Ψᭆ מ • ֺ͞Ά櫮რͺͧͼѺ • API:
UpdateThingShadow { "reported": { "power": "off" }, "desired": { "power": "on" } } 13
͘ΡΏͣᇫ䙪ݶ๗ • IoTϔϝαφ͢匍ࣁ΄ᇫ䙪;͘ΡΏͣᇫ䙪΄૧ړ ( delta )ΨݑͧݐΠ̵͘ΡΏͣᇫ䙪(櫮რͺͧΡ)ݶ ๗ • ϕϡϐμ: $aws/things/<thingName>/shadow/update/
delta { "reported": { "power": "off" }, "desired": { "power": "on" }, "delta": { "power": "on" } } 14
Θͭ͜奞͚ͥ͜͡; • ͘ΡΏͣᇫ䙪( desired );匍ࣁ΄ᇫ䙪( reported )ΨJSON୵ୗͽכਂ̵ͭͳ ૧ړ( delta )͢ਂࣁͭ͵䁰ݳ̵IoTϔϝαφ͘͢ΡΏͣᇫ䙪ݶ๗ݢᚆ
• IoTϔϝαφ΅MQTTϕϡϐμΎϮϐψЄυΨPublishͭͼDevice Shadow reported Ψᭆמ • ᭋϕϡϐμΨSubscribeͯΡͩ;ͽ delta ΨݑͧݐΠ̵ desired ΄ᇫ䙪ݶ๗ • Device ShadowΨ֢ͯΡϕϡϐμ΅AWS IoTͽԪڹ䷥Δͼ͚Ρ • AWS IoT΅ϮϐψЄυΨᓕቘͯΡ(ϮϐψЄυϣϺЄθЄ)Ͷͧ΄ͽ̵ϔϝα φ㯎ݶ๗ͯΡ͵Η΄䋚ᤰ͢ᥝ 15
๋ṛͽͯ 16
ͽ̵͚ͺΘ΄Ξ͜Device Shadow Ψֵͼ͚Ρ; 17
"An error occurred (ConflictException) when calling the UpdateThingShadow operation: Version
conflict" 18
֜᩸ͣ͢͵΄͡ • Device Shadow΅ݘ͚ desired ͽӤ䨗͚ͣͭΞ͜Ϻϐμ䱛䯤͢ਂࣁͯ Ρ • Device ShadowΨๅෛͯΡ͵ΉαЀμϷϮЀϕͫΡϝЄυϴЀΞΡ
Ϻϐμ • Device Shadow΄ๅෛ͢ݶ䦒咲ኞͭ͵͵Η Version conflict ε϶Є͢咲 ኞͭ͵ • ͺΔΠ̵IoTϔϝαφΞΡ毱ᔺDevice Shadow΄ๅෛΞͼ̵ίϤϷ ΞΡUpdateThingShadow͢०䤂ͭΚ͚ͯᇫ䙪ͼ͚͵ 19
Ϳ͜ᥴ䷥ͭ͵΄͡ • ϯϝαϸίϤϷͽൎͭͼͣ͠͵͚ϔЄό;ͳ ͜ͽ͚Θ΄ͽPublishͯΡϕϡϐμΨړ櫝ͭ͵ • ൎͭͼͣ͠͵͚ϔЄό: εЀϖϳЄσ憎ͱ ͵͚ϔЄό(櫮რ/ჅଶͿ) • ͳ͜ͽ͚ϔЄό:
㶨ΡϔЄόړຉአ΄ϔЄ ό • ͳ͜ͽ͚ϔЄό΅Device Shadowአϕϡϐμͽ ΅̵ͥ㳨΄ϕϡϐμPublishͯΡΞ͜䄜ๅ • ֺ: my/things/<thingName>/state • ͺΔΠ̵ӧᥝDevice Shadow΄ๅෛΨಪګͭ͵ 20
ර懺 • Device ShadowᭆΡΏͣϔЄό΅ίϤϷ͢ൎͭͼͥ͠Ώͣ Θ΄͡嘦扯ͭΞ͜ • 㶨奈ϔϝαφ΄ϔЄόΨᭆΡͶͧͽ͘ΆDevice Shadow; ΅吖ΡϕϡϐμͽԪ᪃ΠΡ •
AWS IoT΄ϮϐψЄυϣϺЄθЄΞͼձ΄ϕϡϐμͽ Publish/Subscribeݢᚆ 21
ίυδЀύ 1. Ϳ͚͜͵ದ悬Ψֵͼ͚Ρ΄͡ 2. ίϤϷ͡ΟIoTϔϝαφ΄֢०䤂ͯΡ㺔氂 3. ᭗Ꭳͭ͵͚樌ᵍ͢ͰΡ㺔氂 ! 4. AWS
IoT΄ϓφϕͿͯ͜Ρ͡㺔氂 22
Ϳ͚͜͵抓氂͢ਂࣁͭ͵΄͡ • ͘ΡկΨ伋͵ͭ͵䁰ݳ̵ͳ΄կΨ伋͵ͭͼ͡Οᇙਧ΄樌ᵍ䶅᭗Ꭳͫͱ͵ ͡͵ • ٍ֛ጱ͚͜;̵IoTϔϝαφ吖ଉ͢咲ኞͭ͵ΟϷϫαЀϖ᭗ᎣΨͯΡ;͚͜ Θ΄ • ֺ͞Ά̵吖ଉ咲ኞ5ړ䶅ϳЄσΎϷϫЀϖ᭗ᎣͯΡ •
吖ଉϔЄό΄ᇫ䙪ᓕቘDynamoDBΨڥአͭͼ͚Ρ • ϷϫαЀϖ᭗ᎣΞͼϳЄσ͢吖ଉΨᥴၾͯΡΔͽਧࢧහΔͽ媗Πᬬͭ᭗ Ꭳͭ姆ͧΡᥝ͘Π 23
吖ଉϔЄό͢DynamoDBͥΔͽ SELECT *, topic(3) as deviceId FROM '$aws/things/+/shadow/update' WHERE state.reported.someIllegalState
= True 24
୮䦒΄ᔰίЄκϓμώϰ 25
CloudWatch Events΄φξυϲЄϸΨڥአͭ ͵ϷϫαЀϖ᭗Ꭳ 26
抓氂 • CloudWatch Events΅吖ଉ͢咲ኞͭ͵͡Ϳ͜͡ൎͽ͚ͣ • ͳ΄͵Η̵ଉᇙਧ΄樌ᵍ䶅Lambda樛හΨ᩸㵕ͫͱͼͭΔ ͜ • ͺΔΠ̵僻洏抓ᰂ͢咲ኞͯΡ •
Δ͵̵ 吖ଉ͢咲ኞͭͼ͡Οᇙਧ樌ᵍ䶅 ᭗Ꭳ͢ͽ͚ͣ 27
ϷϫαЀϖ᭗Ꭳ͢ͰΡϞόЄЀ 28
Ϳ͜ᥴ䷥ͭ͵΄͡ 29
DynamoDB Streams + Step Functions 30
ίЄκϓμώϰ • 吖ଉᓕቘϓЄϣϸϔЄό͢PutItemͫ ͵ΟDynamoDB StreamsͽLambda樛 හΨ᩸㵕 • ͳ΄Lambda樛හ͢Step FunctionsΨ᩸㵕 •
๋ڡ5ړ樌Wait̵ϷϫαЀϖڣਧ Lambda樛හΨ᩸㵕ͭ᭗ᎣͯΏͣ͡ڣਧ • ᭗ᎣͯΏͣ䁰ݳ΅εЀϖϳЄσϷϫα Ѐϖ • ͩΨਧࢧහ媗Πᬬͯ 31
ϸЄϤࢧහ΄ڡ๗戔ਧ ... "States": { "ConfigureRemindCount": { "Type": "Pass", "Result": {
"count": 5, "index": 0, "step": 1 }, "ResultPath": "$.iterator", "Next": "WaitAMinutes" }, "WaitAMinutes": { "Type": "Wait", "Seconds": 300, "Next": "RemindIterator" }, 32
ਧ䦒樌Wait ... "States": { "ConfigureRemindCount": { "Type": "Pass", "Result": {
"count": 5, "index": 0, "step": 1 }, "ResultPath": "$.iterator", "Next": "WaitAMinutes" }, "WaitAMinutes": { "Type": "Wait", "Seconds": 300, "Next": "RemindIterator" }, 33
ϷϫαЀϖ΄ڣਧ ... "RemindIterator": { "Type": "Task", "Resource": "<_LAMBDA_ARN_>", "ResultPath": "$.iterator",
"Next": "IsRemindCountReached" }, "IsRemindCountReached": { "Type": "Choice", "Choices": [ { "Variable": "$.iterator.continue", "BooleanEquals": true, "Next": "Remind" } ], "Default": "Done" }, 34
ϷϫαЀϖڣਧ΄πЄϖ ... def handler(event, context): iterator = event['iterator'] dynamodb =
boto3.resource('dynamodb').Table(os.environ['TABLE_NAME']) result = dynamodb.get_item(Key={'yourKey': event['yourValue']}) if not result.get('Item'): return {'continue': False} if iterator['index'] <= iterator['count']: continue_ = True else: continue_ = False return { 'index': iterator['index'] + iterator['step'], 'step': iterator['step'], 'count': iteratoro['count'], 'continue': continue_ } 35
ϸЄϤΨ姅姆ͯΡ͡ڣਧ ... "RemindIterator": { "Type": "Task", "Resource": "<_LAMBDA_ARN_>", "ResultPath": "$.iterator",
"Next": "IsRemindCountReached" }, "IsRemindCountReached": { "Type": "Choice", "Choices": [ { "Variable": "$.iterator.continue", "BooleanEquals": true, "Next": "Remind" } ], "Default": "Done" }, 36
ϷϫαЀϖ᭗Ꭳ ... "Remind": { "Type": "Task", "Resource": "<_LAMBDA_ARN_>", "Next": "WaitAMinutes"
}, "Done": { "Type": "Pass", "End": true } } } 37
ϸЄϤ΄奰ԧ ... "Remind": { "Type": "Task", "Resource": "<_LAMBDA_ARN_>", "Next": "WaitAMinutes"
}, "Done": { "Type": "Pass", "End": true } } } 38
ίυδЀύ 1. Ϳ͚͜͵ದ悬Ψֵͼ͚Ρ΄͡ 2. ίϤϷ͡ΟIoTϔϝαφ΄֢०䤂ͯΡ㺔氂 3. ᭗Ꭳͭ͵͚樌ᵍ͢ͰΡ㺔氂 4. AWS IoT΄ϓφϕͿͯ͜Ρ͡㺔氂
39
抓氂 • AWS IoT΄ഄΡᛩ͚Ψϓφϕͭ͵͚ • ͽΘͿ͜䨗ͥ΄͚͚͢΄͡ړ͡ΟΩ • ͩ΄͘͵Π΄ఘ䁭΅͘ΔΠ͚Ҙ • ಋറΠͽ͢ΩΆΡφόαϸ
" 40
Ք΅ͩΩఽͮͽϓφϕ䨗͚ͼΔͯ 41
ϓφϕڥአͭͼ͚ΡϯυϲЄϸ • ϓφϕϢϹЄϭϼЄμ: pytest5 • 䰤伛΄unittestϯυϲЄϸΞΠṛ䱛ᚆΓ͚΄ͽ • AWS IoT΄ϯϐμ: moto4
• E2E: AWS IoT Device SDK for Python6 • paho7ΞΠು۸ͫͼ͚ͼֵ͚Κͯ͡͵ 7 http://www.eclipse.org/paho/ 6 https://github.com/aws/aws-iot-device-sdk-python 4 https://github.com/spulec/moto 5 https://github.com/pytest-dev/pytest 42
ϳϘϐϕϓφϕ 43
ϳϘϐϕϓφϕ΄䜐ኼ • motoΨڥአͭͼBoto3΄IoT8/IoTDataPlane9Ψϯϐμ۸ • pytest΄ conftest.py ͽfixutreΨਧ嬝ͭͼͥ͠ • ݱ圵ϓφϕξЄφͽfixtureΨΉڊͭͼϓφϕկΨ戔ਧͯΡ •
ϓφϕξЄφΨϞ϶ϮЄό۸ͭͼτЀϤϸ懿ᬿͽϓφϕθϝ ϹϐυΨṛΗΡ 9 http://boto3.readthedocs.io/en/latest/reference/services/iot-data.html 8 http://boto3.readthedocs.io/en/latest/reference/services/iot.html 44
ϓφϕ䌏΄Ϯϊϐϖ • ᇙਧ΄Thing͢ਂࣁͯΡ͡ڣਧͭͼ͚ΡͶͧ class IoTHandler(object): ... def _does_thing_exist(self, thing_name): response
= self.iot.describe_thing(thingName=thing_name) if thing_name == response.get('thingName'): return True else: return False 45
ϳϘϐϕϓφϕ΄πЄϖ class TestDoesThingExist(object): @pytest.mark.usefixtures('create_thing') @pytest.mark.parametrize( 'thing_name, expected', [ ('my_thing_01', True),
('my_thing_02', False) ]) def test_some_things_exist(self, thing_name, expected): actual = IoTHandler()._does_thing_exist(thing_name) assert actual is expected 46
ϓφϕ䌏΄Ϯϊϐϖ • Boto3΄ഄΡᛩ͚Ψϯϐμͫͱ͵͚ • Δ͵̵Thingͯ͢ͽਂࣁͭͼ͚Ρᇫ丆Ψ֢Π͵͚ class IoTHandler(object): ... def _does_thing_exist(self,
thing_name): response = self.iot.describe_thing(thingName=thing_name) if thing_name == response.get('thingName'): return True else: return False 47
ϳϘϐϕϓφϕ΄πЄϖ • ϓφϕͭ͵͚ڹկ(Thingͯ͢ͽਂࣁͭͼ͚Ρ)Ψෆ͞Ρ class TestDoesThingExist(object): @pytest.mark.usefixtures('create_thing') @pytest.mark.parametrize( 'thing_name, expected', [
('my_thing_01', True), ('my_thing_02', False) ]) def test_some_things_exist(self, thing_name, expected): actual = IoTHandler()._does_thing_exist(thing_name) assert actual is expected 48
ThingΨԪڹ֢౮( conftest.py ) from moto import mock_iot @pytest.fixture(scope='function') def create_thing(request):
mock = mock_iot() mock.start() iot = boto3.client('iot') iot.create_thing(thingName='my_thing_01') request.addfinalizer(lambda: mock.stop()) 49
ϓφϕ䌏΄Ϯϊϐϖ • Thing͢ਂࣁͯΡ͡ڣਧͯΡړઓΨϓφϕͭ͵͚ class IoTHandler(object): ... def _does_thing_exist(self, thing_name): response
= self.iot.describe_thing(thingName=thing_name) if thing_name == response.get('thingName'): return True else: return False 50
ϳϘϐϕϓφϕ • ϓφϕͭ͵͚ξЄφΨϞ϶ϮЄό۸ class TestDoesThingExist(object): @pytest.mark.usefixtures('create_thing') @pytest.mark.parametrize( 'thing_name, expected', [
('my_thing_01', True), ('my_thing_02', False) ]) def test_some_things_exist(self, thing_name, expected): actual = IoTHandler()._does_thing_exist(thing_name) assert actual is expected 51
ϳϘϐϕϓφϕ΄πЄϖ • 奾ຎΨϓφϕ class TestDoesThingExist(object): @pytest.mark.usefixtures('create_thing') @pytest.mark.parametrize( 'thing_name, expected', [
('my_thing_01', True), ('my_thing_02', False) ]) def test_some_things_exist(self, thing_name, expected): actual = IoTHandler()._does_thing_exist(thing_name) assert actual is expected 52
E2Eϓφϕ 53
E2Eϓφϕ΄᯿ᥝ • ᇙςЄϝЄϹφίϤϷξЄτϴЀͽ ΅E2Eϓφϕ΅᯿ᥝ10 • ϡόρ϶φαϐώΠ͢ͷ΄ͽ̵ ݱ圵AWSϷϊЄφ͢ྋͭͥ昧൭ͭͼ͚ Ρ͡ϓφϕͭͼͥ͠ᥝ͘͢Ρ • ϓφϕ΄ϢΰЄϖϝϐμ͢ग़昼ͥ
ͼΘΚΡ㭅㮔΅͘Ρ 10 https://www.slideshare.net/theburningmonk/serverless-in-production- an-experience-report-jeffconf-88207165/78 54
E2Eϓφϕ΄䜐ኼ • ϳϘϐϕϓφϕ(;͚͜͡moto)ͽ΅䌏䖕ͽ͚ͣ塅㾨Ψϓφϕ ͯΡ • AWS IoT樛ͭͼ͚͜;AWS IoT Rule Engine΄ഄΡᛩ͚
• fixtureͽAWS IoT Device SDK for PythonΨ̵ֵ͚ϮϐψЄυΨ Publishͫͱͼͥ͠ • 奾ຎΨώδϐμͭͼϓφϕ 55
ϓφϕͭ͵͚AWSϷϊЄφ • DynamoDB吖ଉϔЄό͢PutItemͫΡͩ;Ψ嘦扯ͭ͵͚ 56
ϓφϕොဩ 57
ϮϐψЄυ΄Publish @pytest.mark.parametrize( 'publish_message', [ (({ 'state': { 'reported': { 'someIllegalState':
True } } },)) ], indirect=True) def test_some_illegal_states_occured(publish_message): ... SELECT *, topic(3) as device_id FROM '$aws/things/+/shadow/update' WHERE state.reported.someIllegalState = True 58
conftest.py @pytest.fixture(scope='function') def publish_message(request): for message in request.param: publish2topic(iot_endpoint='_IOT_ENDPOINT_', topic=f'$aws/things/{my_thing}/shadow/update',
message=message, iot_credentials='_IOT_CREDENTIALS_') time.sleep(10) def publish2topic(iot_endpoint, topic, message, iot_credentials): mqtt = AWSIoTMQTTClient('api-test') mqtt.configureEndpoint(iot_endpoint, 8883) mqtt.configureCredentials(iot_credentials['root_ca'], iot_credentials['private_key'], iot_credentials['certificate']) mqtt.connect() mqtt.publish(topic, json.dumps(message), 1) 59
Δ;Η • ͚ͥͺ͡΄抓氂;ᥴ䷥ොဩΨͪ奧Օ • ΔͶΔͶද࠺΄֟ࣈ΅͘Πͳ͜ • ͣ姆ͣ毇䔴ͼ͚ͥφόαϸ 60