Auto-merge for PR #638 via VersionBot

More typescript conversions
This commit is contained in:
resin-io-versionbot[bot] 2018-05-02 12:18:19 +00:00 committed by GitHub
commit 6229e3ee61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 163 additions and 77 deletions

View File

@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file
automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
This project adheres to [Semantic Versioning](http://semver.org/).
## v7.5.3 - 2018-05-02
* Add typescript linting to supervisor tests #638 [Cameron Diver]
* Convert conversions module to typescript #638 [Cameron Diver]
* Move shared types to separate module #638 [Cameron Diver]
* Convert blink module to typescript #638 [Cameron Diver]
* Convert lib/constants module to typescript #638 [Cameron Diver]
* Type parameters for validation functions better #638 [Cameron Diver]
## v7.5.2 - 2018-05-01
* Add some more unit tests to the multicontainer supervisor #519 [Pablo Carranza Velez]

View File

@ -41,6 +41,7 @@ RUN JOBS=MAX npm install --no-optional --unsafe-perm
COPY webpack.config.js fix-jsonstream.js hardcode-migrations.js tsconfig.json /usr/src/app/
COPY src /usr/src/app/src
COPY test /usr/src/app/test
COPY typings /usr/src/app/typings
RUN npm test \
&& npm run build

View File

@ -1,7 +1,7 @@
{
"name": "resin-supervisor",
"description": "This is resin.io's Supervisor, a program that runs on IoT devices and has the task of running user Apps (which are Docker containers), and updating them as Resin's API informs it to.",
"version": "7.5.2",
"version": "7.5.3",
"license": "Apache-2.0",
"repository": {
"type": "git",
@ -10,7 +10,7 @@
"scripts": {
"start": "./entry.sh",
"build": "webpack",
"lint": "resin-lint src/ test/",
"lint": "resin-lint --typescript src/ test/",
"test": "npm run lint && JUNIT_REPORT_PATH=report.xml mocha --exit -r ts-node/register -r coffee-script/register -r register-coffee-coverage test/*.{js,coffee} && npm run coverage",
"versionist": "versionist",
"coverage": "istanbul report text && istanbul report html"
@ -58,7 +58,7 @@
"pubnub": "^3.7.13",
"register-coffee-coverage": "0.0.1",
"request": "^2.51.0",
"resin-lint": "^1.3.1",
"resin-lint": "^1.5.7",
"resin-register-device": "^3.0.0",
"resin-sync": "^9.3.0",
"resumable-request": "^2.0.0",

View File

@ -1,2 +0,0 @@
constants = require './constants'
module.exports = require('blinking')(constants.ledFile)

5
src/lib/blink.ts Normal file
View File

@ -0,0 +1,5 @@
import blinking = require('blinking');
import constants = require('./constants');
export = blinking(constants.ledFile);

View File

@ -1,43 +0,0 @@
{ checkString } = require './validation'
bootMountPointFromEnv = checkString(process.env.BOOT_MOUNTPOINT)
rootMountPoint = checkString(process.env.ROOT_MOUNTPOINT) ? '/mnt/root'
supervisorNetworkInterface = 'supervisor0'
module.exports =
rootMountPoint: rootMountPoint
databasePath: checkString(process.env.DATABASE_PATH) ? '/data/database.sqlite'
dockerSocket: process.env.DOCKER_SOCKET ? '/var/run/docker.sock'
supervisorImage: checkString(process.env.SUPERVISOR_IMAGE) ? 'resin/rpi-supervisor'
ledFile: checkString(process.env.LED_FILE) ? '/sys/class/leds/led0/brightness'
forceSecret: # Only used for development
api: checkString(process.env.RESIN_SUPERVISOR_SECRET) ? null
logsChannel: checkString(process.env.RESIN_SUPERVISOR_LOGS_CHANNEL) ? null
vpnStatusPath: checkString(process.env.VPN_STATUS_PATH) ? "#{rootMountPoint}/run/openvpn/vpn_status"
hostOSVersionPath: checkString(process.env.HOST_OS_VERSION_PATH) ? "#{rootMountPoint}/etc/os-release"
privateAppEnvVars: [
'RESIN_SUPERVISOR_API_KEY'
'RESIN_API_KEY'
]
dataPath: checkString(process.env.RESIN_DATA_PATH) ? '/resin-data'
bootMountPointFromEnv: bootMountPointFromEnv
bootMountPoint: bootMountPointFromEnv ? '/boot'
configJsonPathOnHost: checkString(process.env.CONFIG_JSON_PATH)
proxyvisorHookReceiver: checkString(process.env.RESIN_PROXYVISOR_HOOK_RECEIVER) ? 'http://0.0.0.0:1337'
apiEndpointFromEnv: checkString(process.env.API_ENDPOINT)
configJsonNonAtomicPath: '/boot/config.json'
defaultPubnubSubscribeKey: process.env.DEFAULT_PUBNUB_SUBSCRIBE_KEY
defaultPubnubPublishKey: process.env.DEFAULT_PUBNUB_PUBLISH_KEY
defaultMixpanelToken: process.env.DEFAULT_MIXPANEL_TOKEN
supervisorNetworkInterface: supervisorNetworkInterface
allowedInterfaces: ['resin-vpn', 'tun0', 'docker0', 'lo', supervisorNetworkInterface]
appsJsonPath: process.env.APPS_JSON_PATH ? '/boot/apps.json'
ipAddressUpdateInterval: 30 * 1000
imageCleanupErrorIgnoreTimeout: 3600 * 1000
maxDeltaDownloads: 3
defaultVolumeLabels: {
'io.resin.supervised': 'true'
}
process.env.DOCKER_HOST ?= "unix://#{module.exports.dockerSocket}"

48
src/lib/constants.ts Normal file
View File

@ -0,0 +1,48 @@
import { checkString } from './validation';
const bootMountPointFromEnv = checkString(process.env.BOOT_MOUNTPOINT);
const rootMountPoint = checkString(process.env.ROOT_MOUNTPOINT) || '/mnt/root';
const supervisorNetworkInterface = 'supervisor0';
const constants = {
rootMountPoint,
databasePath: checkString(process.env.DATABASE_PATH) || '/data/database.sqlite',
dockerSocket: process.env.DOCKER_SOCKET || '/var/run/docker.sock',
supervisorImage: checkString(process.env.SUPERVISOR_IMAGE) || 'resin/rpi-supervisor',
ledFile: checkString(process.env.LED_FILE) || '/sys/class/leds/led0/brightness',
vpnStatusPath:
checkString(process.env.VPN_STATUS_PATH) || `${rootMountPoint}/run/openvpn/vpn_status`,
hostOSVersionPath:
checkString(process.env.HOST_OS_VERSION_PATH) || `${rootMountPoint}/etc/os-release`,
privateAppEnvVars: [
'RESIN_SUPERVISOR_API_KEY',
'RESIN_API_KEY',
],
dataPath: checkString(process.env.RESIN_DATA_PATH) || '/resin-data',
bootMountPointFromEnv,
bootMountPoint: bootMountPointFromEnv || '/boot',
configJsonPathOnHost: checkString(process.env.CONFIG_JSON_PATH),
proxyvisorHookReceiver:
checkString(process.env.RESIN_PROXYVISOR_HOOK_RECEIVER) || 'http://0.0.0.0:1337',
apiEndpointFromEnv: checkString(process.env.API_ENDPOINT),
configJsonNonAtomicPath: '/boot/config.json',
defaultPubnubSubscribeKey: process.env.DEFAULT_PUBNUB_SUBSCRIBE_KEY,
defaultPubnubPublishKey: process.env.DEFAULT_PUBNUB_PUBLISH_KEY,
defaultMixpanelToken: process.env.DEFAULT_MIXPANEL_TOKEN,
supervisorNetworkInterface: supervisorNetworkInterface,
allowedInterfaces: [ 'resin-vpn', 'tun0', 'docker0', 'lo', supervisorNetworkInterface ],
appsJsonPath: process.env.APPS_JSON_PATH || '/boot/apps.json',
ipAddressUpdateInterval: 30 * 1000,
imageCleanupErrorIgnoreTimeout: 3600 * 1000,
maxDeltaDownloads: 3,
defaultVolumeLabels: {
'io.resin.supervised': 'true',
},
};
if (process.env.DOCKER_HOST == null) {
process.env.DOCKER_HOST = `unix://${constants.dockerSocket}`;
}
export = constants;

View File

@ -1,12 +0,0 @@
_ = require 'lodash'
exports.envArrayToObject = (env) ->
# env is an array of strings that say 'key=value'
toPair = (keyVal) ->
m = keyVal.match(/^([^=]+)=(.*)$/)
if !m?
console.log("WARNING: Could not correctly parse env var #{keyVal}. " +
'Please fix this var and recreate the container.')
return null
return m[1..]
_(env).map(toPair).filter(([_, v]) -> v?).fromPairs().value()

21
src/lib/conversions.ts Normal file
View File

@ -0,0 +1,21 @@
import * as _ from 'lodash';
import { EnvVarObject } from './types';
export function envArrayToObject(env: string[]): EnvVarObject {
const toPair = (keyVal: string) => {
const m = keyVal.match(/^([^=]+)=(.*)$/);
if (m == null) {
console.log(`WARNING: Could not correctly parse env var ${keyVal}. ` +
'Please fix this var and recreate the container.');
return [null, null];
}
return m.slice(1);
};
return _(env)
.map(toPair)
.filter(([_, v]) => v != null)
.fromPairs()
.value();
}

7
src/lib/types.ts Normal file
View File

@ -0,0 +1,7 @@
export interface EnvVarObject {
[name: string]: string;
}
export interface LabelObject {
[name: string]: string;
}

View File

@ -1,28 +1,24 @@
import * as _ from 'lodash';
import { inspect } from 'util';
import { EnvVarObject, LabelObject } from './types';
export interface CheckIntOptions {
positive?: boolean;
}
export interface EnvVarObject {
[name: string]: string;
}
export interface LabelObject {
[name: string]: string;
}
const ENV_VAR_KEY_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
const LABEL_NAME_REGEX = /^[a-zA-Z][a-zA-Z0-9\.\-]*$/;
type NullableString = string | undefined | null;
/**
* checkInt
*
* Check an input string as a number, optionally specifying a requirement
* to be positive
*/
export function checkInt(s: string | null, options: CheckIntOptions = {}): number | void {
export function checkInt(s: NullableString, options: CheckIntOptions = {}): number | void {
if (s == null) {
return;
}
@ -45,7 +41,7 @@ export function checkInt(s: string | null, options: CheckIntOptions = {}): numbe
*
* Check that a string exists, and is not an empty string, 'null', or 'undefined'
*/
export function checkString(s: string): string | void {
export function checkString(s: NullableString): string | void {
if (s == null || !_.isString(s) || _.includes([ 'null', 'undefined', '' ], s)) {
return;
}
@ -104,13 +100,13 @@ export function isValidEnv(obj: EnvVarObject): boolean {
return _.every(obj, (val, key) => {
if (!isValidShortText(key)) {
console.log('debug: Non-valid short text env var key passed to validation.isValidEnv');
console.log(`\tKey: ${inspect(key)}`)
console.log(`\tKey: ${inspect(key)}`);
return false;
}
if (!ENV_VAR_KEY_REGEX.test(key)) {
console.log('debug: Invalid env var key passed to validation.isValidEnv');
console.log(`\tKey: ${inspect(key)}`)
console.log(`\tKey: ${inspect(key)}`);
return false;
}
@ -230,7 +226,7 @@ export function isValidDependentAppsObject(apps: any): boolean {
return false;
}
return true;
}
},
});
});
}
@ -282,7 +278,7 @@ function isValidService(service: any, serviceId: string): boolean {
return false;
}
return true;
}
},
});
}
@ -340,7 +336,7 @@ export function isValidAppsObject(obj: any): boolean {
}
return true;
});
}
},
});
});
}
@ -405,10 +401,10 @@ export function isValidDependentDevicesObject(devices: any): boolean {
return false;
}
return true;
}
},
});
});
}
},
});
});
}

View File

@ -0,0 +1,38 @@
m = require 'mochainon'
{ expect } = m.chai
conversion = require '../src/lib/conversions'
describe 'conversions', ->
describe 'envArrayToObject', ->
it 'should convert an env array to an object', ->
expect(conversion.envArrayToObject([
'key=value'
'test1=test2'
'k=v'
'equalsvalue=thisvaluehasan=char'
'asd='
'number=123'
])).to.deep.equal({
key: 'value'
test1: 'test2'
k: 'v'
equalsvalue: 'thisvaluehasan=char'
asd: ''
number: '123'
})
it 'should ignore invalid env array entries', ->
expect(conversion.envArrayToObject([
'key1',
'key1=value1'
])).to.deep.equal({
key1: 'value1'
})
it 'should return an empty object with an empty input', ->
expect(conversion.envArrayToObject(null)).to.deep.equal({})
expect(conversion.envArrayToObject('')).to.deep.equal({})
expect(conversion.envArrayToObject([])).to.deep.equal({})
expect(conversion.envArrayToObject(1)).to.deep.equal({})

18
typings/blinking.d.ts vendored Normal file
View File

@ -0,0 +1,18 @@
declare module 'blinking' {
interface Pattern {
blinks?: number;
onDuration?: number;
offDuration?: number;
pause?: number;
}
interface Blink {
start: (pattern: Pattern) => void
stop: () => void;
}
function blinking(ledFile: string): Blink;
export = blinking;
}