mirror of https://github.com/knative/docs.git
Add files via upload
This commit is contained in:
parent
73a2cae3e4
commit
2bb9073b0d
|
@ -0,0 +1,117 @@
|
||||||
|
# Demo: Binding running services to an IoT core (PubSub)
|
||||||
|
|
||||||
|
> For the ease of the demo, a few variables here are hard-coded.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Define environment variables:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
export IOTCORE_PROJECT="s9-demo"
|
||||||
|
export IOTCORE_REG="next18-demo"
|
||||||
|
export IOTCORE_DEVICE="next18-demo-client"
|
||||||
|
export IOTCORE_REGION="us-central1"
|
||||||
|
export IOTCORE_TOPIC_DATA="iot-demo"
|
||||||
|
export IOTCORE_TOPIC_DEVICE="iot-demo-device"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Creating a device registry
|
||||||
|
|
||||||
|
Run the following command to create a device registry:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
gcloud iot registries create $IOTCORE_REG \
|
||||||
|
--project=$IOTCORE_PROJECT \
|
||||||
|
--region=$IOTCORE_REGION \
|
||||||
|
--event-notification-config=$IOTCORE_TOPIC_DATA \
|
||||||
|
--state-pubsub-topic=$IOTCORE_TOPIC_DEVICE
|
||||||
|
```
|
||||||
|
|
||||||
|
## Creating device certificates
|
||||||
|
|
||||||
|
Create certificates to connect the device to the IoT Core gateway:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
openssl genrsa -out rsa_private.pem 2048
|
||||||
|
openssl rsa -in rsa_private.pem -pubout -out rsa_public.pem
|
||||||
|
```
|
||||||
|
|
||||||
|
## Registering the IoT device
|
||||||
|
|
||||||
|
Once created, add the public key to the IoT core registry:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
gcloud iot devices create $IOTCORE_DEVICE \
|
||||||
|
--project=$IOTCORE_PROJECT \
|
||||||
|
--region=$IOTCORE_REGION \
|
||||||
|
--registry=$IOTCORE_REG \
|
||||||
|
--public-key path=./rsa_public.pem,type=rs256
|
||||||
|
```
|
||||||
|
|
||||||
|
## Generating Data
|
||||||
|
|
||||||
|
To mimic an IoT device sending data to the IoT gateway, run the provided
|
||||||
|
Node.js client with the following parameters:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
node send-data.js \
|
||||||
|
--projectId=$IOTCORE_PROJECT \
|
||||||
|
--cloudRegion=$IOTCORE_REGION \
|
||||||
|
--registryId=$IOTCORE_REG \
|
||||||
|
--deviceId=$IOTCORE_DEVICE \
|
||||||
|
--privateKeyFile=./iot_demo_private.pem \
|
||||||
|
--algorithm=RS256
|
||||||
|
```
|
||||||
|
|
||||||
|
This "device" will publish one event per second to the IoT Core gateway.
|
||||||
|
The gateway will automatically publish the received events to the configured
|
||||||
|
PubSub topic (`iot-demo`).
|
||||||
|
|
||||||
|
The following payload is sent by this simulated IoT client:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
{
|
||||||
|
source_id: 'next18-demo-client',
|
||||||
|
event_id: '41e13421-25aa-4e93-bca8-0ffeb5c040c8',
|
||||||
|
event_ts: 1531515192370,
|
||||||
|
metric: 9
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `event_id` value here is a unique UUIDv4 ID, `event_ts` is UNIX Epoch time, and `metric`
|
||||||
|
is a random number 1-10.
|
||||||
|
|
||||||
|
## Create a function that handles events
|
||||||
|
|
||||||
|
Now we want to consume these IoT events, so let's create the function to handle the events:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
kubectl apply -f event-flow/route.yaml
|
||||||
|
kubectl apply -f event-flow/configuration.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create an event source
|
||||||
|
|
||||||
|
Before we can bind an action to an event source, we have to create an event source
|
||||||
|
that knows how to wire events into actions for that particular event type.
|
||||||
|
|
||||||
|
First let's create a ServiceAccount, so that we can run the local receive adapter
|
||||||
|
in Pull mode to poll for the events from this topic.
|
||||||
|
|
||||||
|
Then let's create a GCP PubSub as an event source that we can bind to.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
kubectl apply -f event-flow/serviceaccount.yaml
|
||||||
|
kubectl apply -f event-flow/serviceaccountbinding.yaml
|
||||||
|
kubectl apply -f event-flow/eventsource.yaml
|
||||||
|
kubectl apply -f event-flow/eventtype.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Bind IoT events to our function
|
||||||
|
|
||||||
|
We have now created a function that we want to consume our IoT events, and we have an event
|
||||||
|
source that's sending events via GCP PubSub, so let's wire the two together:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
kubectl apply -f event-flow/flow.yaml
|
||||||
|
```
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Copyright 2018 The Knative Authors
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
apiVersion: serving.knative.dev/v1alpha1
|
||||||
|
kind: Configuration
|
||||||
|
metadata:
|
||||||
|
name: iot-function
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
revisionTemplate:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
knative.dev/type: container
|
||||||
|
spec:
|
||||||
|
container:
|
||||||
|
image: github.com/mchmarny/next18/event-flow
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "demo-device",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "MQTT Demo Device for Google Cloud IoT Core using NodeJS",
|
||||||
|
"main": "device.js",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"author": "Google Inc.",
|
||||||
|
"dependencies": {
|
||||||
|
"@google-cloud/nodejs-repo-tools": "2.2.1",
|
||||||
|
"@google-cloud/pubsub": "0.16.4",
|
||||||
|
"MD5": "^1.3.0",
|
||||||
|
"ava": "0.25.0",
|
||||||
|
"jsonwebtoken": "8.2.0",
|
||||||
|
"mqtt": "2.16.0",
|
||||||
|
"node-uuid": "1.4.8",
|
||||||
|
"uuid": "3.2.1",
|
||||||
|
"yargs": "11.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {}
|
||||||
|
}
|
|
@ -0,0 +1,296 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2017, Google, Inc.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// [START iot_mqtt_include]
|
||||||
|
const fs = require('fs');
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
const mqtt = require('mqtt');
|
||||||
|
const uuid = require('node-uuid');
|
||||||
|
const os = require('os');
|
||||||
|
// [END iot_mqtt_include]
|
||||||
|
|
||||||
|
// The initial backoff time after a disconnection occurs, in seconds.
|
||||||
|
var MINIMUM_BACKOFF_TIME = 1;
|
||||||
|
|
||||||
|
// The maximum backoff time before giving up, in seconds.
|
||||||
|
var MAXIMUM_BACKOFF_TIME = 59;
|
||||||
|
|
||||||
|
// Whether to wait with exponential backoff before publishing.
|
||||||
|
var shouldBackoff = false;
|
||||||
|
|
||||||
|
// The current backoff time.
|
||||||
|
var backoffTime = 1;
|
||||||
|
|
||||||
|
// Whether an asynchronous publish chain is in progress.
|
||||||
|
var publishChainInProgress = false;
|
||||||
|
|
||||||
|
console.log('Knative IoT Example');
|
||||||
|
var argv = require(`yargs`)
|
||||||
|
.options({
|
||||||
|
projectId: {
|
||||||
|
default: process.env.GCLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT,
|
||||||
|
description: 'The Project ID to use. Defaults to the value of the GCLOUD_PROJECT or GOOGLE_CLOUD_PROJECT environment variables.',
|
||||||
|
requiresArg: true,
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
cloudRegion: {
|
||||||
|
default: 'us-central1',
|
||||||
|
description: 'GCP cloud region.',
|
||||||
|
requiresArg: true,
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
registryId: {
|
||||||
|
description: 'Cloud IoT registry ID.',
|
||||||
|
requiresArg: true,
|
||||||
|
demandOption: true,
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
deviceId: {
|
||||||
|
description: 'Cloud IoT device ID.',
|
||||||
|
requiresArg: true,
|
||||||
|
demandOption: true,
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
privateKeyFile: {
|
||||||
|
description: 'Path to private key file.',
|
||||||
|
requiresArg: true,
|
||||||
|
demandOption: true,
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
algorithm: {
|
||||||
|
description: 'Encryption algorithm to generate the JWT.',
|
||||||
|
requiresArg: true,
|
||||||
|
demandOption: true,
|
||||||
|
choices: ['RS256', 'ES256'],
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
numMessages: {
|
||||||
|
default: 100,
|
||||||
|
description: 'Number of messages to publish.',
|
||||||
|
requiresArg: true,
|
||||||
|
type: 'number'
|
||||||
|
},
|
||||||
|
tokenExpMins: {
|
||||||
|
default: 20,
|
||||||
|
description: 'Minutes to JWT token expiration.',
|
||||||
|
requiresArg: true,
|
||||||
|
type: 'number'
|
||||||
|
},
|
||||||
|
mqttBridgeHostname: {
|
||||||
|
default: 'mqtt.googleapis.com',
|
||||||
|
description: 'MQTT bridge hostname.',
|
||||||
|
requiresArg: true,
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
mqttBridgePort: {
|
||||||
|
default: 8883,
|
||||||
|
description: 'MQTT bridge port.',
|
||||||
|
requiresArg: true,
|
||||||
|
type: 'number'
|
||||||
|
},
|
||||||
|
messageType: {
|
||||||
|
default: 'events',
|
||||||
|
description: 'Message type to publish.',
|
||||||
|
requiresArg: true,
|
||||||
|
choices: ['events', 'state'],
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.example(`node $0 send-data.js --projectId=blue-jet-123 \\\n\t--registryId=my-registry --deviceId=my-node-device \\\n\t--privateKeyFile=../rsa_private.pem --algorithm=RS256 \\\n\t --cloudRegion=us-west1`)
|
||||||
|
.wrap(120)
|
||||||
|
.recommendCommands()
|
||||||
|
.epilogue(`For more information, see https://cloud.google.com/iot-core/docs`)
|
||||||
|
.help()
|
||||||
|
.strict()
|
||||||
|
.argv;
|
||||||
|
|
||||||
|
// Create a Cloud IoT Core JWT for the given project id, signed with the given
|
||||||
|
// private key.
|
||||||
|
// [START iot_mqtt_jwt]
|
||||||
|
function createJwt(projectId, privateKeyFile, algorithm) {
|
||||||
|
// Create a JWT to authenticate this device. The device will be disconnected
|
||||||
|
// after the token expires, and will have to reconnect with a new token. The
|
||||||
|
// audience field should always be set to the GCP project id.
|
||||||
|
const token = {
|
||||||
|
'iat': parseInt(Date.now() / 1000),
|
||||||
|
'exp': parseInt(Date.now() / 1000) + 20 * 60, // 20 minutes
|
||||||
|
'aud': projectId
|
||||||
|
};
|
||||||
|
const privateKey = fs.readFileSync(privateKeyFile);
|
||||||
|
return jwt.sign(token, privateKey, { algorithm: algorithm });
|
||||||
|
}
|
||||||
|
// [END iot_mqtt_jwt]
|
||||||
|
|
||||||
|
// Mock some data
|
||||||
|
var mockData = function () {
|
||||||
|
var load = os.loadavg();
|
||||||
|
var msg = {
|
||||||
|
'source_id': "next18-demo-client",
|
||||||
|
'event_id': uuid.v4(),
|
||||||
|
'event_ts': new Date().getTime(),
|
||||||
|
'metric': Math.floor(Math.random() * 10) + 1
|
||||||
|
};
|
||||||
|
console.dir(msg);
|
||||||
|
return Buffer.from(JSON.stringify(msg)).toString('base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish numMessages messages asynchronously, starting from message
|
||||||
|
// messagesSent.
|
||||||
|
// [START iot_mqtt_publish]
|
||||||
|
function publishAsync(messagesSent, numMessages) {
|
||||||
|
// If we have published enough messages or backed off too many times, stop.
|
||||||
|
if (messagesSent > numMessages || backoffTime >= MAXIMUM_BACKOFF_TIME) {
|
||||||
|
if (backoffTime >= MAXIMUM_BACKOFF_TIME) {
|
||||||
|
console.log('Backoff time is too high. Giving up.');
|
||||||
|
}
|
||||||
|
console.log('Closing connection to MQTT. Goodbye!');
|
||||||
|
client.end();
|
||||||
|
publishChainInProgress = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish and schedule the next publish.
|
||||||
|
publishChainInProgress = true;
|
||||||
|
var publishDelayMs = 0;
|
||||||
|
if (shouldBackoff) {
|
||||||
|
publishDelayMs = 1000 * (backoffTime + Math.random());
|
||||||
|
backoffTime *= 2;
|
||||||
|
console.log(`Backing off for ${publishDelayMs}ms before publishing.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
|
||||||
|
// const payload = `${argv.registryId}/${argv.deviceId}-payload-${messagesSent}`;
|
||||||
|
|
||||||
|
const payload = mockData();
|
||||||
|
|
||||||
|
// Publish "payload" to the MQTT topic. qos=1 means at least once delivery.
|
||||||
|
// Cloud IoT Core also supports qos=0 for at most once delivery.
|
||||||
|
console.log('Publishing message:', payload);
|
||||||
|
client.publish(mqttTopic, payload, { qos: 1 }, function (err) {
|
||||||
|
if (!err) {
|
||||||
|
shouldBackoff = false;
|
||||||
|
backoffTime = MINIMUM_BACKOFF_TIME;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var schedulePublishDelayMs = argv.messageType === 'events' ? 1000 : 2000;
|
||||||
|
setTimeout(function () {
|
||||||
|
// [START iot_mqtt_jwt_refresh]
|
||||||
|
let secsFromIssue = parseInt(Date.now() / 1000) - iatTime;
|
||||||
|
if (secsFromIssue > argv.tokenExpMins * 60) {
|
||||||
|
iatTime = parseInt(Date.now() / 1000);
|
||||||
|
console.log(`\tRefreshing token after ${secsFromIssue} seconds.`);
|
||||||
|
|
||||||
|
client.end();
|
||||||
|
connectionArgs.password = createJwt(argv.projectId, argv.privateKeyFile, argv.algorithm);
|
||||||
|
client = mqtt.connect(connectionArgs);
|
||||||
|
|
||||||
|
client.on('connect', (success) => {
|
||||||
|
console.log('connect');
|
||||||
|
if (!success) {
|
||||||
|
console.log('Client not connected...');
|
||||||
|
} else if (!publishChainInProgress) {
|
||||||
|
publishAsync(1, argv.numMessages);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('close', () => {
|
||||||
|
console.log('close');
|
||||||
|
shouldBackoff = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('error', (err) => {
|
||||||
|
console.log('error', err);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('message', (topic, message, packet) => {
|
||||||
|
console.log('message received: ', Buffer.from(message, 'base64').toString('ascii'));
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('packetsend', () => {
|
||||||
|
// Note: logging packet send is very verbose
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// [END iot_mqtt_jwt_refresh]
|
||||||
|
publishAsync(messagesSent + 1, numMessages);
|
||||||
|
}, schedulePublishDelayMs);
|
||||||
|
}, publishDelayMs);
|
||||||
|
}
|
||||||
|
// [END iot_mqtt_publish]
|
||||||
|
|
||||||
|
// [START iot_mqtt_run]
|
||||||
|
// The mqttClientId is a unique string that identifies this device. For Google
|
||||||
|
// Cloud IoT Core, it must be in the format below.
|
||||||
|
const mqttClientId = `projects/${argv.projectId}/locations/${argv.cloudRegion}/registries/${argv.registryId}/devices/${argv.deviceId}`;
|
||||||
|
|
||||||
|
// With Google Cloud IoT Core, the username field is ignored, however it must be
|
||||||
|
// non-empty. The password field is used to transmit a JWT to authorize the
|
||||||
|
// device. The "mqtts" protocol causes the library to connect using SSL, which
|
||||||
|
// is required for Cloud IoT Core.
|
||||||
|
let connectionArgs = {
|
||||||
|
host: argv.mqttBridgeHostname,
|
||||||
|
port: argv.mqttBridgePort,
|
||||||
|
clientId: mqttClientId,
|
||||||
|
username: 'unused',
|
||||||
|
password: createJwt(argv.projectId, argv.privateKeyFile, argv.algorithm),
|
||||||
|
protocol: 'mqtts',
|
||||||
|
secureProtocol: 'TLSv1_2_method'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a client, and connect to the Google MQTT bridge.
|
||||||
|
let iatTime = parseInt(Date.now() / 1000);
|
||||||
|
let client = mqtt.connect(connectionArgs);
|
||||||
|
|
||||||
|
// Subscribe to the /devices/{device-id}/config topic to receive config updates.
|
||||||
|
client.subscribe(`/devices/${argv.deviceId}/config`);
|
||||||
|
|
||||||
|
// The MQTT topic that this device will publish data to. The MQTT
|
||||||
|
// topic name is required to be in the format below. The topic name must end in
|
||||||
|
// 'state' to publish state and 'events' to publish telemetry. Note that this is
|
||||||
|
// not the same as the device registry's Cloud Pub/Sub topic.
|
||||||
|
const mqttTopic = `/devices/${argv.deviceId}/${argv.messageType}`;
|
||||||
|
|
||||||
|
client.on('connect', (success) => {
|
||||||
|
console.log('connect');
|
||||||
|
if (!success) {
|
||||||
|
console.log('Client not connected...');
|
||||||
|
} else if (!publishChainInProgress) {
|
||||||
|
publishAsync(1, argv.numMessages);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('close', () => {
|
||||||
|
console.log('close');
|
||||||
|
shouldBackoff = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('error', (err) => {
|
||||||
|
console.log('error', err);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('message', (topic, message, packet) => {
|
||||||
|
console.log('message received: ', Buffer.from(message, 'base64').toString('ascii'));
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('packetsend', () => {
|
||||||
|
// Note: logging packet send is very verbose
|
||||||
|
});
|
||||||
|
|
||||||
|
// Once all of the messages have been published, the connection to Google Cloud
|
||||||
|
// IoT will be closed and the process will exit. See the publishAsync method.
|
||||||
|
// [END iot_mqtt_run]
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Copyright 2018 The Knative Authors
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
apiVersion: feeds.knative.dev/v1alpha1
|
||||||
|
kind: EventSource
|
||||||
|
metadata:
|
||||||
|
name: gcppubsub
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
type: gcppubsub
|
||||||
|
source: gcppubsub
|
||||||
|
image: github.com/knative/eventing/pkg/sources/gcppubsub
|
||||||
|
parameters:
|
||||||
|
image: github.com/knative/eventing/pkg/sources/gcppubsub/receive_adapter
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Copyright 2018 The Knative Authors
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
apiVersion: feeds.knative.dev/v1alpha1
|
||||||
|
kind: EventType
|
||||||
|
metadata:
|
||||||
|
name: receive
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
eventSource: gcppubsub
|
||||||
|
description: "subscription for receiving pubsub messages"
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Copyright 2018 The Knative Authors
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
apiVersion: flows.knative.dev/v1alpha1
|
||||||
|
kind: Flow
|
||||||
|
metadata:
|
||||||
|
name: iot-example
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
serviceAccountName: binder
|
||||||
|
trigger:
|
||||||
|
service: gcppubsub
|
||||||
|
eventType: receive
|
||||||
|
resource: gcppubsub/receive
|
||||||
|
parameters:
|
||||||
|
projectID: s9-demo
|
||||||
|
topic: iot-demo
|
||||||
|
action:
|
||||||
|
target:
|
||||||
|
kind: Route
|
||||||
|
apiVersion: serving.knative.dev/v1alpha1
|
||||||
|
name: iot-function
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 The Knative Authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Event represents PubSub payload
|
||||||
|
type Event struct {
|
||||||
|
ID string `json:"ID"`
|
||||||
|
Data string `json:"Data"`
|
||||||
|
Attributes map[string]string `json:"Attributes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventPayload represents PubSub Data payload
|
||||||
|
type EventPayload struct {
|
||||||
|
ID string `json:"event_id"`
|
||||||
|
SourceID string `json:"source_id"`
|
||||||
|
SentOn int `json:"event_ts"`
|
||||||
|
Metric int `json:"metric"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlePost(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
//log.Println(string(body))
|
||||||
|
|
||||||
|
// decode the pubsub message
|
||||||
|
// var event map[string]string
|
||||||
|
var event Event
|
||||||
|
if err := json.Unmarshal(body, &event); err != nil {
|
||||||
|
log.Printf("Failed to unmarshal event: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode pubsub payload
|
||||||
|
rawEvent, _ := base64.StdEncoding.DecodeString(event.Data)
|
||||||
|
|
||||||
|
// decode iot data
|
||||||
|
data, _ := base64.StdEncoding.DecodeString(string(rawEvent))
|
||||||
|
|
||||||
|
// decode the pubsub message payload
|
||||||
|
var payload EventPayload
|
||||||
|
if err := json.Unmarshal(data, &payload); err != nil {
|
||||||
|
log.Printf("Failed to unmarshal payload: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Data sent by device %q: [metric: %d, on: %v]",
|
||||||
|
event.Attributes["deviceId"], payload.Metric, payload.SentOn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/", handlePost)
|
||||||
|
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Copyright 2018 The Knative Authors
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
apiVersion: serving.knative.dev/v1alpha1
|
||||||
|
kind: Route
|
||||||
|
metadata:
|
||||||
|
name: iot-function
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
traffic:
|
||||||
|
- configurationName: iot-function
|
||||||
|
percent: 100
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Copyright 2018 The Knative Authors
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: binder
|
||||||
|
namespace: default
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Copyright 2018 The Knative Authors
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: bind-admin
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: binder
|
||||||
|
namespace: default
|
||||||
|
roleRef:
|
||||||
|
kind: ClusterRole
|
||||||
|
name: cluster-admin
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
Loading…
Reference in New Issue