diff --git a/iot/beta-features/gateway/README.md b/iot/beta-features/gateway/README.md deleted file mode 100644 index 349b47496b..0000000000 --- a/iot/beta-features/gateway/README.md +++ /dev/null @@ -1,39 +0,0 @@ -Google Cloud Platform logo - -# Google Cloud IoT Core NodeJS Gateway sample - -This sample app demonstrates sending telemetry data on behalf of a device using the Cloud IoT Core gateways. - -# Setup - -Run the following command to install the library dependencies for NodeJS: - - npm install - -# Running the sample - - Commands: - createGateway - listGateways - bindDeviceToGateway - unbindDeviceFromGateway - listDevicesForGateway - - listen Listen for config messages on a gateway and device - relayData Sends data on behalf of a device. - - Options: - --projectId, -p The Project ID to use. Defaults to the value of the GCLOUD_PROJECT or GOOGLE_CLOUD_PROJECT - environment variables. [string] - --serviceAccount, -s The path to your service credentials JSON. [string] - --cloudRegion, -c [string] [default: "us-central1"] - --help Show help [boolean] - - Examples: - node hub.js relayData my-device my-registry "test" - - For more information, see https://cloud.google.com/iot-core/docs - -# Notes - -By default, gateways use the "Association only" method for authentication, which means the device does not have to store its own JWT. For other authentication methods, check [here for more information](https://cloud.google.com/iot/docs/how-tos/gateways/manage-gateways#authentication_methods). \ No newline at end of file diff --git a/iot/beta-features/gateway/gateway.js b/iot/beta-features/gateway/gateway.js deleted file mode 100644 index 75cf42594e..0000000000 --- a/iot/beta-features/gateway/gateway.js +++ /dev/null @@ -1,1155 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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. - -/* eslint-disable prefer-destructuring */ - -'use strict'; - -// [START iot_gateway_include] -const fs = require('fs'); -const {google} = require('googleapis'); -const jwt = require('jsonwebtoken'); -const mqtt = require('mqtt'); -// [END iot_gateway_include] - -// [START iot_gateway_client_config] -const API_VERSION = 'v1'; -const DISCOVERY_API = 'https://cloudiot.googleapis.com/$discovery/rest'; - -// Returns an authorized API client by discovering the Cloud IoT Core API with -// the provided API key. -const getClient = async () => { - const authClient = await google.auth.getClient({ - scopes: ['https://www.googleapis.com/auth/cloud-platform'], - }); - - const discoveryUrl = `${DISCOVERY_API}?version=${API_VERSION}`; - - google.options({ - auth: authClient, - }); - - try { - return google.discoverAPI(discoveryUrl); - } catch (err) { - console.log('Error during API discovery.', err); - } -}; -// [END iot_gateway_client_config] - -// [START iot_gateway_client_backoff_variables] -// The initial backoff time after a disconnection occurs, in seconds. -const MINIMUM_BACKOFF_TIME = 1; - -// The maximum backoff time before giving up, in seconds. -const MAXIMUM_BACKOFF_TIME = 32; - -// Whether to wait with exponential backoff before publishing. -let shouldBackoff = false; - -// The current backoff time. -let backoffTime = 1; - -// Whether an asynchronous publish chain is in progress -let publishChainInProgress = false; - -// [END iot_gateway_client_backoff_variables] - -// Create a Cloud IoT Core JWT for the given project id, signed with the given -// private key. -// [START iot_mqtt_jwt] -const 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] - -// Creates a gateway. -const createGateway = ( - client, - projectId, - cloudRegion, - registryId, - gatewayId, - certificateFile, - algorithm -) => { - // [START create_gateway] - // const cloudRegion = 'us-central1'; - // const deviceId = 'my-unauth-device'; - // const gatewayId = 'my-gateway'; - // const projectId = 'adjective-noun-123'; - // const registryId = 'my-registry'; - const parentName = `projects/${projectId}/locations/${cloudRegion}/registries/${registryId}`; - console.log('Creating gateway:', gatewayId); - - const certFormat = algorithm === 'ES256' ? 'ES256_PEM' : 'RSA_X509_PEM'; - - console.log(certFormat); - - const createRequest = { - parent: parentName, - resource: { - id: gatewayId, - credentials: [ - { - publicKey: { - format: certFormat, - key: fs.readFileSync(certificateFile).toString(), - }, - }, - ], - gatewayConfig: { - gatewayType: 'GATEWAY', - gatewayAuthMethod: 'ASSOCIATION_ONLY', - }, - }, - }; - - client.projects.locations.registries.devices.create( - createRequest, - (err, res) => { - if (err) { - console.log('Could not create device'); - console.log(err); - } else { - console.log('Created device'); - console.log(res.data); - } - } - ); - // [END create_gateway] -}; - -// Creates a device to bind to a gateway. -const createDeviceForGateway = async ( - client, - projectId, - cloudRegion, - registryId, - deviceId -) => { - // [START create_device] - // const cloudRegion = 'us-central1'; - // const deviceId = 'my-unauth-device'; - // const gatewayId = 'my-gateway'; - // const projectId = 'adjective-noun-123'; - // const registryId = 'my-registry'; - const parentName = `projects/${projectId}/locations/${cloudRegion}/registries/${registryId}`; - const getRequest = { - name: `${parentName}/devices/${deviceId}`, - }; - - let exists = false; - let device = {}; - try { - const {data} = await client.projects.locations.registries.devices.get( - getRequest - ); - console.log('Device exists'); - device = data; - exists = true; - } catch (err) { - // Device not found - console.log('Error while getting device', err); - } - - if (exists) { - return device; - } else { - console.log('Creating device:', deviceId); - - const createRequest = { - parent: parentName, - resource: { - id: deviceId, - gatewayConfig: { - gatewayType: 'NON_GATEWAY', - gatewayAuthMethod: 'ASSOCIATION_ONLY', - }, - }, - }; - - try { - const {data} = await client.projects.locations.registries.devices.create( - createRequest - ); - console.log('Created device'); - device = data; - console.log(device); - return device; - } catch (err) { - console.log('Could not create device'); - console.log(err); - } - } - // [END create_device] -}; - -// Binds a device to a gateway so that it can be attached. -const bindDeviceToGateway = async ( - client, - projectId, - cloudRegion, - registryId, - deviceId, - gatewayId -) => { - // [START bind_device_to_gateway] - // const cloudRegion = 'us-central1'; - // const deviceId = 'my-unauth-device'; - // const gatewayId = 'my-gateway'; - // const projectId = 'adjective-noun-123'; - // const registryId = 'my-registry'; - const device = await createDeviceForGateway( - client, - projectId, - cloudRegion, - registryId, - deviceId, - gatewayId - ); - - console.log(`Binding device: ${JSON.stringify(device.id)}`); - const parentName = `projects/${projectId}/locations/${cloudRegion}/registries/${registryId}`; - - const bindRequest = { - parent: parentName, - deviceId: device.id, - gatewayId: gatewayId, - }; - - try { - await client.projects.locations.registries.bindDeviceToGateway(bindRequest); - console.log('Bound device to', gatewayId); - } catch (err) { - console.log('Could not bind device', err); - } - // [END bind_device_to_gateway] -}; - -// Unbinds a device from a gateway. -const unbindDeviceFromGateway = async ( - client, - projectId, - cloudRegion, - registryId, - deviceId, - gatewayId -) => { - // [START unbind_device_to_gateway] - // const cloudRegion = 'us-central1'; - // const deviceId = 'my-unauth-device'; - // const gatewayId = 'my-gateway'; - // const projectId = 'adjective-noun-123'; - // const registryId = 'my-registry'; - console.log(`Unbinding device: ${deviceId}`); - const parentName = `projects/${projectId}/locations/${cloudRegion}/registries/${registryId}`; - - const unbindRequest = { - parent: parentName, - deviceId: deviceId, - gatewayId: gatewayId, - }; - - try { - await client.projects.locations.registries.unbindDeviceFromGateway( - unbindRequest - ); - console.log('Device no longer bound.'); - } catch (err) { - console.log('Could not unbind device', err); - } - // [END unbind_device_to_gateway] -}; - -// Unbinds the given device from all gateways -const unbindDeviceFromAllGateways = async ( - client, - projectId, - cloudRegion, - registryId, - deviceId -) => { - const parentName = `projects/${projectId}/locations/${cloudRegion}`; - const registryName = `${parentName}/registries/${registryId}`; - const request = { - name: `${registryName}/devices/${deviceId}`, - }; - - // get information about this device - let device; - try { - const {data} = await client.projects.locations.registries.devices.get( - request - ); - device = data; - } catch (err) { - console.error('Could not get device', err); - return; - } - - if (device) { - const isGateway = device.gatewayConfig.gatewayType === 'GATEWAY'; - - if (!isGateway) { - const listGatewaysForDeviceRequest = { - parent: registryName, - 'gatewayListOptions.associationsDeviceId': deviceId, - }; - - // get list of all gateways this non-gateway device is bound to - let gateways = []; - try { - const {data} = await client.projects.locations.registries.devices.list( - listGatewaysForDeviceRequest - ); - - if (data.devices && data.devices.length > 0) { - gateways = data.devices; - } - } catch (err) { - console.error('Could not list gateways', err); - return; - } - - const promises = gateways.map(gateway => { - const unbindRequest = { - parent: registryName, - deviceId: device.id, - gatewayId: gateway.id, - }; - - // for each gateway, make the call to unbind it - return client.projects.locations.registries.unbindDeviceFromGateway( - unbindRequest - ); - }); - - await Promise.all(promises); - } - } -}; - -const unbindAllDevices = async (client, projectId, cloudRegion, registryId) => { - const parentName = `projects/${projectId}/locations/${cloudRegion}`; - const registryName = `${parentName}/registries/${registryId}`; - const request = { - parent: registryName, - }; - - // get information about this device - let devices; - try { - const {data} = await client.projects.locations.registries.devices.list( - request - ); - - if (!data) { - return; - } - - devices = data.devices; - } catch (err) { - console.error('Could not list devices', err); - return; - } - - if (devices && devices.length > 0) { - const promises = devices.map(device => { - if (device) { - const isGateway = - device.gatewayConfig && - device.gatewayConfig.gatewayType === 'GATEWAY'; - - if (!isGateway) { - return unbindDeviceFromAllGateways( - client, - projectId, - cloudRegion, - registryId, - device.id - ); - } - } - }); - - await Promise.all(promises); - } -}; - -// Lists gateways in a registry. -const listGateways = async (client, projectId, cloudRegion, registryId) => { - // [START list_gateways] - // const cloudRegion = 'us-central1'; - // const projectId = 'adjective-noun-123'; - // const registryId = 'my-registry'; - const parentName = `projects/${projectId}/locations/${cloudRegion}/registries/${registryId}`; - const request = { - parent: parentName, - fieldMask: 'config,gatewayConfig', - }; - - let devices; - try { - const {data} = await client.projects.locations.registries.devices.list( - request - ); - devices = data.devices; - } catch (err) { - console.log('Could not list devices'); - console.log(err); - return; - } - - console.log('Current gateways in registry:'); - devices.forEach(device => { - if ( - device.gatewayConfig !== undefined && - device.gatewayConfig.gatewayType === 'GATEWAY' - ) { - console.log('----\n', device); - } else { - console.log('\t', device); - } - }); - // [END list_gateways] -}; - -// Lists devices bound to a gateway. -const listDevicesForGateway = async ( - client, - projectId, - cloudRegion, - registryId, - gatewayId -) => { - // [START list_devices_for_gateway] - // const cloudRegion = 'us-central1'; - // const gatewayId = 'my-gateway'; - // const projectId = 'adjective-noun-123'; - // const registryId = 'my-registry'; - const parentName = `projects/${projectId}/locations/${cloudRegion}/registries/${registryId}`; - const request = { - parent: parentName, - 'gatewayListOptions.associationsGatewayId': gatewayId, - }; - - let devices; - try { - const {data} = await client.projects.locations.registries.devices.list( - request - ); - devices = data.devices; - } catch (err) { - console.log('Could not list devices'); - console.log(err); - return; - } - - console.log('Current devices bound to gateway: ', gatewayId); - if (devices && devices.length > 0) { - devices.forEach(device => { - console.log(`\tDevice: ${device.numId} : ${device.id}`); - }); - } else { - console.log('No devices bound to this gateway.'); - } - // [END list_devices_for_gateway] -}; - -// Lists gateways a given device is bound to. -const listGatewaysForDevice = async ( - client, - projectId, - cloudRegion, - registryId, - deviceId -) => { - // [START list_gateways_for_device] - // const cloudRegion = 'us-central1'; - // const deviceId = 'my-device'; - // const projectId = 'adjective-noun-123'; - // const registryId = 'my-registry'; - const parentName = `projects/${projectId}/locations/${cloudRegion}/registries/${registryId}`; - const request = { - parent: parentName, - 'gatewayListOptions.associationsDeviceId': deviceId, - }; - - let devices; - try { - const {data} = await client.projects.locations.registries.devices.list( - request - ); - devices = data.devices; - } catch (err) { - console.log('Could not list gateways for device'); - console.log(err); - return; - } - - console.log('Current gateways for device:', deviceId); - if (devices && devices.length > 0) { - devices.forEach(gateway => { - console.log(`\tDevice: ${gateway.numId} : ${gateway.id}`); - }); - } else { - console.log('No gateways associated with this device.'); - } - // [END list_gateways_for_device] -}; - -// Attaches a device to a gateway. -const attachDevice = async (deviceId, client) => { - // [START attach_device] - // const deviceId = 'my-unauth-device'; - const attachTopic = `/devices/${deviceId}/attach`; - console.log(`Attaching: ${attachTopic}`); - const attachPayload = '{}'; - - try { - await client.publish(attachTopic, attachPayload, {qos: 1}); - - shouldBackoff = false; - backoffTime = MINIMUM_BACKOFF_TIME; - } catch (err) { - console.log(err); - } - // [END attach_device] -}; - -// Detaches a device from a gateway. -const detachDevice = async (deviceId, client) => { - // [START detach_device] - const detachTopic = `/devices/${deviceId}/detach`; - console.log(`Detaching: ${detachTopic}`); - const detachPayload = '{}'; - - try { - await client.publish(detachTopic, detachPayload, {qos: 1}); - - shouldBackoff = false; - backoffTime = MINIMUM_BACKOFF_TIME; - } catch (err) { - console.log(err); - } - // [END detach_device] -}; - -// Listen for configuration messages on a gateway and bound device. -const listenForConfigMessages = ( - gatewayId, - deviceId, - registryId, - projectId, - region, - algorithm, - privateKeyFile, - mqttBridgeHostname, - mqttBridgePort, - clientDuration -) => { - // [START listen_for_config_messages] - // const parentName = `projects/${projectId}/locations/${region}`; - // const registryName = `${parentName}/registries/${registryId}`; - - const mqttClientId = `projects/${projectId}/locations/${region}/registries/${registryId}/devices/${gatewayId}`; - console.log(mqttClientId); - const connectionArgs = { - host: mqttBridgeHostname, - port: mqttBridgePort, - clientId: mqttClientId, - username: 'unused', - password: createJwt(projectId, privateKeyFile, algorithm), - protocol: 'mqtts', - qos: 1, - secureProtocol: 'TLSv1_2_method', - }; - - // Create a client, and connect to the Google MQTT bridge. - const client = mqtt.connect(connectionArgs); - - client.on('connect', success => { - if (!success) { - console.log('Client not connected...'); - } else { - console.log('Client connected: Gateway is listening, attaching device'); - attachDevice(deviceId, client); - - setTimeout(() => { - // Subscribe to any configuration topics. - client.subscribe(`/devices/${gatewayId}/config`, {qos: 1}); - client.subscribe(`/devices/${deviceId}/config`, {qos: 1}); - - setTimeout(() => { - detachDevice(deviceId, client); - console.log('Closing connection to MQTT. Goodbye!'); - client.end(true); - }, clientDuration); // Safely detach device and close connection. - }, 5000); - } - }); - - client.on('close', () => { - console.log('Connection closed'); - shouldBackoff = true; - }); - - client.on('error', err => { - console.log('error', err); - }); - - client.on('message', (topic, message) => { - const decodedMessage = Buffer.from(message, 'base64').toString('ascii'); - - if (topic === `/devices/${gatewayId}/errors`) { - console.log(`message received on error topic: ${decodedMessage}`); - } else { - console.log(`message received on topic ${topic}: ${decodedMessage}`); - } - }); - - client.on('packetsend', () => { - // Note: logging packet send is very verbose - }); - // [END listen_for_config_messages] -}; - -// Listen for error messages on a gateway. -const listenForErrorMessages = ( - gatewayId, - registryId, - projectId, - region, - algorithm, - privateKeyFile, - mqttBridgeHostname, - mqttBridgePort, - clientDuration, - deviceId -) => { - // [START listen_for_error_messages] - // const parentName = `projects/${projectId}/locations/${region}`; - // const registryName = `${parentName}/registries/${registryId}`; - - const mqttClientId = `projects/${projectId}/locations/${region}/registries/${registryId}/devices/${gatewayId}`; - console.log(mqttClientId); - const connectionArgs = { - host: mqttBridgeHostname, - port: mqttBridgePort, - clientId: mqttClientId, - username: 'unused', - password: createJwt(projectId, privateKeyFile, algorithm), - protocol: 'mqtts', - qos: 1, - secureProtocol: 'TLSv1_2_method', - }; - - // Create a client, and connect to the Google MQTT bridge. - const client = mqtt.connect(connectionArgs); - - client.on('connect', success => { - if (!success) { - console.log('Client not connected...'); - } else { - setTimeout(() => { - // Subscribe to gateway error topic. - client.subscribe(`/devices/${gatewayId}/errors`, {qos: 0}); - - attachDevice(deviceId, client); - - setTimeout(() => { - console.log('Closing connection to MQTT. Goodbye!'); - client.end(true); - }, clientDuration); // Safely detach device and close connection. - }, 5000); - } - }); - - client.on('close', () => { - console.log('Connection closed'); - shouldBackoff = true; - }); - - client.on('error', err => { - console.log('error', err); - }); - - client.on('message', (topic, message) => { - const decodedMessage = Buffer.from(message, 'base64').toString('ascii'); - - console.log(`message received on error topic ${topic}: ${decodedMessage}`); - }); - - client.on('packetsend', () => { - // Note: logging packet send is very verbose - }); - // [END listen_for_error_messages] -}; - -// Sends telemetry on behalf of a device. -const sendDataFromBoundDevice = ( - gatewayId, - deviceId, - registryId, - projectId, - region, - algorithm, - privateKeyFile, - mqttBridgeHostname, - mqttBridgePort, - numMessages, - tokenExpMins -) => { - // [START iot_send_delegate_data] - // const parentName = `projects/${projectId}/locations/${region}`; - // const registryName = `${parentName}/registries/${registryId}`; - // const binaryData = Buffer.from(data).toString('base64'); - // const request = { - // name: `${registryName}/devices/${deviceId}`, - // binaryData: binaryData - // }; - - const mqttClientId = `projects/${projectId}/locations/${region}/registries/${registryId}/devices/${gatewayId}`; - console.log(`MQTT client id: ${mqttClientId}`); - const connectionArgs = { - host: mqttBridgeHostname, - port: mqttBridgePort, - clientId: mqttClientId, - username: 'unused', - password: createJwt(projectId, privateKeyFile, algorithm), - protocol: 'mqtts', - qos: 1, - secureProtocol: 'TLSv1_2_method', - }; - - // Create a client, and connect to the Google MQTT bridge. - const iatTime = parseInt(Date.now() / 1000); - const client = mqtt.connect(connectionArgs); - - client.on('connect', success => { - if (!success) { - console.log('Client not connected...'); - } else if (!publishChainInProgress) { - console.log('Client connected: Attaching device'); - attachDevice(deviceId, client); - setTimeout(() => { - console.log('Client connected: Gateway is ready to relay'); - publishAsync( - client, - iatTime, - tokenExpMins, - 0, - numMessages, - registryId, - deviceId, - gatewayId, - connectionArgs, - projectId, - privateKeyFile, - algorithm - ); - }, 5000); - } - }); - - client.on('close', () => { - console.log('Connection closed'); - shouldBackoff = true; - }); - - client.on('error', err => { - console.log('error', err); - }); - - client.on('message', (topic, message) => { - console.log( - 'message received: ', - Buffer.from(message, 'base64').toString('ascii') - ); - }); - - client.on('packetsend', () => { - // Note: logging packet send is very verbose - }); - // [END iot_send_delegate_data] -}; - -// Publish numMessages messages asynchronously, starting from message -// messagesSent. -const publishAsync = ( - client, - iatTime, - tokenExpMins, - messagesSent, - numMessages, - registryId, - deviceId, - gatewayId, - connectionArgs, - projectId, - privateKeyFile, - algorithm -) => { - // [START iot_mqtt_publish] - // 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.'); - } - if (messagesSent >= numMessages) { - detachDevice(deviceId, client); - } - console.log('Closing connection to MQTT. Goodbye!'); - client.end(); - publishChainInProgress = false; - return; - } - - // Publish and schedule the next publish. - publishChainInProgress = true; - let publishDelayMs = 0; - if (shouldBackoff) { - publishDelayMs = 1000 * (backoffTime + Math.random()); - backoffTime *= 2; - console.log(`Backing off for ${publishDelayMs}ms before publishing.`); - } - let mqttTopic = `/devices/${gatewayId}/state`; - let payload = `${registryId}/${gatewayId}-connected-${new Date().getTime()}`; - console.log(`Publishing message ${messagesSent}/${numMessages}`); - if (messagesSent > 0) { - mqttTopic = `/devices/${deviceId}/state`; - payload = `${registryId}/${deviceId}-payload-${messagesSent}`; - } - - setTimeout(() => { - // 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} to ${mqttTopic}`); - client.publish(mqttTopic, payload, {qos: 1}, err => { - if (!err) { - shouldBackoff = false; - backoffTime = MINIMUM_BACKOFF_TIME; - } - }); - - const schedulePublishDelayMs = 5000; // messageType === 'events' ? 1000 : 2000; - setTimeout(() => { - // [START iot_mqtt_jwt_refresh] - const secsFromIssue = parseInt(Date.now() / 1000) - iatTime; - if (secsFromIssue > tokenExpMins * 60) { - iatTime = parseInt(Date.now() / 1000); - console.log(`\tRefreshing token after ${secsFromIssue} seconds.`); - - client.end(); - connectionArgs.password = createJwt( - projectId, - privateKeyFile, - algorithm - ); - client = mqtt.connect(connectionArgs); - } - // [END iot_mqtt_jwt_refresh] - publishAsync( - client, - iatTime, - tokenExpMins, - messagesSent + 1, - numMessages, - registryId, - deviceId, - gatewayId - ); - }, schedulePublishDelayMs); - }, publishDelayMs); - // [END iot_mqtt_publish] -}; - -let argv = require(`yargs`) // eslint-disable-line - .demandCommand(1, 'You need at least one command before moving on') - .options({ - algorithm: { - default: 'RS256', - description: 'Encryption algorithm to generate the JWT.', - requiresArg: true, - demandOption: true, - choices: ['RS256', 'ES256'], - type: 'string', - }, - clientDuration: { - default: 60000, - description: 'Duration in milliseconds for MQTT client to run', - requiresArg: true, - type: 'number', - }, - cloudRegion: { - alias: 'c', - default: 'us-central1', - requiresArg: true, - type: 'string', - }, - deviceId: { - description: 'Cloud IoT device ID.', - requiresArg: false, - demandOption: false, - type: 'string', - }, - gatewayId: { - description: 'Cloud IoT gateway ID.', - requiresArg: false, - demandOption: false, - type: 'string', - }, - mqttBridgePort: { - default: 8883, - description: 'MQTT bridge port.', - requiresArg: true, - type: 'number', - }, - mqttBridgeHostname: { - default: 'mqtt.googleapis.com', - description: 'MQTT bridge hostname.', - requiresArg: true, - type: 'string', - }, - privateKeyFile: { - description: 'Path to private key file.', - requiresArg: true, - type: 'string', - }, - projectId: { - alias: 'p', - 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', - }, - serviceAccount: { - alias: 's', - default: process.env.GOOGLE_APPLICATION_CREDENTIALS, - description: 'The path to your service credentials JSON.', - requiresArg: true, - type: 'string', - }, - tokenExpMins: { - default: 20, - description: 'Minutes to JWT token expiration.', - requiresArg: true, - type: 'number', - }, - }) - - .command( - `createGateway `, - `Creates a gateway`, - {}, - async opts => { - const client = await getClient(opts.serviceAccount); - await createGateway( - client, - opts.projectId, - opts.cloudRegion, - opts.registryId, - opts.gatewayId, - opts.publicKeyFile, - opts.algorithm - ); - } - ) - .command( - `listGateways `, - `Lists gateways in a registry.`, - {}, - async opts => { - const client = await getClient(opts.serviceAccount); - await listGateways( - client, - opts.projectId, - opts.cloudRegion, - opts.registryId - ); - } - ) - .command( - `bindDeviceToGateway `, - `Binds a device to a gateway`, - {}, - async opts => { - const client = await getClient(opts.serviceAccount); - await bindDeviceToGateway( - client, - opts.projectId, - opts.cloudRegion, - opts.registryId, - opts.deviceId, - opts.gatewayId - ); - } - ) - .command( - `unbindDeviceFromGateway `, - `Unbinds a device from a gateway`, - {}, - async opts => { - const client = await getClient(opts.serviceAccount); - await unbindDeviceFromGateway( - client, - opts.projectId, - opts.cloudRegion, - opts.registryId, - opts.deviceId, - opts.gatewayId - ); - } - ) - .command( - `unbindDeviceFromAllGateways `, - `Unbinds a device from all gateways`, - {}, - async opts => { - const client = await getClient(opts.serviceAccount); - await unbindDeviceFromAllGateways( - client, - opts.projectId, - opts.cloudRegion, - opts.registryId, - opts.deviceId - ); - } - ) - .command( - `unbindAllDevices `, - `Unbinds all devices in a given registry. Mainly for clearing registries`, - {}, - async opts => { - const client = await getClient(opts.serviceAccount); - await unbindAllDevices( - client, - opts.projectId, - opts.cloudRegion, - opts.registryId - ); - } - ) - .command( - `listDevicesForGateway `, - `Lists devices in a gateway.`, - {}, - async opts => { - const client = await getClient(opts.serviceAccount); - await listDevicesForGateway( - client, - opts.projectId, - opts.cloudRegion, - opts.registryId, - opts.gatewayId - ); - } - ) - .command( - `listGatewaysForDevice `, - `Lists gateways for a given device.`, - {}, - async opts => { - const client = await getClient(opts.serviceAccount); - await listGatewaysForDevice( - client, - opts.projectId, - opts.cloudRegion, - opts.registryId, - opts.deviceId - ); - } - ) - .command( - `listen `, - `Listens for configuration changes on a gateway and bound device.`, - {}, - opts => { - listenForConfigMessages( - opts.gatewayId, - opts.deviceId, - opts.registryId, - opts.projectId, - opts.cloudRegion, - opts.algorithm, - opts.privateKeyFile, - opts.mqttBridgeHostname, - opts.mqttBridgePort, - opts.clientDuration - ); - } - ) - .command( - `listenForErrors `, - `Listens for error messages on a gateway.`, - {}, - opts => { - listenForErrorMessages( - opts.gatewayId, - opts.registryId, - opts.projectId, - opts.cloudRegion, - opts.algorithm, - opts.privateKeyFile, - opts.mqttBridgeHostname, - opts.mqttBridgePort, - opts.clientDuration, - opts.deviceId - ); - } - ) - .command( - `relayData `, - `Sends data on behalf of a bound device.`, - { - numMessages: { - default: 5, - description: 'Number of messages to publish.', - requiresArg: true, - type: 'number', - }, - }, - opts => { - sendDataFromBoundDevice( - opts.gatewayId, - opts.deviceId, - opts.registryId, - opts.projectId, - opts.cloudRegion, - opts.algorithm, - opts.privateKeyFile, - opts.mqttBridgeHostname, - opts.mqttBridgePort, - opts.numMessages, - opts.tokenExpMins - ); - } - ) - .example(`node $0 relayData my-device my-registry "test"`) - .wrap(120) - .recommendCommands() - .epilogue(`For more information, see https://cloud.google.com/iot-core/docs`) - .help() - .strict().argv; diff --git a/iot/beta-features/gateway/gateway.test.js b/iot/beta-features/gateway/gateway.test.js deleted file mode 100644 index 1c742f154b..0000000000 --- a/iot/beta-features/gateway/gateway.test.js +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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'; - -const path = require('path'); -const {PubSub} = require('@google-cloud/pubsub'); -const assert = require('assert'); -const tools = require('@google-cloud/nodejs-repo-tools'); -const uuid = require('uuid'); - -const cmd = 'node gateway.js'; -const topicName = `nodejs-docs-samples-test-iot-${uuid.v4()}`; -const registryName = `nodejs-test-registry-iot-${uuid.v4()}`; - -const helper = 'node manager.js'; -const cwdHelper = path.join(__dirname, '../../manager'); -const installDeps = 'npm install'; -const publicKeyParam = process.env.NODEJS_IOT_RSA_PUBLIC_CERT; -const privateKeyParam = process.env.NODEJS_IOT_RSA_PRIVATE_KEY; - -const pubsub = new PubSub(); - -before(async () => { - tools.checkCredentials(); - const [topic] = await pubsub.createTopic(topicName); - console.log(`Topic ${topic.name} created.`); - - await tools.runAsync(installDeps, cwdHelper); - await tools.runAsync(`${helper} setupIotTopic ${topicName}`, cwdHelper); - await tools.runAsync( - `${helper} createRegistry ${registryName} ${topicName}`, - cwdHelper - ); - console.log(`Registry ${registryName} created`); -}); - -after(async () => { - await tools.runAsync(`${helper} deleteRegistry ${registryName}`, cwdHelper); - console.log(`Registry ${registryName} was deleted`); - - const topic = pubsub.topic(topicName); - await topic.delete(); - console.log(`Topic ${topic.name} deleted.`); -}); - -it('should create a new gateway', async () => { - // create gateway - const gatewayId = `nodejs-test-gateway-iot-${uuid.v4()}`; - const gatewayOut = await tools.runAsync( - `${cmd} createGateway ${registryName} ${gatewayId} RS256 ${publicKeyParam}` - ); // test no error on create gateway - assert.strictEqual(new RegExp('Created device').test(gatewayOut), true); - - // delete gateway with deleteDevice - await tools.runAsync( - `${helper} deleteDevice ${gatewayId} ${registryName}`, - cwdHelper - ); -}); - -it('should list gateways', async () => { - const gatewayId = `nodejs-test-gateway-iot-${uuid.v4()}`; - await tools.runAsync( - `${cmd} createGateway ${registryName} ${gatewayId} RS256 ${publicKeyParam}` - ); - - // look for output in list gateway - const gateways = await tools.runAsync(`${cmd} listGateways ${registryName}`); - assert.strictEqual(new RegExp(`${gatewayId}`).test(gateways), true); - - await tools.runAsync( - `${helper} deleteDevice ${gatewayId} ${registryName}`, - cwdHelper - ); -}); - -it('should bind existing device to gateway', async () => { - const gatewayId = `nodejs-test-gateway-iot-${uuid.v4()}`; - await tools.runAsync( - `${cmd} createGateway ${registryName} ${gatewayId} RS256 ${publicKeyParam}` - ); - - // create device - const deviceId = `nodejs-test-device-iot-${uuid.v4()}`; - await tools.runAsync( - `${helper} createRsa256Device ${deviceId} ${registryName} ${publicKeyParam}`, - cwdHelper - ); - - // bind device to gateway - const bind = await tools.runAsync( - `${cmd} bindDeviceToGateway ${registryName} ${gatewayId} ${deviceId}` - ); - - assert.strictEqual(new RegExp('Device exists').test(bind), true); - assert.strictEqual(new RegExp('Bound device').test(bind), true); - assert.strictEqual(new RegExp('Could not bind device').test(bind), false); - - // test unbind - const unbind = await tools.runAsync( - `${cmd} unbindDeviceFromGateway ${registryName} ${gatewayId} ${deviceId}` - ); - assert.strictEqual(new RegExp('Device no longer bound').test(unbind), true); - - await tools.runAsync( - `${helper} deleteDevice ${gatewayId} ${registryName}`, - cwdHelper - ); - await tools.runAsync( - `${helper} deleteDevice ${deviceId} ${registryName}`, - cwdHelper - ); -}); - -it('should bind new device to gateway', async () => { - const gatewayId = `nodejs-test-gateway-iot-${uuid.v4()}`; - await tools.runAsync( - `${cmd} createGateway ${registryName} ${gatewayId} RS256 ${publicKeyParam}` - ); - - // binding a non-existing device should create it - const deviceId = `nodejs-test-device-iot-${uuid.v4()}`; - const bind = await tools.runAsync( - `${cmd} bindDeviceToGateway ${registryName} ${gatewayId} ${deviceId}` - ); - - assert.strictEqual(new RegExp('Created device').test(bind), true); - assert.strictEqual(new RegExp('Bound device').test(bind), true); - assert.strictEqual(new RegExp('Could not bind device').test(bind), false); - - // unbind and delete device and gateway - const unbind = await tools.runAsync( - `${cmd} unbindDeviceFromGateway ${registryName} ${gatewayId} ${deviceId}` - ); - assert.strictEqual(new RegExp('Device no longer bound').test(unbind), true); - - await tools.runAsync( - `${helper} deleteDevice ${gatewayId} ${registryName}`, - cwdHelper - ); - await tools.runAsync( - `${helper} deleteDevice ${deviceId} ${registryName}`, - cwdHelper - ); -}); - -it('should list devices bound to gateway', async () => { - const gatewayId = `nodejs-test-gateway-iot-${uuid.v4()}`; - await tools.runAsync( - `${cmd} createGateway ${registryName} ${gatewayId} RS256 ${publicKeyParam}` - ); - - // binding a non-existing device should create it - const deviceId = `nodejs-test-device-iot-${uuid.v4()}`; - await tools.runAsync( - `${cmd} bindDeviceToGateway ${registryName} ${gatewayId} ${deviceId}` - ); - - const devices = await tools.runAsync( - `${cmd} listDevicesForGateway ${registryName} ${gatewayId}` - ); - - assert.strictEqual(new RegExp(deviceId).test(devices), true); - assert.strictEqual(new RegExp('No devices bound').test(devices), false); - // cleanup - await tools.runAsync( - `${cmd} unbindDeviceFromGateway ${registryName} ${gatewayId} ${deviceId}` - ); - await tools.runAsync( - `${helper} deleteDevice ${gatewayId} ${registryName}`, - cwdHelper - ); - await tools.runAsync( - `${helper} deleteDevice ${deviceId} ${registryName}`, - cwdHelper - ); -}); - -it('should list gateways for bound device', async () => { - const gatewayId = `nodejs-test-gateway-iot-${uuid.v4()}`; - await tools.runAsync( - `${cmd} createGateway ${registryName} ${gatewayId} RS256 ${publicKeyParam}` - ); - - // binding a non-existing device should create it - const deviceId = `nodejs-test-device-iot-${uuid.v4()}`; - await tools.runAsync( - `${cmd} bindDeviceToGateway ${registryName} ${gatewayId} ${deviceId}` - ); - - const devices = await tools.runAsync( - `${cmd} listGatewaysForDevice ${registryName} ${deviceId}` - ); - - assert.strictEqual(new RegExp(gatewayId).test(devices), true); - assert.strictEqual( - new RegExp('No gateways associated with this device').test(devices), - false - ); - - // cleanup - await tools.runAsync( - `${cmd} unbindDeviceFromGateway ${registryName} ${gatewayId} ${deviceId}` - ); - await tools.runAsync( - `${helper} deleteDevice ${gatewayId} ${registryName}`, - cwdHelper - ); - await tools.runAsync( - `${helper} deleteDevice ${deviceId} ${registryName}`, - cwdHelper - ); -}); - -it('should listen for bound device config message', async () => { - const gatewayId = `nodejs-test-gateway-iot-${uuid.v4()}`; - await tools.runAsync( - `${cmd} createGateway ${registryName} ${gatewayId} RS256 ${publicKeyParam}` - ); - - const deviceId = `nodejs-test-device-iot-${uuid.v4()}`; - await tools.runAsync( - `${cmd} bindDeviceToGateway ${registryName} ${gatewayId} ${deviceId}` - ); - - // listen for configuration changes - const out = await tools.runAsync( - `${cmd} listen ${deviceId} ${gatewayId} ${registryName} ${privateKeyParam} --clientDuration=30000` - ); - - assert.strictEqual(new RegExp('message received').test(out), true); - - // cleanup - await tools.runAsync( - `${cmd} unbindDeviceFromGateway ${registryName} ${gatewayId} ${deviceId}` - ); - await tools.runAsync( - `${helper} deleteDevice ${gatewayId} ${registryName}`, - cwdHelper - ); - await tools.runAsync( - `${helper} deleteDevice ${deviceId} ${registryName}`, - cwdHelper - ); -}); - -it('should listen for error topic messages', async () => { - const gatewayId = `nodejs-test-gateway-iot-${uuid.v4()}`; - await tools.runAsync( - `${cmd} createGateway ${registryName} ${gatewayId} RS256 ${publicKeyParam}` - ); - - // create a device but don't associate it with the gateway - const deviceId = `nodejs-test-device-iot-${uuid.v4()}`; - await tools.runAsync( - `${helper} createRsa256Device ${deviceId} ${registryName} ${publicKeyParam}`, - cwdHelper - ); - - // check error topic contains error of attaching a device that is not bound - const out = await tools.runAsync( - `${cmd} listenForErrors ${gatewayId} ${registryName} ${deviceId} ${privateKeyParam} --clientDuration=30000` - ); - - assert.strictEqual( - new RegExp(`DeviceId ${deviceId} is not associated with Gateway`).test(out), - true - ); - - // cleanup - await tools.runAsync( - `${cmd} unbindDeviceFromGateway ${registryName} ${gatewayId} ${deviceId}` - ); - await tools.runAsync( - `${helper} deleteDevice ${gatewayId} ${registryName}`, - cwdHelper - ); - await tools.runAsync( - `${helper} deleteDevice ${deviceId} ${registryName}`, - cwdHelper - ); -}); - -it('should send data from bound device', async () => { - const gatewayId = `nodejs-test-gateway-iot-${uuid.v4()}`; - await tools.runAsync( - `${cmd} createGateway ${registryName} ${gatewayId} RS256 ${publicKeyParam}` - ); - - const deviceId = `nodejs-test-device-iot-${uuid.v4()}`; - await tools.runAsync( - `${cmd} bindDeviceToGateway ${registryName} ${gatewayId} ${deviceId}` - ); - - // relay telemetry on behalf of device - const out = await tools.runAsync( - `${cmd} relayData ${deviceId} ${gatewayId} ${registryName} ${privateKeyParam} --numMessages=5` - ); - - assert.strictEqual(new RegExp('Publishing message 5/5').test(out), true); - assert.strictEqual(new RegExp('Error: Connection refused').test(out), false); - - await tools.runAsync( - `${cmd} unbindDeviceFromGateway ${registryName} ${gatewayId} ${deviceId}` - ); - await tools.runAsync( - `${helper} deleteDevice ${gatewayId} ${registryName}`, - cwdHelper - ); - await tools.runAsync( - `${helper} deleteDevice ${deviceId} ${registryName}`, - cwdHelper - ); -}); diff --git a/iot/beta-features/gateway/package.json b/iot/beta-features/gateway/package.json deleted file mode 100644 index 1d820d7a88..0000000000 --- a/iot/beta-features/gateway/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "nodejs-docs-samples-iot-manager", - "version": "0.0.1", - "description": "Example of Cloud IoT Core gateways feature", - "main": "manager.js", - "license": "Apache-2.0", - "author": "Google LLC", - "repository": { - "type": "git", - "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git" - }, - "engines": { - "node": ">=8.0.0" - }, - "scripts": { - "test": "repo-tools test install --cmd=npm -- run unit-test", - "unit-test": "mocha *.test.js --timeout=600000" - }, - "dependencies": { - "googleapis": "^45.0.0", - "jsonwebtoken": "^8.3.0", - "mqtt": "^3.0.0", - "yargs": "^15.0.0" - }, - "devDependencies": { - "@google-cloud/nodejs-repo-tools": "^3.3.0", - "mocha": "^6.0.0", - "@google-cloud/pubsub": "^1.0.0", - "uuid": "^3.1.0" - }, - "cloud-repo-tools": { - "requiresKeyFile": true, - "requiresProjectId": true - } -} diff --git a/iot/beta-features/gateway/resources/README.md b/iot/beta-features/gateway/resources/README.md deleted file mode 100644 index 29a1f453ee..0000000000 --- a/iot/beta-features/gateway/resources/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Test public certificate files - -The certificates in this folder are only provided for testing and should not be -used for registering or connecting your devices.