Skip to content

Commit 36bd752

Browse files
Green grass support (#17)
Greengrass support, updated crt version. We now can safely rely on pip installation of awscrt
1 parent e44c19d commit 36bd752

File tree

11 files changed

+372
-22
lines changed

11 files changed

+372
-22
lines changed

README.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,17 @@ for a stable development environment.
1212
This SDK is built on the AWS Common Runtime, a collection of libraries
1313
([1](https://github.com/awslabs/aws-c-common),
1414
[2](https://github.com/awslabs/aws-c-io),
15-
[3](https://github.com/awslabs/aws-c-mqtt), ...) written in C to be
15+
[3](https://github.com/awslabs/aws-c-mqtt),
16+
[4](https://github.com/awslabs/aws-c-http),
17+
[5](https://github.com/awslabs/aws-c-cal) ...) written in C to be
1618
cross-platform, high-performance, secure, and reliable. The libraries are bound
1719
to Python by the [awscrt](https://github.com/awslabs/aws-crt-python) package.
1820

21+
The awscrt package can be installed via. pip
22+
```
23+
pip install awscrt
24+
```
25+
1926
Integration with AWS IoT Services such as
2027
[Device Shadow](https://docs.aws.amazon.com/iot/latest/developerguide/iot-device-shadows.html)
2128
and [Jobs](https://docs.aws.amazon.com/iot/latest/developerguide/iot-jobs.html)
@@ -24,14 +31,14 @@ is provided by code that been generated from a model of the service.
2431
# Installation
2532
## Minimum Requirements
2633
* Python 3.5+ or Python 2.7+
27-
* CMake 3.1+
28-
* Clang 3.9+ or GCC 4.4+ or MSVC 2015+
34+
35+
## Install from pypi
36+
```
37+
pip install awsiot
38+
```
2939

3040
## Build from source
3141
```
32-
git clone --branch v0.2.0 https://github.com/awslabs/aws-crt-python.git --recursive
33-
git clone https://github.com/awslabs/aws-iot-device-sdk-python-v2.git
34-
pip install ./aws-crt-python
3542
pip install ./aws-iot-device-sdk-python-v2
3643
```
3744

@@ -266,6 +273,11 @@ and receive.
266273
</pre>
267274
</details>
268275

276+
## basic discovery
277+
278+
This sample intended for use directly with the
279+
[Getting Started with AWS IoT Greengrass](https://docs.aws.amazon.com/greengrass/latest/developerguide/gg-gs.html) guide.
280+
269281
# License
270282

271283
This library is licensed under the Apache 2.0 License.

awsiot/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
__all__ = [
1515
'iotjobs',
1616
'iotshadow',
17+
'greengrass_discovery',
1718
]
1819

19-
from aws_crt import mqtt
20+
from awscrt import mqtt
2021
from concurrent.futures import Future
2122
import json
2223
from typing import Any, Callable, Dict, Optional, Tuple, TypeVar

awsiot/greengrass_discovery.py

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License").
4+
# You may not use this file except in compliance with the License.
5+
# A copy of the License is located at
6+
#
7+
# http://aws.amazon.com/apache2.0
8+
#
9+
# or in the "license" file accompanying this file. This file is distributed
10+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
# express or implied. See the License for the specific language governing
12+
# permissions and limitations under the License.
13+
14+
from awscrt.http import HttpClientConnection, HttpRequest
15+
from awscrt import io
16+
from awscrt.io import ClientBootstrap, ClientTlsContext, TlsConnectionOptions, SocketOptions
17+
import awsiot
18+
from concurrent.futures import Future
19+
import json
20+
21+
class DiscoveryClient(object):
22+
__slots__ = ['_bootstrap', '_tls_context', '_socket_options', '_region', '_tls_connection_options', '_gg_server_name', 'gg_url', 'port']
23+
24+
def __init__(self, bootstrap, socket_options, tls_context, region):
25+
assert bootstrap is not None and isinstance(bootstrap, ClientBootstrap)
26+
assert socket_options is not None and isinstance(socket_options, SocketOptions)
27+
assert tls_context is not None and isinstance(tls_context, ClientTlsContext)
28+
assert region is not None and isinstance(region, str)
29+
30+
self._bootstrap = bootstrap
31+
self._socket_options = socket_options
32+
self._region = region
33+
self._gg_server_name = 'greengrass-ats.iot.{}.amazonaws.com'.format(region)
34+
self._tls_connection_options = tls_context.new_connection_options()
35+
self._tls_connection_options.set_server_name(self._gg_server_name)
36+
self.port = 8443
37+
38+
if io.is_alpn_available():
39+
self._tls_connection_options.set_alpn_list('x-amzn-http-ca')
40+
self.port = 443
41+
42+
def discover(self, thing_name):
43+
ret_future = Future()
44+
response_body = bytearray()
45+
request = None
46+
47+
def on_incoming_body(response_chunk):
48+
response_body.extend(response_chunk)
49+
50+
def on_request_complete(completion_future):
51+
global request
52+
try:
53+
response_code = request.response_code
54+
# marking request as global prevents the GC from reclaiming it,
55+
# so force it to do it here.
56+
request = None
57+
if response_code == 200:
58+
payload_str = response_body.decode('utf-8')
59+
discover_res = DiscoverResponse.from_payload(json.loads(payload_str))
60+
ret_future.set_result(discover_res)
61+
else:
62+
ret_future.set_exception(DiscoveryException('Error during discover call: response code ={}'.format(response_code), response_code))
63+
64+
except Exception as e:
65+
ret_future.set_exception(e)
66+
67+
def on_connection_completed(conn_future):
68+
global request
69+
try:
70+
connection = conn_future.result()
71+
request = connection.make_request(
72+
method='GET',
73+
uri_str='/greengrass/discover/thing/{}'.format(thing_name),
74+
outgoing_headers={'host':self._gg_server_name},
75+
on_outgoing_body=None,
76+
on_incoming_body=on_incoming_body)
77+
78+
request.response_completed.add_done_callback(on_request_complete)
79+
80+
except Exception as e:
81+
# marking request as global prevents the GC from reclaiming it,
82+
# so force it to do it here.
83+
request = None
84+
ret_future.set_exception(e)
85+
86+
connect_future = HttpClientConnection.new_connection(self._bootstrap, self._gg_server_name, self.port, self._socket_options, None, self._tls_connection_options)
87+
connect_future.add_done_callback(on_connection_completed)
88+
89+
return ret_future
90+
91+
class DiscoveryException(Exception):
92+
_slots_ = ['http_response_code', 'message']
93+
94+
def __init__(self, message, response_code):
95+
self.http_response_code = response_code
96+
self.message = message
97+
98+
99+
class ConnectivityInfo(awsiot.ModeledClass):
100+
__slots__ = ['id', 'host_address', 'metadata', 'port']
101+
102+
def ___init___(self):
103+
for slot in self.__slots__:
104+
setattr(self, slot, None)
105+
106+
@classmethod
107+
def from_payload(cls, payload):
108+
# type: (typing.Dict[str, typing.Any]) -> ConnectivityInfo
109+
new = cls()
110+
val = payload.get('Id')
111+
if val is not None:
112+
new.id = val
113+
val = payload.get('HostAddress')
114+
if val is not None:
115+
new.host_address = val
116+
val = payload.get('PortNumber')
117+
if val is not None:
118+
new.port = val
119+
val = payload.get('Metadata')
120+
if val is not None:
121+
new.metadata = val
122+
return new
123+
124+
class GGCore(awsiot.ModeledClass):
125+
__slots__ = ['thing_arn', 'connectivity']
126+
127+
def ___init___(self):
128+
for slot in self.__slots__:
129+
setattr(self, slot, None)
130+
131+
@classmethod
132+
def from_payload(cls, payload):
133+
# type: (typing.Dict[str, typing.Any]) -> GGCore
134+
new = cls()
135+
val = payload.get('thingArn')
136+
if val is not None:
137+
new.thing_arn = val
138+
val = payload.get('Connectivity')
139+
if val is not None:
140+
new.connectivity = [ConnectivityInfo.from_payload(i) for i in val]
141+
142+
return new
143+
144+
class GGGroup(awsiot.ModeledClass):
145+
__slots__ = ['gg_group_id', 'cores', 'certificate_authorities']
146+
147+
def ___init___(self):
148+
for slot in self.__slots__:
149+
setattr(self, slot, None)
150+
151+
@classmethod
152+
def from_payload(cls, payload):
153+
# type: (typing.Dict[str, typing.Any]) -> GGGroup
154+
new = cls()
155+
val = payload.get('GGGroupId')
156+
if val is not None:
157+
new.gg_group_id = val
158+
val = payload.get('Cores')
159+
if val is not None:
160+
new.cores = [GGCore.from_payload(i) for i in val]
161+
val = payload.get('CAs')
162+
if val is not None:
163+
new.certificate_authorities = val
164+
165+
return new
166+
167+
class DiscoverResponse(awsiot.ModeledClass):
168+
__slots__ = ['gg_groups']
169+
170+
def ___init___(self):
171+
for slot in self.__slots__:
172+
setattr(self, slot, None)
173+
174+
@classmethod
175+
def from_payload(cls, payload):
176+
# type: (typing.Dict[str, typing.Any]) -> DiscoverResponse
177+
new = cls()
178+
val = payload.get('GGGroups')
179+
if val is not None:
180+
new.gg_groups = [GGGroup.from_payload(i) for i in val]
181+
182+
return new

awsiot/iotjobs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
# This file is generated
1515

16-
import aws_crt.mqtt
16+
import awscrt.mqtt
1717
import awsiot
1818
import concurrent.futures
1919
import datetime

awsiot/iotshadow.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
# This file is generated
1515

16-
import aws_crt.mqtt
16+
import awscrt.mqtt
1717
import awsiot
1818
import concurrent.futures
1919
import datetime
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
version: 0.2
2+
#this build spec assumes the ubuntu 14.04 trusty image
3+
phases:
4+
install:
5+
commands:
6+
- sudo add-apt-repository ppa:ubuntu-toolchain-r/test
7+
- sudo apt-get update -y
8+
- sudo apt-get install python3 python3-pip python python-pip -y
9+
- pip3 install --upgrade setuptools
10+
- pip3 install --upgrade pip
11+
- pip install --upgrade setuptools
12+
- pip install --upgrade pip
13+
pre_build:
14+
commands:
15+
- curl https://www.amazontrust.com/repository/AmazonRootCA1.pem --output /tmp/AmazonRootCA1.pem
16+
- cert=$(aws secretsmanager get-secret-value --secret-id "unit-test/certificate" --query "SecretString" | cut -f2 -d":" | cut -f2 -d\") && echo "$cert" > /tmp/certificate.pem
17+
- key=$(aws secretsmanager get-secret-value --secret-id "unit-test/privatekey" --query "SecretString" | cut -f2 -d":" | cut -f2 -d\") && echo "$key" > /tmp/privatekey.pem
18+
- ENDPOINT=$(aws secretsmanager get-secret-value --secret-id "unit-test/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g')
19+
build:
20+
commands:
21+
- echo Build started on `date`
22+
- pip3 install ./
23+
- python3 samples/basic_discovery.py --region us-east-1 --cert /tmp/certificate.pem --key /tmp/privatekey.pem --ca_file /tmp/AmazonRootCA1.pem --thing_name aws-sdk-crt-unit-test --print_discover_resp_only -v Trace
24+
- pip install ./
25+
- python samples/basic_discovery.py --region us-east-1 --cert /tmp/certificate.pem --key /tmp/privatekey.pem --ca_file /tmp/AmazonRootCA1.pem --thing_name aws-sdk-crt-unit-test --print_discover_resp_only -v Trace
26+
post_build:
27+
commands:
28+
- echo Build completed on `date`
29+

0 commit comments

Comments
 (0)