From fe5fd2b7ef5f40d688ee85c874934487d0c2d702 Mon Sep 17 00:00:00 2001
From: Rich Bayliss <rich@balena.io>
Date: Tue, 4 Feb 2020 09:43:22 +0000
Subject: [PATCH] bug: Delay sending logs until the device is provisioned

Connects-to: #1189
Change-type: patch
Signed-off-by: Rich Bayliss <rich@balena.io>
---
 src/api-binder.ts |  6 +++++-
 src/event-bus.ts  | 17 +++++++++++++++
 src/logger.ts     | 54 ++++++++++++++++++-----------------------------
 src/supervisor.ts |  6 +++---
 4 files changed, 46 insertions(+), 37 deletions(-)
 create mode 100644 src/event-bus.ts

diff --git a/src/api-binder.ts b/src/api-binder.ts
index 81a7d3b4..338af3bb 100644
--- a/src/api-binder.ts
+++ b/src/api-binder.ts
@@ -8,6 +8,8 @@ import { PinejsClientRequest, StatusError } from 'pinejs-client-request';
 import * as deviceRegister from 'resin-register-device';
 import * as url from 'url';
 
+import * as globalEventBus from './event-bus';
+
 import Config, { ConfigType } from './config';
 import Database from './db';
 import { EventTracker } from './event-tracker';
@@ -929,7 +931,9 @@ export class APIBinder {
 		]);
 
 		if (!conf.provisioned || conf.apiKey != null || conf.pinDevice != null) {
-			return this.provisionOrRetry(conf.bootstrapRetryDelay as number);
+			await this.provisionOrRetry(conf.bootstrapRetryDelay);
+			globalEventBus.getInstance().emit('deviceProvisioned');
+			return;
 		}
 
 		return conf;
diff --git a/src/event-bus.ts b/src/event-bus.ts
new file mode 100644
index 00000000..77c34b6e
--- /dev/null
+++ b/src/event-bus.ts
@@ -0,0 +1,17 @@
+import { EventEmitter } from 'events';
+import * as _ from 'lodash';
+import StrictEventEmitter from 'strict-event-emitter-types';
+
+export interface GlobalEvents {
+	deviceProvisioned: void;
+}
+
+type GlobalEventEmitter = StrictEventEmitter<EventEmitter, GlobalEvents>;
+
+export class GlobalEventBus extends (EventEmitter as new () => GlobalEventEmitter) {
+	public constructor() {
+		super();
+	}
+}
+
+export const getInstance = _.once(() => new GlobalEventBus());
diff --git a/src/logger.ts b/src/logger.ts
index d5baf024..1a4d0c84 100644
--- a/src/logger.ts
+++ b/src/logger.ts
@@ -1,7 +1,7 @@
 import * as Bluebird from 'bluebird';
 import * as _ from 'lodash';
 
-import Config, { ConfigChangeMap, ConfigKey, ConfigType } from './config';
+import Config, { ConfigType } from './config';
 import DB from './db';
 import { EventTracker } from './event-tracker';
 import Docker from './lib/docker-utils';
@@ -16,6 +16,7 @@ import {
 } from './logging';
 import LogMonitor from './logging/monitor';
 
+import * as globalEventBus from './event-bus';
 import log from './lib/supervisor-console';
 
 interface LoggerSetupOptions {
@@ -72,40 +73,27 @@ export class Logger {
 
 		// Only setup a config listener if we have to
 		if (!this.balenaBackend.isIntialised()) {
-			const handler = async (values: ConfigChangeMap<ConfigKey>) => {
-				if (
-					'uuid' in values ||
-					'apiEndpoint' in values ||
-					'deviceApiKey' in values
-				) {
-					// If any of the values we're interested in have
-					// changed, retrieve all of the values, check that
-					// they're all set, and provide them to the
-					// balenaBackend
+			globalEventBus.getInstance().once('deviceProvisioned', async () => {
+				const conf = await config.getMany([
+					'uuid',
+					'apiEndpoint',
+					'deviceApiKey',
+				]);
 
-					const conf = await config.getMany([
-						'uuid',
-						'apiEndpoint',
-						'deviceApiKey',
-					]);
-
-					// We use Boolean here, as deviceApiKey when unset
-					// is '' for legacy reasons. Once we're totally
-					// typescript, we can make it have a default value
-					// of undefined.
-					if (_.every(conf, Boolean)) {
-						// Everything is set, provide the values to the
-						// balenaBackend, and remove our listener
-						this.balenaBackend!.assignFields(
-							conf.apiEndpoint,
-							conf.uuid!,
-							conf.deviceApiKey,
-						);
-						config.removeListener('change', handler);
-					}
+				// We use Boolean here, as deviceApiKey when unset
+				// is '' for legacy reasons. Once we're totally
+				// typescript, we can make it have a default value
+				// of undefined.
+				if (_.every(conf, Boolean)) {
+					// Everything is set, provide the values to the
+					// balenaBackend, and remove our listener
+					this.balenaBackend!.assignFields(
+						conf.apiEndpoint,
+						conf.uuid!,
+						conf.deviceApiKey,
+					);
 				}
-			};
-			config.on('change', handler);
+			});
 		}
 	}
 
diff --git a/src/supervisor.ts b/src/supervisor.ts
index 75bfe2ae..6ce441dd 100644
--- a/src/supervisor.ts
+++ b/src/supervisor.ts
@@ -87,9 +87,6 @@ export class Supervisor {
 		log.debug('Starting event tracker');
 		await this.eventTracker.init(conf);
 
-		log.debug('Starting api binder');
-		await this.apiBinder.initClient();
-
 		log.debug('Starting logging infrastructure');
 		this.logger.init({
 			enableLogs: conf.loggingEnabled,
@@ -97,6 +94,9 @@ export class Supervisor {
 			...conf,
 		});
 
+		log.debug('Starting api binder');
+		await this.apiBinder.initClient();
+
 		this.logger.logSystemMessage('Supervisor starting', {}, 'Supervisor start');
 		if (conf.legacyAppsPresent && this.apiBinder.balenaApi != null) {
 			log.info('Legacy app detected, running migration');