Skip to content

Commit 37d869c

Browse files
authored
Merge pull request #5 from awslabs/development
Release 2.0.0 to support vending multiple tokens and dashboard
2 parents 3ce8612 + 30f1023 commit 37d869c

12 files changed

+539
-214
lines changed

.github/workflows/CI.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,4 @@ jobs:
4040
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
4141
- name: Testing CLI (Runs both unit and integration tests)
4242
run: |
43-
coverage run --source=src -m pytest -v -s . && coverage report --show-missing --fail-under=63
43+
coverage run --source=src -m pytest -v -s . && coverage report --show-missing --fail-under=75

README.md

+163-100
Large diffs are not rendered by default.

gdk-config.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"component" :{
33
"aws.greengrass.labs.database.InfluxDB": {
44
"author": "AWS IoT Greengrass",
5-
"version": "NEXT_PATCH",
5+
"version": "2.0.0",
66
"build": {
77
"build_system": "zip"
88
},

recipe.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
RecipeFormatVersion: '2020-01-25'
33
ComponentName: aws.greengrass.labs.database.InfluxDB
4-
ComponentVersion: '1.0.0'
4+
ComponentVersion: '2.0.0'
55
ComponentDescription: 'A component that provisions and manages an InfluxDB instance.'
66
ComponentPublisher: Amazon
77
ComponentDependencies:
@@ -15,7 +15,7 @@ ComponentConfiguration:
1515
DefaultConfiguration:
1616
AutoProvision: 'true'
1717
InfluxDBMountPath: '/home/ggc_user/dashboard'
18-
SecretArn: 'arn:aws:secretsmanager:<region>:<account>:secret:<name>'
18+
SecretArn: 'arn:aws:secretsmanager:region:account:secret:name'
1919
InfluxDBContainerName: greengrass_InfluxDB
2020
InfluxDBOrg: 'greengrass'
2121
InfluxDBBucket: 'greengrass-telemetry'
@@ -48,7 +48,7 @@ ComponentConfiguration:
4848
operations:
4949
- aws.greengrass#GetSecretValue
5050
resources:
51-
- 'arn:aws:secretsmanager:<region>:<account>:secret:<name>'
51+
- 'arn:aws:secretsmanager:region:account:secret:name'
5252
Manifests:
5353
- Platform:
5454
os: /darwin|linux/

src/influxDBTokenPublisher.py

+24-23
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919

2020
logging.basicConfig(level=logging.INFO)
2121
TIMEOUT = 10
22+
# Influx commands need to be given the port of InfluxDB inside the container, which is always 8086 unless
23+
# overridden inside the InfluxDB config
24+
INFLUX_CONTAINER_PORT = 8086
2225

2326

2427
def parse_arguments() -> Namespace:
@@ -47,7 +50,7 @@ def parse_arguments() -> Namespace:
4750
return parser.parse_args()
4851

4952

50-
def retrieve_influxDB_token(args) -> str:
53+
def retrieve_influxDB_token_json(args) -> str:
5154
"""
5255
Retrieve the created RW token from InfluxDB.
5356
@@ -61,11 +64,10 @@ def retrieve_influxDB_token(args) -> str:
6164
"""
6265

6366
token_json = ""
64-
dockerExecProcess = ""
6567
authListCommand = ['docker', 'exec', '-t', args.influxdb_container_name, 'influx', 'auth', 'list', '--json']
6668
if args.server_protocol == "https":
6769
authListCommand.append('--host')
68-
authListCommand.append('https://{}:{}'.format(args.influxdb_container_name, args.influxdb_port))
70+
authListCommand.append('https://{}:{}'.format(args.influxdb_container_name, INFLUX_CONTAINER_PORT))
6971

7072
if bool(strtobool(args.skip_tls_verify)):
7173
authListCommand.append('--skip-verify')
@@ -78,48 +80,47 @@ def retrieve_influxDB_token(args) -> str:
7880
if dockerExecProcess.stderr:
7981
logging.error(dockerExecProcess.stderr)
8082
if(len(token_json) == 0):
81-
logging.error('Failed to retrieve InfluxDB RW token data from Docker! Retrieved token was: {}'.format(token_json))
83+
logging.error('Failed to retrieve InfluxDB RW token data from Docker! Retrieved data was: {}'.format(token_json))
8284
exit(1)
83-
influxdb_rw_token = next(d for d in json.loads(token_json) if d['description'] == 'greengrass_readwrite')['token']
84-
if(len(influxdb_rw_token) == 0):
85-
logging.error('Failed to parse InfluxDB RW token! Retrieved token was: {}'.format(influxdb_rw_token))
85+
influxdb_token = json.loads(token_json)[0]['token']
86+
if(len(influxdb_token) == 0):
87+
logging.error('Retrieved InfluxDB tokens was empty!')
8688
exit(1)
8789

88-
return influxdb_rw_token
90+
return token_json
8991

9092

91-
def listen_to_token_requests(args, influxdb_rw_token) -> None:
93+
def listen_to_token_requests(args, influxdb_token_json) -> None:
9294
"""
9395
Setup a new IPC subscription over local pub/sub to listen to token requests and vend tokens.
9496
9597
Parameters
9698
----------
9799
args(Namespace): Parsed arguments
98-
influxdb_rw_token(str): InfluxDB RW token
100+
influxdb_token_json(str): InfluxDB token JSON string
99101
100102
Returns
101103
-------
102104
None
103105
"""
104106

105107
try:
106-
influxDB_data = {}
107-
influxDB_data['InfluxDBContainerName'] = args.influxdb_container_name
108-
influxDB_data['InfluxDBOrg'] = args.influxdb_org
109-
influxDB_data['InfluxDBBucket'] = args.influxdb_bucket
110-
influxDB_data['InfluxDBPort'] = args.influxdb_port
111-
influxDB_data['InfluxDBInterface'] = args.influxdb_interface
112-
influxDB_data['InfluxDBRWToken'] = influxdb_rw_token
113-
influxDB_data['InfluxDBServerProtocol'] = args.server_protocol
114-
influxDB_data['InfluxDBSkipTLSVerify'] = args.skip_tls_verify
115-
influxDB_json = json.dumps(influxDB_data)
108+
influxdb_metadata = {}
109+
influxdb_metadata['InfluxDBContainerName'] = args.influxdb_container_name
110+
influxdb_metadata['InfluxDBOrg'] = args.influxdb_org
111+
influxdb_metadata['InfluxDBBucket'] = args.influxdb_bucket
112+
influxdb_metadata['InfluxDBPort'] = args.influxdb_port
113+
influxdb_metadata['InfluxDBInterface'] = args.influxdb_interface
114+
influxdb_metadata['InfluxDBServerProtocol'] = args.server_protocol
115+
influxdb_metadata['InfluxDBSkipTLSVerify'] = args.skip_tls_verify
116+
influxdb_metadata_json = json.dumps(influxdb_metadata)
116117

117118
logging.info('Successfully retrieved InfluxDB parameters!')
118119

119120
ipc_client = awsiot.greengrasscoreipc.connect()
120121
request = SubscribeToTopicRequest()
121122
request.topic = args.subscribe_topic
122-
handler = InfluxDBTokenStreamHandler(influxDB_json, args.publish_topic)
123+
handler = InfluxDBTokenStreamHandler(influxdb_metadata_json, influxdb_token_json, args.publish_topic)
123124
operation = ipc_client.new_subscribe_to_topic(handler)
124125
operation.activate(request)
125126
logging.info('Successfully subscribed to topic: {}'.format(args.subscribe_topic))
@@ -138,8 +139,8 @@ def listen_to_token_requests(args, influxdb_rw_token) -> None:
138139
if __name__ == "__main__":
139140
try:
140141
args = parse_arguments()
141-
influxdb_rw_token = retrieve_influxDB_token(args)
142-
listen_to_token_requests(args, influxdb_rw_token)
142+
influxdb_token_json = retrieve_influxDB_token_json(args)
143+
listen_to_token_requests(args, influxdb_token_json)
143144
# Keep the main thread alive, or the process will exit.
144145
while True:
145146
time.sleep(10)

src/influxDBTokenStreamHandler.py

+53-14
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,28 @@
33

44
import concurrent.futures
55
import logging
6-
6+
import json
77
import awsiot.greengrasscoreipc
88
import awsiot.greengrasscoreipc.client as client
99
from awsiot.greengrasscoreipc.model import (
1010
PublishToTopicRequest,
1111
PublishMessage,
12-
BinaryMessage,
12+
JsonMessage,
1313
SubscriptionResponseMessage,
1414
UnauthorizedError
1515
)
1616

1717
TIMEOUT = 10
18+
# Admin token description is in the format "USERNAME's Token"
19+
ADMIN_TOKEN_IDENTIFIER = "'s Token"
1820

1921

2022
class InfluxDBTokenStreamHandler(client.SubscribeToTopicStreamHandler):
21-
def __init__(self, influxDB_json, publish_topic):
23+
def __init__(self, influxdb_metadata_json, influxdb_token_json, publish_topic):
2224
super().__init__()
2325
# We need a separate IPC client for publishing
24-
self.influxDB_json = influxDB_json
26+
self.influxDB_metadata_json = influxdb_metadata_json
27+
self.influxDB_token_json = influxdb_token_json
2528
self.publish_topic = publish_topic
2629
self.publish_client = awsiot.greengrasscoreipc.connect()
2730
logging.info("Initialized InfluxDBTokenStreamHandler")
@@ -39,12 +42,12 @@ def handle_stream_event(self, event: SubscriptionResponseMessage) -> None:
3942
None
4043
"""
4144
try:
42-
message = str(event.binary_message.message, "utf-8")
43-
if message == 'GetInfluxDBData':
44-
logging.info('Sending InfluxDB RW Token on the response topic')
45-
self.publish_response()
46-
else:
47-
logging.warning('Unknown request type received over pub/sub')
45+
message = event.json_message.message
46+
publish_json = self.get_publish_json(message)
47+
if not publish_json:
48+
logging.error("Failed to construct requested response for access")
49+
return
50+
self.publish_response(publish_json)
4851
except Exception:
4952
logging.error('Received an error', exc_info=True)
5053

@@ -80,13 +83,49 @@ def on_stream_closed(self) -> None:
8083
"""
8184
logging.info('Subscribe to topic stream closed.')
8285

83-
def publish_response(self) -> None:
86+
def get_publish_json(self, message):
87+
"""
88+
Parse the correct token based on the IPC message received, and construct the final JSON to publish.
89+
90+
:param message: the received IPC messsage
91+
:return: the complete JSON, including token, to publish
92+
"""
93+
94+
loaded_token_json = json.loads(self.influxDB_token_json)
95+
publish_json = json.loads(self.influxDB_metadata_json)
96+
97+
if not message['action'] == 'RetrieveToken':
98+
logging.warning('Unknown request type received over pub/sub')
99+
return None
100+
101+
token = ''
102+
if message['accessLevel'] == 'RW':
103+
token = next(d for d in loaded_token_json if d['description'] == 'greengrass_readwrite')['token']
104+
elif message['accessLevel'] == 'RO':
105+
token = next(d for d in loaded_token_json if d['description'] == 'greengrass_read')['token']
106+
elif message['accessLevel'] == 'Admin':
107+
if not ADMIN_TOKEN_IDENTIFIER in loaded_token_json[0]['description']:
108+
logging.warning("InfluxDB admin token is missing or in an incorrect format")
109+
return None
110+
token = loaded_token_json[0]['token']
111+
else:
112+
logging.warning('Unknown token request type specified over pub/sub')
113+
return None
114+
115+
if len(token) == 0:
116+
raise ValueError('Failed to parse InfluxDB {} token!'.format(message['accessLevel']))
117+
publish_json['InfluxDBTokenAccessType'] = message['accessLevel']
118+
publish_json['InfluxDBToken'] = token
119+
logging.info('Sending InfluxDB {} Token on the response topic'.format(message['accessLevel']))
120+
return publish_json
121+
122+
def publish_response(self, publishMessage) -> None:
84123
"""
85124
Publish the InfluxDB token on the token response topic.
86125
87126
Parameters
88127
----------
89-
None
128+
publishMessage(str): the message to send including InfluxDB metadata and token
90129
91130
Returns
92131
-------
@@ -96,8 +135,8 @@ def publish_response(self) -> None:
96135
request = PublishToTopicRequest()
97136
request.topic = self.publish_topic
98137
publish_message = PublishMessage()
99-
publish_message.binary_message = BinaryMessage()
100-
publish_message.binary_message.message = bytes(self.influxDB_json, "utf-8")
138+
publish_message.json_message = JsonMessage()
139+
publish_message.json_message.message = publishMessage
101140
request.publish_message = publish_message
102141
operation = self.publish_client.new_publish_to_topic()
103142
operation.activate(request)

0 commit comments

Comments
 (0)