From 6e3bedeb1d1fe8e1facb2bb3330d2ecc499118b1 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Wed, 20 Mar 2019 16:48:20 -0700 Subject: [PATCH 1/2] fix: Return a promise when retrying provisioning to avoid continuing after a failure Otherwise we'll keep doing the rest of the APIBinder init steps, like reporting initial config, potentially before completing the provisioning. Change-type: patch Signed-off-by: Pablo Carranza Velez --- src/api-binder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api-binder.ts b/src/api-binder.ts index 718db6e7..60a85455 100644 --- a/src/api-binder.ts +++ b/src/api-binder.ts @@ -875,7 +875,7 @@ export class APIBinder { delay: retryDelay, }); await Bluebird.delay(retryDelay); - this.provisionOrRetry(retryDelay); + return this.provisionOrRetry(retryDelay); } } From 22a5b331969af7e8f84bc8b5054ff80ff6c33dad Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Wed, 20 Mar 2019 16:59:13 -0700 Subject: [PATCH 2/2] fix: When pinning a preloaded device, ensure the pinning is done when retrying after a failure Without this patch, if for some reason device pinning fails (e.g. connectivity goes down) or anything interrupts the initialization after provisioning completes but before pinning is completed, after a retry the supervisor would just skip the pinning code, leaving the device unpinned. This patch ensures that the pinning procedure is run even if the device was already provisioned (as long as the pinning flag has been set, of course). This matches the behavior that the CoffeeScript code had from before the TypeScript conversion. Change-type: patch Signed-off-by: Pablo Carranza Velez --- src/api-binder.ts | 94 +++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/src/api-binder.ts b/src/api-binder.ts index 60a85455..957720e5 100644 --- a/src/api-binder.ts +++ b/src/api-binder.ts @@ -796,59 +796,57 @@ export class APIBinder { // FIXME: Config typing const opts = await this.config.get('provisioningOptions'); if ( - opts.registered_at != null && - opts.deviceId != null && - opts.provisioningApiKey == null + opts.registered_at == null || + opts.deviceId == null || + opts.provisioningApiKey != null ) { - return; - } - - if (opts.registered_at != null && opts.deviceId == null) { - console.log( - 'Device is registered but no device id available, attempting key exchange', - ); - device = (await this.exchangeKeyAndGetDeviceOrRegenerate(opts)) || null; - } else if (opts.registered_at == null) { - console.log('New device detected. Provisioning...'); - try { - device = await deviceRegister.register(opts).timeout(opts.apiTimeout); - } catch (err) { - if (DuplicateUuidError(err)) { - console.log('UUID already registered, trying a key exchange'); - device = await this.exchangeKeyAndGetDeviceOrRegenerate(opts); - } else { - throw err; + if (opts.registered_at != null && opts.deviceId == null) { + console.log( + 'Device is registered but no device id available, attempting key exchange', + ); + device = (await this.exchangeKeyAndGetDeviceOrRegenerate(opts)) || null; + } else if (opts.registered_at == null) { + console.log('New device detected. Provisioning...'); + try { + device = await deviceRegister.register(opts).timeout(opts.apiTimeout); + } catch (err) { + if (DuplicateUuidError(err)) { + console.log('UUID already registered, trying a key exchange'); + device = await this.exchangeKeyAndGetDeviceOrRegenerate(opts); + } else { + throw err; + } } + opts.registered_at = Date.now(); + } else if (opts.provisioningApiKey != null) { + console.log( + 'Device is registered but we still have an apiKey, attempting key exchange', + ); + device = await this.exchangeKeyAndGetDevice(opts); } - opts.registered_at = Date.now(); - } else if (opts.provisioningApiKey != null) { - console.log( - 'Device is registered but we still have an apiKey, attempting key exchange', - ); - device = await this.exchangeKeyAndGetDevice(opts); - } - if (!device) { - // TODO: Type this? - throw new Error(`Failed to provision device!`); - } - const { id } = device; - if (!this.balenaApi) { - throw new InternalInconsistencyError( - 'Attempting to provision a device without an initialized API client', - ); - } - this.balenaApi.passthrough.headers.Authorization = `Bearer ${ - opts.deviceApiKey - }`; + if (!device) { + // TODO: Type this? + throw new Error(`Failed to provision device!`); + } + const { id } = device; + if (!this.balenaApi) { + throw new InternalInconsistencyError( + 'Attempting to provision a device without an initialized API client', + ); + } + this.balenaApi.passthrough.headers.Authorization = `Bearer ${ + opts.deviceApiKey + }`; - const configToUpdate = { - registered_at: opts.registered_at, - deviceId: id, - apiKey: null, - }; - await this.config.set(configToUpdate); - this.eventTracker.track('Device bootstrap success'); + const configToUpdate = { + registered_at: opts.registered_at, + deviceId: id, + apiKey: null, + }; + await this.config.set(configToUpdate); + this.eventTracker.track('Device bootstrap success'); + } // Now check if we need to pin the device const pinValue = await this.config.get('pinDevice');