mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-02-21 18:06:47 +00:00
Merge pull request #1015 from balena-io/1014-delay-logstream
Dont setup a logstream until we're provisioned
This commit is contained in:
commit
e46fec2118
81
package-lock.json
generated
81
package-lock.json
generated
@ -243,9 +243,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/dockerode": {
|
||||
"version": "2.5.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-2.5.13.tgz",
|
||||
"integrity": "sha512-TgSP2nhCZgKOYcuMyuUs1SvLWZCd20z6SczPadLL11iCEEMDiblE23cwIyc1BR7FPpntwT9Z+IcdFNAXUAKmKQ==",
|
||||
"version": "2.5.20",
|
||||
"resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-2.5.20.tgz",
|
||||
"integrity": "sha512-g2eM9q+pur7iZc897K/OSq8sCL7VdVcCPzNkdeTukUokfvgl3TaP+nT7G8BMpnSSojrJFKl7VdTciP7hbVgfKA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
@ -1609,8 +1609,17 @@
|
||||
"deep-equal": "^1.0.1",
|
||||
"dns-equal": "^1.0.0",
|
||||
"dns-txt": "^2.0.2",
|
||||
"multicast-dns": "git+https://github.com/resin-io-modules/multicast-dns.git#a15c63464eb43e8925b187ed5cb9de6892e8aacc",
|
||||
"multicast-dns-service-types": "^1.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"multicast-dns": {
|
||||
"version": "git+https://github.com/resin-io-modules/multicast-dns.git#a15c63464eb43e8925b187ed5cb9de6892e8aacc",
|
||||
"from": "git+https://github.com/resin-io-modules/multicast-dns.git#a15c63464eb43e8925b187ed5cb9de6892e8aacc",
|
||||
"requires": {
|
||||
"dns-packet": "^1.0.1",
|
||||
"thunky": "^0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"brace-expansion": {
|
||||
@ -2794,16 +2803,6 @@
|
||||
"integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=",
|
||||
"dev": true
|
||||
},
|
||||
"dns-packet": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz",
|
||||
"integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ip": "^1.1.0",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"dns-txt": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
|
||||
@ -3993,8 +3992,7 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@ -4015,14 +4013,12 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -4037,14 +4033,12 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
@ -4166,8 +4160,7 @@
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@ -4179,7 +4172,6 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -4194,7 +4186,6 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@ -4202,8 +4193,7 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
@ -4307,8 +4297,7 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@ -4460,7 +4449,6 @@
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@ -7319,15 +7307,6 @@
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
||||
"dev": true
|
||||
},
|
||||
"multicast-dns": {
|
||||
"version": "git+https://github.com/resin-io-modules/multicast-dns.git#a15c63464eb43e8925b187ed5cb9de6892e8aacc",
|
||||
"from": "git+https://github.com/resin-io-modules/multicast-dns.git#listen-on-all-interfaces",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"dns-packet": "^1.0.1",
|
||||
"thunky": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"multicast-dns-service-types": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
|
||||
@ -10074,12 +10053,6 @@
|
||||
"xtend": "~4.0.1"
|
||||
}
|
||||
},
|
||||
"thunky": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz",
|
||||
"integrity": "sha1-vzAUaCTituZ7Dy16Ssi+smkIaE4=",
|
||||
"dev": true
|
||||
},
|
||||
"tildify": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz",
|
||||
@ -10810,14 +10783,12 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -10837,8 +10808,7 @@
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
@ -10986,7 +10956,6 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@ -10994,8 +10963,7 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
@ -11099,8 +11067,7 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -40,7 +40,7 @@
|
||||
"@types/bluebird": "^3.5.25",
|
||||
"@types/chai": "^4.1.7",
|
||||
"@types/common-tags": "^1.8.0",
|
||||
"@types/dockerode": "^2.5.13",
|
||||
"@types/dockerode": "^2.5.20",
|
||||
"@types/event-stream": "^3.3.34",
|
||||
"@types/express": "^4.11.1",
|
||||
"@types/knex": "^0.14.14",
|
||||
|
@ -9,7 +9,7 @@ import { PinejsClientRequest, StatusError } from 'pinejs-client-request';
|
||||
import * as deviceRegister from 'resin-register-device';
|
||||
import * as url from 'url';
|
||||
|
||||
import Config from './config';
|
||||
import Config, { ConfigType } from './config';
|
||||
import Database from './db';
|
||||
import DeviceConfig from './device-config';
|
||||
import { EventTracker } from './event-tracker';
|
||||
@ -25,10 +25,10 @@ import { request, requestOpts } from './lib/request';
|
||||
import { writeLock } from './lib/update-lock';
|
||||
import { DeviceApplicationState } from './types/state';
|
||||
|
||||
import { SchemaReturn as ConfigSchemaType } from './config/schema-type';
|
||||
|
||||
import log from './lib/supervisor-console';
|
||||
|
||||
import DeviceState = require('./device-state');
|
||||
|
||||
const REPORT_SUCCESS_DELAY = 1000;
|
||||
const MAX_REPORT_RETRY_DELAY = 60000;
|
||||
|
||||
@ -42,11 +42,7 @@ export interface APIBinderConstructOpts {
|
||||
config: Config;
|
||||
// FIXME: Remove this
|
||||
db: Database;
|
||||
// TODO: Typings
|
||||
deviceState: {
|
||||
deviceConfig: DeviceConfig;
|
||||
[key: string]: any;
|
||||
};
|
||||
deviceState: DeviceState;
|
||||
eventTracker: EventTracker;
|
||||
}
|
||||
|
||||
@ -67,7 +63,7 @@ interface DeviceTag {
|
||||
value: string;
|
||||
}
|
||||
|
||||
type KeyExchangeOpts = ConfigSchemaType<'provisioningOptions'>;
|
||||
type KeyExchangeOpts = ConfigType<'provisioningOptions'>;
|
||||
|
||||
export class APIBinder {
|
||||
public router: express.Router;
|
||||
@ -79,7 +75,7 @@ export class APIBinder {
|
||||
};
|
||||
private eventTracker: EventTracker;
|
||||
|
||||
private balenaApi: PinejsClientRequest | null = null;
|
||||
public balenaApi: PinejsClientRequest | null = null;
|
||||
private cachedBalenaApi: PinejsClientRequest | null = null;
|
||||
private lastReportedState: DeviceApplicationState = {
|
||||
local: {},
|
||||
@ -532,7 +528,9 @@ export class APIBinder {
|
||||
// the watchdog to kill the supervisor - and killing the supervisor will
|
||||
// not help in this situation
|
||||
log.error(
|
||||
'Non-200 response from the API! Status code: ${e.statusCode} - message:',
|
||||
`Non-200 response from the API! Status code: ${
|
||||
e.statusCode
|
||||
} - message:`,
|
||||
e,
|
||||
);
|
||||
} else {
|
||||
@ -972,3 +970,5 @@ export class APIBinder {
|
||||
return router;
|
||||
}
|
||||
}
|
||||
|
||||
export default APIBinder;
|
||||
|
@ -1,15 +0,0 @@
|
||||
do ->
|
||||
# Make NodeJS RFC 3484 compliant for properly handling IPv6
|
||||
# See: https://github.com/nodejs/node/pull/14731
|
||||
# https://github.com/nodejs/node/pull/17793
|
||||
dns = require('dns')
|
||||
{ lookup } = dns
|
||||
dns.lookup = (name, opts, cb) ->
|
||||
if typeof cb isnt 'function'
|
||||
return lookup(name, { verbatim: true }, opts)
|
||||
return lookup(name, Object.assign({ verbatim: true }, opts), cb)
|
||||
|
||||
Supervisor = require './supervisor'
|
||||
|
||||
supervisor = new Supervisor()
|
||||
supervisor.init()
|
21
src/app.ts
Normal file
21
src/app.ts
Normal file
@ -0,0 +1,21 @@
|
||||
// This was originally wrapped in a do block in
|
||||
// coffeescript, and it's not clear now why that was the
|
||||
// case, so I'm going to maintain that behaviour
|
||||
(() => {
|
||||
// Make NodeJS RFC 3484 compliant for properly handling IPv6
|
||||
// See: https://github.com/nodejs/node/pull/14731
|
||||
// https://github.com/nodejs/node/pull/17793
|
||||
const dns = require('dns');
|
||||
const { lookup } = dns;
|
||||
dns.lookup = (name: string, opts: any, cb: (err?: Error) => void) => {
|
||||
if (typeof cb !== 'function') {
|
||||
return lookup(name, { verbatim: true }, opts);
|
||||
}
|
||||
return lookup(name, Object.assign({ verbatim: true }, opts), cb);
|
||||
};
|
||||
})();
|
||||
|
||||
import Supervisor from './supervisor';
|
||||
|
||||
const supervisor = new Supervisor();
|
||||
supervisor.init();
|
2
src/application-manager.d.ts
vendored
2
src/application-manager.d.ts
vendored
@ -45,6 +45,8 @@ export class ApplicationManager extends EventEmitter {
|
||||
public db: DB;
|
||||
public images: Images;
|
||||
|
||||
public proxyvisor: any;
|
||||
|
||||
public getCurrentApp(appId: number): Bluebird<Application | null>;
|
||||
|
||||
// TODO: This actually returns an object, but we don't need the values just yet
|
||||
|
@ -23,14 +23,20 @@ import {
|
||||
|
||||
interface ConfigOpts {
|
||||
db: DB;
|
||||
configPath: string;
|
||||
configPath?: string;
|
||||
}
|
||||
|
||||
type ConfigMap<T extends SchemaTypeKey> = { [key in T]: SchemaReturn<key> };
|
||||
type ConfigChangeMap<T extends SchemaTypeKey> = {
|
||||
export type ConfigMap<T extends SchemaTypeKey> = {
|
||||
[key in T]: SchemaReturn<key>
|
||||
};
|
||||
export type ConfigChangeMap<T extends SchemaTypeKey> = {
|
||||
[key in T]?: SchemaReturn<key>
|
||||
};
|
||||
|
||||
// Export this type renamed, for storing config keys
|
||||
export type ConfigKey = SchemaTypeKey;
|
||||
export type ConfigType<T extends ConfigKey> = SchemaReturn<T>;
|
||||
|
||||
interface ConfigEvents {
|
||||
change: ConfigChangeMap<SchemaTypeKey>;
|
||||
}
|
||||
|
31
src/device-state.d.ts
vendored
Normal file
31
src/device-state.d.ts
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { Router } from 'express';
|
||||
|
||||
import ApplicationManager from './application-manager';
|
||||
import Config from './config';
|
||||
import Database from './db';
|
||||
import DeviceConfig from './device-config';
|
||||
import EventTracker from './event-tracker';
|
||||
import Logger from './logger';
|
||||
|
||||
// This is a very incomplete definition of the device state
|
||||
// class, which should be rewritten in typescript soon
|
||||
class DeviceState extends EventEmitter {
|
||||
public applications: ApplicationManager;
|
||||
public router: Router;
|
||||
public deviceConfig: DeviceConfig;
|
||||
|
||||
public constructor(args: {
|
||||
config: Config;
|
||||
db: Database;
|
||||
eventTracker: EventTracker;
|
||||
logger: Logger;
|
||||
});
|
||||
|
||||
public healthcheck(): Promise<void>;
|
||||
public normaliseLegacy(client: PinejsClientRequest): Promise<void>;
|
||||
|
||||
public async init();
|
||||
}
|
||||
|
||||
export = DeviceState;
|
@ -5,16 +5,17 @@ import * as memoizee from 'memoizee';
|
||||
|
||||
import Mixpanel = require('mixpanel');
|
||||
|
||||
import { ConfigType } from './config';
|
||||
import log from './lib/supervisor-console';
|
||||
import supervisorVersion = require('./lib/supervisor-version');
|
||||
|
||||
export type EventTrackProperties = Dictionary<any>;
|
||||
|
||||
interface InitArgs {
|
||||
uuid: string;
|
||||
unmanaged: boolean;
|
||||
mixpanelHost: { host: string; path: string } | null;
|
||||
mixpanelToken: string;
|
||||
uuid: ConfigType<'uuid'>;
|
||||
unmanaged: ConfigType<'unmanaged'>;
|
||||
mixpanelHost: ConfigType<'mixpanelHost'>;
|
||||
mixpanelToken: ConfigType<'mixpanelToken'>;
|
||||
}
|
||||
|
||||
// The minimum amount of time to wait between sending
|
||||
@ -112,3 +113,5 @@ export class EventTracker {
|
||||
return _.merge({}, properties, this.defaultProperties);
|
||||
}
|
||||
}
|
||||
|
||||
export default EventTracker;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import Config, { ConfigChangeMap, ConfigKey, ConfigType } from './config';
|
||||
import DB from './db';
|
||||
import { EventTracker } from './event-tracker';
|
||||
import Docker from './lib/docker-utils';
|
||||
@ -18,12 +19,14 @@ import LogMonitor from './logging/monitor';
|
||||
import log from './lib/supervisor-console';
|
||||
|
||||
interface LoggerSetupOptions {
|
||||
apiEndpoint: string;
|
||||
uuid: string;
|
||||
deviceApiKey: string;
|
||||
unmanaged: boolean;
|
||||
apiEndpoint: ConfigType<'apiEndpoint'>;
|
||||
uuid: ConfigType<'uuid'>;
|
||||
deviceApiKey: ConfigType<'deviceApiKey'>;
|
||||
unmanaged: ConfigType<'unmanaged'>;
|
||||
localMode: ConfigType<'localMode'>;
|
||||
|
||||
enableLogs: boolean;
|
||||
localMode: boolean;
|
||||
config: Config;
|
||||
}
|
||||
|
||||
type LogEventObject = Dictionary<any> | null;
|
||||
@ -57,6 +60,7 @@ export class Logger {
|
||||
unmanaged,
|
||||
enableLogs,
|
||||
localMode,
|
||||
config,
|
||||
}: LoggerSetupOptions) {
|
||||
this.balenaBackend = new BalenaLogBackend(apiEndpoint, uuid, deviceApiKey);
|
||||
this.localBackend = new LocalLogBackend();
|
||||
@ -65,6 +69,44 @@ export class Logger {
|
||||
|
||||
this.backend.unmanaged = unmanaged;
|
||||
this.backend.publishEnabled = enableLogs;
|
||||
|
||||
// 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
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
config.on('change', handler);
|
||||
}
|
||||
}
|
||||
|
||||
public switchBackend(localMode: boolean) {
|
||||
|
@ -31,17 +31,18 @@ export class BalenaLogBackend extends LogBackend {
|
||||
private stream: stream.PassThrough;
|
||||
private timeout: NodeJS.Timer;
|
||||
|
||||
public constructor(apiEndpoint: string, uuid: string, deviceApiKey: string) {
|
||||
public initialised = false;
|
||||
|
||||
public constructor(
|
||||
apiEndpoint: string,
|
||||
uuid: Nullable<string>,
|
||||
deviceApiKey: string,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.opts = url.parse(`${apiEndpoint}/device/v2/${uuid}/log-stream`) as any;
|
||||
this.opts.method = 'POST';
|
||||
this.opts.headers = {
|
||||
Authorization: `Bearer ${deviceApiKey}`,
|
||||
'Content-Type': 'application/x-ndjson',
|
||||
'Content-Encoding': 'gzip',
|
||||
};
|
||||
|
||||
if (uuid != null && deviceApiKey !== '') {
|
||||
this.assignFields(apiEndpoint, uuid, deviceApiKey);
|
||||
}
|
||||
// This stream serves serves as a message buffer during reconnections
|
||||
// while we unpipe the old, malfunctioning connection and then repipe a
|
||||
// new one.
|
||||
@ -71,8 +72,15 @@ export class BalenaLogBackend extends LogBackend {
|
||||
});
|
||||
}
|
||||
|
||||
public isIntialised(): boolean {
|
||||
return this.initialised;
|
||||
}
|
||||
|
||||
public log(message: LogMessage) {
|
||||
if (this.unmanaged || !this.publishEnabled) {
|
||||
// TODO: Perhaps don't just drop logs when we haven't
|
||||
// yet initialised (this happens when a device has not yet
|
||||
// been provisioned)
|
||||
if (this.unmanaged || !this.publishEnabled || !this.initialised) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -100,6 +108,18 @@ export class BalenaLogBackend extends LogBackend {
|
||||
this.write(message);
|
||||
}
|
||||
|
||||
public assignFields(apiEndpoint: string, uuid: string, deviceApiKey: string) {
|
||||
this.opts = url.parse(`${apiEndpoint}/device/v2/${uuid}/log-stream`) as any;
|
||||
this.opts.method = 'POST';
|
||||
this.opts.headers = {
|
||||
Authorization: `Bearer ${deviceApiKey}`,
|
||||
'Content-Type': 'application/x-ndjson',
|
||||
'Content-Encoding': 'gzip',
|
||||
};
|
||||
|
||||
this.initialised = true;
|
||||
}
|
||||
|
||||
private setup = _.throttle(() => {
|
||||
this.req = https.request(this.opts);
|
||||
|
||||
|
@ -1,101 +0,0 @@
|
||||
EventEmitter = require 'events'
|
||||
|
||||
{ EventTracker } = require './event-tracker'
|
||||
{ DB } = require './db'
|
||||
{ Config } = require './config'
|
||||
{ APIBinder } = require './api-binder'
|
||||
DeviceState = require './device-state'
|
||||
{ SupervisorAPI } = require './supervisor-api'
|
||||
{ Logger } = require './logger'
|
||||
|
||||
version = require './lib/supervisor-version'
|
||||
{ log } = require './lib/supervisor-console'
|
||||
|
||||
constants = require './lib/constants'
|
||||
|
||||
startupConfigFields = [
|
||||
'uuid'
|
||||
'listenPort'
|
||||
'apiEndpoint'
|
||||
'apiSecret'
|
||||
'apiTimeout'
|
||||
'unmanaged'
|
||||
'deviceApiKey'
|
||||
'mixpanelToken'
|
||||
'mixpanelHost'
|
||||
'loggingEnabled'
|
||||
'localMode'
|
||||
'legacyAppsPresent'
|
||||
]
|
||||
|
||||
module.exports = class Supervisor extends EventEmitter
|
||||
constructor: ->
|
||||
@db = new DB()
|
||||
@config = new Config({ @db })
|
||||
@eventTracker = new EventTracker()
|
||||
@logger = new Logger({ @db, @eventTracker })
|
||||
@deviceState = new DeviceState({ @config, @db, @eventTracker, @logger })
|
||||
@apiBinder = new APIBinder({ @config, @db, @deviceState, @eventTracker })
|
||||
|
||||
# FIXME: rearchitect proxyvisor to avoid this circular dependency
|
||||
# by storing current state and having the APIBinder query and report it / provision devices
|
||||
@deviceState.applications.proxyvisor.bindToAPI(@apiBinder)
|
||||
# We could also do without the below dependency, but it's part of a much larger refactor
|
||||
@deviceState.applications.apiBinder = @apiBinder
|
||||
|
||||
@api = new SupervisorAPI({
|
||||
@config,
|
||||
@eventTracker,
|
||||
routers: [
|
||||
@apiBinder.router,
|
||||
@deviceState.router
|
||||
],
|
||||
healthchecks: [
|
||||
@apiBinder.healthcheck.bind(@apiBinder),
|
||||
@deviceState.healthcheck.bind(@deviceState)
|
||||
]
|
||||
})
|
||||
|
||||
init: =>
|
||||
|
||||
log.info("Supervisor v#{version} starting up...")
|
||||
|
||||
@db.init()
|
||||
.tap =>
|
||||
@config.init() # Ensures uuid, deviceApiKey, apiSecret
|
||||
.then =>
|
||||
@config.getMany(startupConfigFields)
|
||||
.then (conf) =>
|
||||
# We can't print to the dashboard until the logger has started up,
|
||||
# so we leave a trail of breadcrumbs in the logs in case runtime
|
||||
# fails to get to the first dashboard logs
|
||||
log.debug('Starting event tracker')
|
||||
@eventTracker.init(conf)
|
||||
.then =>
|
||||
log.debug('Starting up api binder')
|
||||
@apiBinder.initClient()
|
||||
.then =>
|
||||
log.debug('Starting logging infrastructure')
|
||||
@logger.init({
|
||||
apiEndpoint: conf.apiEndpoint,
|
||||
uuid: conf.uuid,
|
||||
deviceApiKey: conf.deviceApiKey,
|
||||
unmanaged: conf.unmanaged,
|
||||
enableLogs: conf.loggingEnabled,
|
||||
localMode: conf.localMode
|
||||
})
|
||||
.then =>
|
||||
@logger.logSystemMessage('Supervisor starting', {}, 'Supervisor start')
|
||||
.then =>
|
||||
if conf.legacyAppsPresent
|
||||
log.info('Legacy app detected, running migration')
|
||||
@deviceState.normaliseLegacy(@apiBinder.balenaApi)
|
||||
.then =>
|
||||
@deviceState.init()
|
||||
.then =>
|
||||
# initialize API
|
||||
log.info('Starting API server')
|
||||
@api.listen(constants.allowedInterfaces, conf.listenPort, conf.apiTimeout)
|
||||
@deviceState.on('shutdown', => @api.stop())
|
||||
.then =>
|
||||
@apiBinder.start() # this will first try to provision if it's a new device
|
118
src/supervisor.ts
Normal file
118
src/supervisor.ts
Normal file
@ -0,0 +1,118 @@
|
||||
import APIBinder from './api-binder';
|
||||
import Config, { ConfigKey } from './config';
|
||||
import Database from './db';
|
||||
import EventTracker from './event-tracker';
|
||||
import Logger from './logger';
|
||||
import SupervisorAPI from './supervisor-api';
|
||||
|
||||
import DeviceState = require('./device-state');
|
||||
|
||||
import constants = require('./lib/constants');
|
||||
import log from './lib/supervisor-console';
|
||||
import version = require('./lib/supervisor-version');
|
||||
|
||||
const startupConfigFields: ConfigKey[] = [
|
||||
'uuid',
|
||||
'listenPort',
|
||||
'apiEndpoint',
|
||||
'apiSecret',
|
||||
'apiTimeout',
|
||||
'unmanaged',
|
||||
'deviceApiKey',
|
||||
'mixpanelToken',
|
||||
'mixpanelHost',
|
||||
'loggingEnabled',
|
||||
'localMode',
|
||||
'legacyAppsPresent',
|
||||
];
|
||||
|
||||
export class Supervisor {
|
||||
private db: Database;
|
||||
private config: Config;
|
||||
private eventTracker: EventTracker;
|
||||
private logger: Logger;
|
||||
private deviceState: DeviceState;
|
||||
private apiBinder: APIBinder;
|
||||
private api: SupervisorAPI;
|
||||
|
||||
public constructor() {
|
||||
this.db = new Database();
|
||||
this.config = new Config({ db: this.db });
|
||||
this.eventTracker = new EventTracker();
|
||||
this.logger = new Logger({ db: this.db, eventTracker: this.eventTracker });
|
||||
this.deviceState = new DeviceState({
|
||||
config: this.config,
|
||||
db: this.db,
|
||||
eventTracker: this.eventTracker,
|
||||
logger: this.logger,
|
||||
});
|
||||
this.apiBinder = new APIBinder({
|
||||
config: this.config,
|
||||
db: this.db,
|
||||
deviceState: this.deviceState,
|
||||
eventTracker: this.eventTracker,
|
||||
});
|
||||
|
||||
// FIXME: rearchitect proxyvisor to avoid this circular dependency
|
||||
// by storing current state and having the APIBinder query and report it / provision devices
|
||||
this.deviceState.applications.proxyvisor.bindToAPI(this.apiBinder);
|
||||
// We could also do without the below dependency, but it's part of a much larger refactor
|
||||
this.deviceState.applications.apiBinder = this.apiBinder;
|
||||
|
||||
this.api = new SupervisorAPI({
|
||||
config: this.config,
|
||||
eventTracker: this.eventTracker,
|
||||
routers: [this.apiBinder.router, this.deviceState.router],
|
||||
healthchecks: [
|
||||
this.apiBinder.healthcheck.bind(this.apiBinder),
|
||||
this.deviceState.healthcheck.bind(this.deviceState),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
public async init() {
|
||||
log.info(`Supervisor v${version} starting up...`);
|
||||
|
||||
await this.db.init();
|
||||
await this.config.init();
|
||||
|
||||
const conf = await this.config.getMany(startupConfigFields);
|
||||
|
||||
// We can't print to the dashboard until the logger
|
||||
// has started up, so we leave a trail of breadcrumbs
|
||||
// in the logs in case runtime fails to get to the
|
||||
// first dashboard logs
|
||||
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,
|
||||
config: this.config,
|
||||
...conf,
|
||||
});
|
||||
|
||||
this.logger.logSystemMessage('Supervisor starting', {}, 'Supervisor start');
|
||||
if (conf.legacyAppsPresent) {
|
||||
log.info('Legacy app detected, running migration');
|
||||
this.deviceState.normaliseLegacy(this.apiBinder.balenaApi);
|
||||
}
|
||||
|
||||
await this.deviceState.init();
|
||||
|
||||
log.info('Starting API server');
|
||||
this.api.listen(
|
||||
constants.allowedInterfaces,
|
||||
conf.listenPort,
|
||||
conf.apiTimeout,
|
||||
);
|
||||
this.deviceState.on('shutdown', () => this.api.stop());
|
||||
|
||||
await this.apiBinder.start();
|
||||
}
|
||||
}
|
||||
|
||||
export default Supervisor;
|
@ -1,6 +1,6 @@
|
||||
{ expect } = require './lib/chai-config'
|
||||
|
||||
Supervisor = require '../src/supervisor'
|
||||
{ Supervisor } = require '../src/supervisor'
|
||||
|
||||
describe 'Startup', ->
|
||||
it 'should startup correctly', ->
|
||||
|
@ -69,7 +69,7 @@ module.exports = function(env) {
|
||||
return {
|
||||
mode: env == null || !env.noOptimize ? 'production' : 'development',
|
||||
devtool: 'none',
|
||||
entry: './src/app.coffee',
|
||||
entry: './src/app.ts',
|
||||
output: {
|
||||
filename: 'app.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
|
Loading…
x
Reference in New Issue
Block a user