mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2024-12-20 14:13:08 +00:00
Merge pull request #1136 from balena-io/extract_backup
Extract backup logic from device state
This commit is contained in:
commit
cf94feb88b
357
package-lock.json
generated
357
package-lock.json
generated
@ -5,18 +5,18 @@
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
|
||||
"integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
|
||||
"version": "7.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
|
||||
"integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/highlight": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"@babel/highlight": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
|
||||
"integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
|
||||
"version": "7.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz",
|
||||
"integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.0.0",
|
||||
@ -24,40 +24,11 @@
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -469,9 +440,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/prettier": {
|
||||
"version": "1.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.16.4.tgz",
|
||||
"integrity": "sha512-MG7ExKBo7AQ5UrL1awyYLNinNM/kyXgE4iP4Ul9fB+T7n768Z5Xem8IZeP6Bna0xze8gkDly49Rgge2HOEw4xA==",
|
||||
"version": "1.18.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.18.3.tgz",
|
||||
"integrity": "sha512-48rnerQdcZ26odp+HOvDGX8IcUkYOCuMc2BodWYTe956MqkHlOGAG4oFQ83cjZ0a4GAgj7mb4GUClxYd2Hlodg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/range-parser": {
|
||||
@ -492,6 +463,16 @@
|
||||
"@types/tough-cookie": "*"
|
||||
}
|
||||
},
|
||||
"@types/rimraf": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-2.0.3.tgz",
|
||||
"integrity": "sha512-dZfyfL/u9l/oi984hEXdmAjX3JHry7TLWw43u1HQ8HhPv6KtfxnrZ3T/bleJ0GEvnk9t5sM7eePkgMqz3yBcGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/glob": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/rwlock": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/rwlock/-/rwlock-5.0.2.tgz",
|
||||
@ -1088,6 +1069,15 @@
|
||||
"ansi-wrap": "0.1.0"
|
||||
}
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"ansi-underline": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-underline/-/ansi-underline-0.1.1.tgz",
|
||||
@ -1604,6 +1594,15 @@
|
||||
"resolved": "https://registry.npmjs.org/fetch-readablestream/-/fetch-readablestream-0.2.0.tgz",
|
||||
"integrity": "sha512-qu4mXWf4wus4idBIN/kVH+XSer8IZ9CwHP+Pd7DL7TuKNC1hP7ykon4kkBjwJF3EMX2WsFp4hH7gU7CyL7ucXw==",
|
||||
"dev": true
|
||||
},
|
||||
"node-web-streams": {
|
||||
"version": "github:resin-io-modules/node-web-streams#46f98300b69090bde3f6b4983877ccfe283a892c",
|
||||
"from": "github:resin-io-modules/node-web-streams#emit-errors",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-stream": "^1.1.0",
|
||||
"web-streams-polyfill": "^1.3.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1775,6 +1774,17 @@
|
||||
"tar-stream": "^1.5.2"
|
||||
}
|
||||
},
|
||||
"resin-discoverable-services": {
|
||||
"version": "git+https://github.com/resin-io-modules/resin-discoverable-services.git#afca9e4700ec5ef82aa897f14bd5a46f06518061",
|
||||
"from": "git+https://github.com/resin-io-modules/resin-discoverable-services.git#find-on-all-interfaces",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bluebird": "^3.0.0",
|
||||
"bonjour": "git+https://github.com/resin-io/bonjour.git#e018851dc823b4b3f670f658f71d0c1c7f3e637c",
|
||||
"ip": "^1.1.4",
|
||||
"lodash": "^4.17.4"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
@ -1970,7 +1980,7 @@
|
||||
"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#listen-on-all-interfaces",
|
||||
"multicast-dns": "git+https://github.com/resin-io-modules/multicast-dns.git#a15c63464eb43e8925b187ed5cb9de6892e8aacc",
|
||||
"multicast-dns-service-types": "^1.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -2283,6 +2293,17 @@
|
||||
"chai": "^3.5.0"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"check-error": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
|
||||
@ -2443,17 +2464,6 @@
|
||||
"string-width": "^1.0.1",
|
||||
"strip-ansi": "^3.0.1",
|
||||
"wrap-ansi": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"clone": {
|
||||
@ -3210,9 +3220,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"deprecate": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/deprecate/-/deprecate-1.1.0.tgz",
|
||||
"integrity": "sha512-b5dDNQYdy2vW9WXUD8+RQlfoxvqztLLhDE+T7Gd37I5E8My7nJkKu6FmhdDeRWJ8B+yjZKuwjCta8pgi8kgSqA==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/deprecate/-/deprecate-1.1.1.tgz",
|
||||
"integrity": "sha512-ZGDXefq1xknT292LnorMY5s8UVU08/WKdzDZCUT6t9JzsiMSP4uzUhgpqugffNVcT5WC6wMBiSQ+LFjlv3v7iQ==",
|
||||
"dev": true
|
||||
},
|
||||
"deps-regex": {
|
||||
@ -4520,8 +4530,7 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@ -4542,14 +4551,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"
|
||||
@ -4564,20 +4571,17 @@
|
||||
"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",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@ -4694,8 +4698,7 @@
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@ -4707,7 +4710,6 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -4722,7 +4724,6 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@ -4730,14 +4731,12 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@ -4756,7 +4755,6 @@
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@ -4837,8 +4835,7 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@ -4850,7 +4847,6 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@ -4936,8 +4932,7 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@ -4973,7 +4968,6 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@ -4993,7 +4987,6 @@
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@ -5037,14 +5030,12 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -5079,9 +5070,9 @@
|
||||
}
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.13.2",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
|
||||
"integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==",
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
|
||||
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
@ -5197,9 +5188,9 @@
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.6.tgz",
|
||||
"integrity": "sha512-vfmKZp3XPM36DNF0qhW+Cdxk7xm7gTEHY1clv1Xq1arwRQuKZgAhw+NZNWbJBtuaNxzNXwhfdPYRrvIbjfS33A==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
|
||||
"integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
@ -8493,15 +8484,6 @@
|
||||
"integrity": "sha512-W0SgGKaB9qSCfFfNj2uQZ/5BlVumaNHjVCAPdEoXrkEJ3ynSf/806LEz1rbDFbJ4+PL9G8IxRkJJTvZndd5D9g==",
|
||||
"dev": true
|
||||
},
|
||||
"node-web-streams": {
|
||||
"version": "github:resin-io-modules/node-web-streams#46f98300b69090bde3f6b4983877ccfe283a892c",
|
||||
"from": "github:resin-io-modules/node-web-streams#46f98300b69090bde3f6b4983877ccfe283a892c",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-stream": "^1.1.0",
|
||||
"web-streams-polyfill": "^1.3.2"
|
||||
}
|
||||
},
|
||||
"noop-logger": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
|
||||
@ -9183,9 +9165,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"prettier": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.0.tgz",
|
||||
"integrity": "sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw==",
|
||||
"version": "1.19.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
|
||||
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
|
||||
"dev": true
|
||||
},
|
||||
"pretty-format": {
|
||||
@ -9746,21 +9728,10 @@
|
||||
"lodash": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"resin-discoverable-services": {
|
||||
"version": "git+https://github.com/resin-io-modules/resin-discoverable-services.git#afca9e4700ec5ef82aa897f14bd5a46f06518061",
|
||||
"from": "git+https://github.com/resin-io-modules/resin-discoverable-services.git#afca9e4700ec5ef82aa897f14bd5a46f06518061",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bluebird": "^3.0.0",
|
||||
"bonjour": "git+https://github.com/resin-io/bonjour.git#fixed-mdns",
|
||||
"ip": "^1.1.4",
|
||||
"lodash": "^4.17.4"
|
||||
}
|
||||
},
|
||||
"resin-lint": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/resin-lint/-/resin-lint-3.1.0.tgz",
|
||||
"integrity": "sha512-bipsVrhMBtoegrBdJf/6NMQke4g8xmZENSu0fBU1KvxLXNhGPQkmobY7vVmP47BeD0m0Zdv9yrEc43w2S+kRWA==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/resin-lint/-/resin-lint-3.1.1.tgz",
|
||||
"integrity": "sha512-BgIsrj9fvWcELoqfiu0dGflqkysByn7m/XVgbv19YdnnVToEtyQkFzfF9oY+h6nnr45pRYkorE6NAFYaVaYhLQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/bluebird": "^3.5.26",
|
||||
@ -9768,7 +9739,7 @@
|
||||
"@types/glob": "^5.0.35",
|
||||
"@types/node": "^8.10.45",
|
||||
"@types/optimist": "0.0.29",
|
||||
"@types/prettier": "^1.16.1",
|
||||
"@types/prettier": "^1.18.3",
|
||||
"bluebird": "^3.5.4",
|
||||
"coffee-script": "^1.10.0",
|
||||
"coffeelint": "^1.15.0",
|
||||
@ -9777,7 +9748,7 @@
|
||||
"glob": "^7.0.3",
|
||||
"merge": "^1.2.0",
|
||||
"optimist": "^0.6.1",
|
||||
"prettier": "^1.16.4",
|
||||
"prettier": "^1.19.1",
|
||||
"tslint": "^5.15.0",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"tslint-no-unused-expression-chai": "^0.1.4",
|
||||
@ -9785,21 +9756,21 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/bluebird": {
|
||||
"version": "3.5.27",
|
||||
"resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.27.tgz",
|
||||
"integrity": "sha512-6BmYWSBea18+tSjjSC3QIyV93ZKAeNWGM7R6aYt1ryTZXrlHF+QLV0G2yV0viEGVyRkyQsWfMoJ0k/YghBX5sQ==",
|
||||
"version": "3.5.28",
|
||||
"resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.28.tgz",
|
||||
"integrity": "sha512-0Vk/kqkukxPKSzP9c8WJgisgGDx5oZDbsLLWIP5t70yThO/YleE+GEm2S1GlRALTaack3O7U5OS5qEm7q2kciA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "8.10.48",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.48.tgz",
|
||||
"integrity": "sha512-c35YEBTkL4rzXY2ucpSKy+UYHjUBIIkuJbWYbsGIrKLEWU5dgJMmLkkIb3qeC3O3Tpb1ZQCwecscvJTDjDjkRw==",
|
||||
"version": "8.10.59",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.59.tgz",
|
||||
"integrity": "sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ==",
|
||||
"dev": true
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.5.5",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz",
|
||||
"integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==",
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz",
|
||||
"integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==",
|
||||
"dev": true
|
||||
},
|
||||
"coffee-script": {
|
||||
@ -9807,18 +9778,6 @@
|
||||
"resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz",
|
||||
"integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==",
|
||||
"dev": true
|
||||
},
|
||||
"prettier": {
|
||||
"version": "1.17.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.1.tgz",
|
||||
"integrity": "sha512-TzGRNvuUSmPgwivDqkZ9tM/qTGW9hqDKWOE9YHiyQdixlKbv7kvEqsmDPrcHJTKwthU774TQwZXVtaQ/mMsvjg==",
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.4.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.5.tgz",
|
||||
"integrity": "sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -10778,6 +10737,15 @@
|
||||
"is-regexp": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"strip-bom": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
|
||||
@ -11309,18 +11277,18 @@
|
||||
"dev": true
|
||||
},
|
||||
"tslint": {
|
||||
"version": "5.16.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.16.0.tgz",
|
||||
"integrity": "sha512-UxG2yNxJ5pgGwmMzPMYh/CCnCnh0HfPgtlVRDs1ykZklufFBL1ZoTlWFRz2NQjcoEiDoRp+JyT0lhBbbH/obyA==",
|
||||
"version": "5.20.1",
|
||||
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz",
|
||||
"integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"builtin-modules": "^1.1.1",
|
||||
"chalk": "^2.3.0",
|
||||
"commander": "^2.12.1",
|
||||
"diff": "^3.2.0",
|
||||
"diff": "^4.0.1",
|
||||
"glob": "^7.1.1",
|
||||
"js-yaml": "^3.13.0",
|
||||
"js-yaml": "^3.13.1",
|
||||
"minimatch": "^3.0.4",
|
||||
"mkdirp": "^0.5.1",
|
||||
"resolve": "^1.3.2",
|
||||
@ -11329,25 +11297,11 @@
|
||||
"tsutils": "^2.29.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
"diff": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz",
|
||||
"integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==",
|
||||
"dev": true
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.13.1",
|
||||
@ -11360,22 +11314,13 @@
|
||||
}
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz",
|
||||
"integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==",
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
|
||||
"integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-parse": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -11395,9 +11340,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"tsutils": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.10.0.tgz",
|
||||
"integrity": "sha512-q20XSMq7jutbGB8luhKKsQldRKWvyBO2BGqni3p4yq8Ys9bEP/xQw3KepKmMRt9gJ4lvQSScrihJrcKdKoSU7Q==",
|
||||
"version": "3.17.1",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz",
|
||||
"integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^1.8.1"
|
||||
@ -11895,8 +11840,7 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@ -11917,14 +11861,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"
|
||||
@ -11939,20 +11881,17 @@
|
||||
"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",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@ -12069,8 +12008,7 @@
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@ -12082,7 +12020,6 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -12097,7 +12034,6 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@ -12105,14 +12041,12 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@ -12131,7 +12065,6 @@
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@ -12212,8 +12145,7 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@ -12225,7 +12157,6 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@ -12311,8 +12242,7 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@ -12348,7 +12278,6 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@ -12368,7 +12297,6 @@
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@ -12412,14 +12340,12 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -12454,9 +12380,9 @@
|
||||
}
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.13.2",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
|
||||
"integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==",
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
|
||||
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
@ -12913,17 +12839,6 @@
|
||||
"requires": {
|
||||
"string-width": "^1.0.1",
|
||||
"strip-ansi": "^3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
|
@ -55,6 +55,7 @@
|
||||
"@types/mz": "0.0.32",
|
||||
"@types/node": "^10.12.17",
|
||||
"@types/request": "^2.48.1",
|
||||
"@types/rimraf": "^2.0.3",
|
||||
"@types/rwlock": "^5.0.2",
|
||||
"@types/shell-quote": "^1.6.0",
|
||||
"@types/sinon": "^7.0.13",
|
||||
@ -101,10 +102,10 @@
|
||||
"mz": "^2.7.0",
|
||||
"network-checker": "^0.1.1",
|
||||
"pinejs-client-request": "^5.2.0",
|
||||
"prettier": "1.17.0",
|
||||
"prettier": "1.19.1",
|
||||
"pretty-ms": "^4.0.0",
|
||||
"request": "^2.51.0",
|
||||
"resin-lint": "^3.1.0",
|
||||
"resin-lint": "^3.1.1",
|
||||
"resin-register-device": "^3.0.0",
|
||||
"resumable-request": "^2.0.0",
|
||||
"rimraf": "^2.6.2",
|
||||
|
@ -4,17 +4,15 @@ import * as express from 'express';
|
||||
import { isLeft } from 'fp-ts/lib/Either';
|
||||
import * as t from 'io-ts';
|
||||
import * as _ from 'lodash';
|
||||
import * as Path from 'path';
|
||||
import { PinejsClientRequest, StatusError } from 'pinejs-client-request';
|
||||
import * as deviceRegister from 'resin-register-device';
|
||||
import * as url from 'url';
|
||||
|
||||
import Config, { ConfigType } from './config';
|
||||
import Database from './db';
|
||||
import DeviceConfig from './device-config';
|
||||
import { EventTracker } from './event-tracker';
|
||||
import { loadBackupFromMigration } from './lib/migration';
|
||||
|
||||
import * as constants from './lib/constants';
|
||||
import {
|
||||
ContractValidationError,
|
||||
ContractViolationError,
|
||||
@ -22,10 +20,9 @@ import {
|
||||
ExchangeKeyError,
|
||||
InternalInconsistencyError,
|
||||
} from './lib/errors';
|
||||
import { pathExistsOnHost } from './lib/fs-utils';
|
||||
import * as request from './lib/request';
|
||||
import { writeLock } from './lib/update-lock';
|
||||
import { DeviceApplicationState } from './types/state';
|
||||
import { DeviceApplicationState, TargetState } from './types/state';
|
||||
|
||||
import log from './lib/supervisor-console';
|
||||
|
||||
@ -73,14 +70,12 @@ export class APIBinder {
|
||||
public router: express.Router;
|
||||
|
||||
private config: Config;
|
||||
private deviceState: {
|
||||
deviceConfig: DeviceConfig;
|
||||
[key: string]: any;
|
||||
};
|
||||
private deviceState: DeviceState;
|
||||
private eventTracker: EventTracker;
|
||||
private logger: Logger;
|
||||
|
||||
public balenaApi: PinejsClientRequest | null = null;
|
||||
// TODO{type}: Retype me when all types are sorted
|
||||
private cachedBalenaApi: PinejsClientRequest | null = null;
|
||||
private lastReportedState: DeviceApplicationState = {
|
||||
local: {},
|
||||
@ -90,7 +85,7 @@ export class APIBinder {
|
||||
local: {},
|
||||
dependent: {},
|
||||
};
|
||||
private lastTarget: DeviceApplicationState = {};
|
||||
private lastTarget: TargetState;
|
||||
private lastTargetStateFetch = process.hrtime();
|
||||
private reportPending = false;
|
||||
private stateReportErrors = 0;
|
||||
@ -143,9 +138,15 @@ export class APIBinder {
|
||||
}
|
||||
|
||||
public async initClient() {
|
||||
const { unmanaged, apiEndpoint, currentApiKey } = await this.config.getMany(
|
||||
['unmanaged', 'apiEndpoint', 'currentApiKey'],
|
||||
);
|
||||
const {
|
||||
unmanaged,
|
||||
apiEndpoint,
|
||||
currentApiKey,
|
||||
} = await this.config.getMany([
|
||||
'unmanaged',
|
||||
'apiEndpoint',
|
||||
'currentApiKey',
|
||||
]);
|
||||
|
||||
if (unmanaged) {
|
||||
log.debug('Unmanaged mode is set, skipping API client initialization');
|
||||
@ -164,25 +165,6 @@ export class APIBinder {
|
||||
this.cachedBalenaApi = this.balenaApi.clone({}, { cache: {} });
|
||||
}
|
||||
|
||||
public async loadBackupFromMigration(retryDelay: number): Promise<void> {
|
||||
try {
|
||||
const exists = await pathExistsOnHost(
|
||||
Path.join('mnt/data', constants.migrationBackupFile),
|
||||
);
|
||||
if (!exists) {
|
||||
return;
|
||||
}
|
||||
log.info('Migration backup detected');
|
||||
const targetState = await this.getTargetState();
|
||||
await this.deviceState.restoreBackup(targetState);
|
||||
} catch (err) {
|
||||
log.error(`Error restoring migration backup, retrying: ${err}`);
|
||||
|
||||
await Bluebird.delay(retryDelay);
|
||||
return this.loadBackupFromMigration(retryDelay);
|
||||
}
|
||||
}
|
||||
|
||||
public async start() {
|
||||
const conf = await this.config.getMany([
|
||||
'apiEndpoint',
|
||||
@ -222,7 +204,11 @@ export class APIBinder {
|
||||
log.debug('Starting current state report');
|
||||
await this.startCurrentStateReport();
|
||||
|
||||
await this.loadBackupFromMigration(bootstrapRetryDelay);
|
||||
await loadBackupFromMigration(
|
||||
this.deviceState,
|
||||
await this.getTargetState(),
|
||||
bootstrapRetryDelay,
|
||||
);
|
||||
|
||||
this.readyForUpdates = true;
|
||||
log.debug('Starting target state poll');
|
||||
@ -327,11 +313,10 @@ export class APIBinder {
|
||||
|
||||
return (await this.balenaApi
|
||||
.post({ resource: 'device', body: device })
|
||||
// TODO: Remove the `as number` when we fix the config typings
|
||||
.timeout(conf.apiTimeout)) as Device;
|
||||
}
|
||||
|
||||
public async getTargetState(): Promise<DeviceApplicationState> {
|
||||
public async getTargetState(): Promise<TargetState> {
|
||||
const { uuid, apiEndpoint, apiTimeout } = await this.config.getMany([
|
||||
'uuid',
|
||||
'apiEndpoint',
|
||||
@ -355,9 +340,9 @@ export class APIBinder {
|
||||
this.cachedBalenaApi.passthrough,
|
||||
);
|
||||
|
||||
return await this.cachedBalenaApi
|
||||
return (await this.cachedBalenaApi
|
||||
._request(requestParams)
|
||||
.timeout(apiTimeout);
|
||||
.timeout(apiTimeout)) as TargetState;
|
||||
}
|
||||
|
||||
// TODO: Once 100% typescript, change this to a native promise
|
||||
@ -535,9 +520,11 @@ 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,
|
||||
);
|
||||
log.error(
|
||||
`Non-200 response from the API! Status code: ${e.statusCode} - message:`,
|
||||
e,
|
||||
);
|
||||
} else {
|
||||
@ -614,9 +601,10 @@ export class APIBinder {
|
||||
|
||||
private async pollTargetState(isInitialCall: boolean = false): Promise<void> {
|
||||
// TODO: Remove the checkInt here with the config changes
|
||||
const { appUpdatePollInterval, instantUpdates } = await this.config.getMany(
|
||||
['appUpdatePollInterval', 'instantUpdates'],
|
||||
);
|
||||
const {
|
||||
appUpdatePollInterval,
|
||||
instantUpdates,
|
||||
} = await this.config.getMany(['appUpdatePollInterval', 'instantUpdates']);
|
||||
|
||||
// We add jitter to the poll interval so that it's between 0.5 and 1.5 times
|
||||
// the configured interval
|
||||
@ -885,9 +873,7 @@ export class APIBinder {
|
||||
'Attempting to provision a device without an initialized API client',
|
||||
);
|
||||
}
|
||||
this.balenaApi.passthrough.headers.Authorization = `Bearer ${
|
||||
opts.deviceApiKey
|
||||
}`;
|
||||
this.balenaApi.passthrough.headers.Authorization = `Bearer ${opts.deviceApiKey}`;
|
||||
|
||||
const configToUpdate = {
|
||||
registered_at: opts.registered_at,
|
||||
|
@ -111,7 +111,7 @@ export function generateStep<T extends CompositionStepAction>(
|
||||
}
|
||||
|
||||
type Executors<T extends CompositionStepAction> = {
|
||||
[key in T]: (step: CompositionStep<key>) => Promise<unknown>
|
||||
[key in T]: (step: CompositionStep<key>) => Promise<unknown>;
|
||||
};
|
||||
type LockingFn = (
|
||||
// TODO: Once the entire codebase is typescript, change
|
||||
|
@ -580,9 +580,11 @@ export class Images extends (EventEmitter as new () => ImageEventEmitter) {
|
||||
image = this.format(image);
|
||||
// TODO: Get rid of this janky cast once the database is
|
||||
// more strongly typed
|
||||
await this.db.upsertModel('image', image, (image as unknown) as Dictionary<
|
||||
unknown
|
||||
>);
|
||||
await this.db.upsertModel(
|
||||
'image',
|
||||
image,
|
||||
(image as unknown) as Dictionary<unknown>,
|
||||
);
|
||||
}
|
||||
|
||||
private format(image: MaybeImage): Image {
|
||||
|
@ -96,9 +96,9 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
public async get(service: Service) {
|
||||
// Get the container ids for special network handling
|
||||
const containerIds = await this.getContainerIdMap(service.appId!);
|
||||
const services = (await this.getAll(
|
||||
`service-id=${service.serviceId}`,
|
||||
)).filter(currentService =>
|
||||
const services = (
|
||||
await this.getAll(`service-id=${service.serviceId}`)
|
||||
).filter(currentService =>
|
||||
currentService.isEqualConfig(service, containerIds),
|
||||
);
|
||||
|
||||
@ -158,9 +158,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
const svc = await this.get(service);
|
||||
if (svc.containerId == null) {
|
||||
throw new InternalInconsistencyError(
|
||||
`No containerId provided for service ${
|
||||
service.serviceName
|
||||
} in ServiceManager.updateMetadata. Service: ${service}`,
|
||||
`No containerId provided for service ${service.serviceName} in ServiceManager.updateMetadata. Service: ${service}`,
|
||||
);
|
||||
}
|
||||
|
||||
@ -184,9 +182,9 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
public async killAllLegacy(): Promise<void> {
|
||||
// Containers haven't been normalized (this is an updated supervisor)
|
||||
// so we need to stop and remove them
|
||||
const supervisorImageId = (await this.docker
|
||||
.getImage(constants.supervisorImage)
|
||||
.inspect()).Id;
|
||||
const supervisorImageId = (
|
||||
await this.docker.getImage(constants.supervisorImage).inspect()
|
||||
).Id;
|
||||
|
||||
for (const container of await this.docker.listContainers({ all: true })) {
|
||||
if (container.ImageID !== supervisorImageId) {
|
||||
@ -212,9 +210,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
|
||||
if (existingService.containerId == null) {
|
||||
throw new InternalInconsistencyError(
|
||||
`No containerId provided for service ${
|
||||
service.serviceName
|
||||
} in ServiceManager.updateMetadata. Service: ${service}`,
|
||||
`No containerId provided for service ${service.serviceName} in ServiceManager.updateMetadata. Service: ${service}`,
|
||||
);
|
||||
}
|
||||
|
||||
@ -248,9 +244,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
const existing = await this.get(service);
|
||||
if (existing.containerId == null) {
|
||||
throw new InternalInconsistencyError(
|
||||
`No containerId provided for service ${
|
||||
service.serviceName
|
||||
} in ServiceManager.updateMetadata. Service: ${service}`,
|
||||
`No containerId provided for service ${service.serviceName} in ServiceManager.updateMetadata. Service: ${service}`,
|
||||
);
|
||||
}
|
||||
return this.docker.getContainer(existing.containerId);
|
||||
@ -371,9 +365,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
const imageId = service.imageId;
|
||||
if (serviceId == null || imageId == null) {
|
||||
throw new InternalInconsistencyError(
|
||||
`serviceId and imageId not defined for service: ${
|
||||
service.serviceName
|
||||
} in ServiceManager.start`,
|
||||
`serviceId and imageId not defined for service: ${service.serviceName} in ServiceManager.start`,
|
||||
);
|
||||
}
|
||||
|
||||
@ -439,9 +431,7 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
const imageId = service.imageId;
|
||||
if (serviceId == null || imageId == null) {
|
||||
throw new InternalInconsistencyError(
|
||||
`serviceId and imageId not defined for service: ${
|
||||
service.serviceName
|
||||
} in ServiceManager.listenToEvents`,
|
||||
`serviceId and imageId not defined for service: ${service.serviceName} in ServiceManager.listenToEvents`,
|
||||
);
|
||||
}
|
||||
this.logger.attach(this.docker, data.id, {
|
||||
@ -488,17 +478,13 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
const imageId = service.imageId;
|
||||
if (serviceId == null || imageId == null) {
|
||||
throw new InternalInconsistencyError(
|
||||
`serviceId and imageId not defined for service: ${
|
||||
service.serviceName
|
||||
} in ServiceManager.start`,
|
||||
`serviceId and imageId not defined for service: ${service.serviceName} in ServiceManager.start`,
|
||||
);
|
||||
}
|
||||
|
||||
if (service.containerId == null) {
|
||||
throw new InternalInconsistencyError(
|
||||
`containerId not defined for service: ${
|
||||
service.serviceName
|
||||
} in ServiceManager.attachToRunning`,
|
||||
`containerId not defined for service: ${service.serviceName} in ServiceManager.attachToRunning`,
|
||||
);
|
||||
}
|
||||
this.logger.attach(this.docker, service.containerId, {
|
||||
@ -638,17 +624,13 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
const svc = await this.get(service);
|
||||
if (svc.containerId == null) {
|
||||
throw new InternalInconsistencyError(
|
||||
`No containerId provided for service ${
|
||||
service.serviceName
|
||||
} in ServiceManager.prepareForHandover. Service: ${service}`,
|
||||
`No containerId provided for service ${service.serviceName} in ServiceManager.prepareForHandover. Service: ${service}`,
|
||||
);
|
||||
}
|
||||
const container = this.docker.getContainer(svc.containerId);
|
||||
await container.update({ RestartPolicy: {} });
|
||||
return await container.rename({
|
||||
name: `old_${service.serviceName}_${service.imageId}_${service.imageId}_${
|
||||
service.releaseId
|
||||
}`,
|
||||
name: `old_${service.serviceName}_${service.imageId}_${service.imageId}_${service.releaseId}`,
|
||||
});
|
||||
}
|
||||
|
||||
@ -670,17 +652,13 @@ export class ServiceManager extends (EventEmitter as new () => ServiceManagerEve
|
||||
return wait();
|
||||
} else {
|
||||
log.info(
|
||||
`Handover timeout has passed, assuming handover was completed for service ${
|
||||
service.serviceName
|
||||
}`,
|
||||
`Handover timeout has passed, assuming handover was completed for service ${service.serviceName}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
log.info(
|
||||
`Waiting for handover to be completed for service: ${
|
||||
service.serviceName
|
||||
}`,
|
||||
`Waiting for handover to be completed for service: ${service.serviceName}`,
|
||||
);
|
||||
|
||||
return wait().then(() => {
|
||||
|
@ -337,9 +337,7 @@ export class Service {
|
||||
config.cpus = Math.round(Number(config.cpus) * 10 ** 9);
|
||||
if (_.isNaN(config.cpus)) {
|
||||
log.warn(
|
||||
`config.cpus value cannot be parsed. Ignoring.\n Value:${
|
||||
config.cpus
|
||||
}`,
|
||||
`config.cpus value cannot be parsed. Ignoring.\n Value:${config.cpus}`,
|
||||
);
|
||||
config.cpus = undefined;
|
||||
}
|
||||
@ -713,9 +711,7 @@ export class Service {
|
||||
// Add some console output for why a service is not matching
|
||||
// so that if we end up in a restart loop, we know exactly why
|
||||
log.debug(
|
||||
`Replacing container for service ${
|
||||
this.serviceName
|
||||
} because of config changes:`,
|
||||
`Replacing container for service ${this.serviceName} because of config changes:`,
|
||||
);
|
||||
if (!nonArrayEquals) {
|
||||
// Try not to leak any sensitive information
|
||||
|
@ -343,9 +343,9 @@ export function addFeaturesFromLabels(
|
||||
`${constants.dockerSocket}:${constants.dockerSocket}`,
|
||||
);
|
||||
if (service.config.environment['DOCKER_HOST'] == null) {
|
||||
service.config.environment['DOCKER_HOST'] = `unix://${
|
||||
constants.dockerSocket
|
||||
}`;
|
||||
service.config.environment[
|
||||
'DOCKER_HOST'
|
||||
] = `unix://${constants.dockerSocket}`;
|
||||
}
|
||||
// We keep balena.sock for backwards compatibility
|
||||
if (constants.dockerSocket !== '/var/run/balena.sock') {
|
||||
|
@ -30,9 +30,7 @@ function remountAndWriteAtomic(file: string, data: string): Promise<void> {
|
||||
// Here's the dangerous part:
|
||||
return Promise.resolve(
|
||||
childProcess.execAsync(
|
||||
`mount -t vfat -o remount,rw ${
|
||||
constants.bootBlockDevice
|
||||
} ${bootMountPoint}`,
|
||||
`mount -t vfat -o remount,rw ${constants.bootBlockDevice} ${bootMountPoint}`,
|
||||
),
|
||||
)
|
||||
.then(() => {
|
||||
@ -76,9 +74,7 @@ export abstract class DeviceConfigBackend {
|
||||
}
|
||||
|
||||
export class RPiConfigBackend extends DeviceConfigBackend {
|
||||
private static bootConfigVarPrefix = `${
|
||||
constants.hostConfigVarPrefix
|
||||
}CONFIG_`;
|
||||
private static bootConfigVarPrefix = `${constants.hostConfigVarPrefix}CONFIG_`;
|
||||
private static bootConfigPath = `${bootMountPoint}/config.txt`;
|
||||
|
||||
public static bootConfigVarRegex = new RegExp(
|
||||
@ -206,9 +202,7 @@ export class RPiConfigBackend extends DeviceConfigBackend {
|
||||
}
|
||||
|
||||
export class ExtlinuxConfigBackend extends DeviceConfigBackend {
|
||||
private static bootConfigVarPrefix = `${
|
||||
constants.hostConfigVarPrefix
|
||||
}EXTLINUX_`;
|
||||
private static bootConfigVarPrefix = `${constants.hostConfigVarPrefix}EXTLINUX_`;
|
||||
private static bootConfigPath = `${bootMountPoint}/extlinux/extlinux.conf`;
|
||||
|
||||
public static bootConfigVarRegex = new RegExp(
|
||||
|
@ -27,10 +27,10 @@ interface ConfigOpts {
|
||||
}
|
||||
|
||||
export type ConfigMap<T extends SchemaTypeKey> = {
|
||||
[key in T]: SchemaReturn<key>
|
||||
[key in T]: SchemaReturn<key>;
|
||||
};
|
||||
export type ConfigChangeMap<T extends SchemaTypeKey> = {
|
||||
[key in T]?: SchemaReturn<key>
|
||||
[key in T]?: SchemaReturn<key>;
|
||||
};
|
||||
|
||||
// Export this type renamed, for storing config keys
|
||||
@ -173,9 +173,11 @@ export class Config extends (EventEmitter as new () => ConfigEventEmitter) {
|
||||
})
|
||||
.then(() => {
|
||||
if (!_.isEmpty(configJsonVals)) {
|
||||
return this.configJsonBackend.set(configJsonVals as {
|
||||
[key in Schema.SchemaKey]: unknown
|
||||
});
|
||||
return this.configJsonBackend.set(
|
||||
configJsonVals as {
|
||||
[key in Schema.SchemaKey]: unknown;
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -208,9 +208,10 @@ export class DeviceConfig {
|
||||
'Non-dictionary passed to DeviceConfig.setBootConfig',
|
||||
);
|
||||
}
|
||||
await this.setBootConfig(configBackend, step.target as Dictionary<
|
||||
string
|
||||
>);
|
||||
await this.setBootConfig(
|
||||
configBackend,
|
||||
step.target as Dictionary<string>,
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -276,9 +277,9 @@ export class DeviceConfig {
|
||||
}
|
||||
|
||||
public async getCurrent() {
|
||||
const conf = await this.config.getMany(['deviceType'].concat(
|
||||
_.keys(DeviceConfig.configKeys),
|
||||
) as SchemaTypeKey[]);
|
||||
const conf = await this.config.getMany(
|
||||
['deviceType'].concat(_.keys(DeviceConfig.configKeys)) as SchemaTypeKey[],
|
||||
);
|
||||
|
||||
const configBackend = await this.getConfigBackend();
|
||||
|
||||
|
@ -1,27 +1,18 @@
|
||||
Promise = require 'bluebird'
|
||||
_ = require 'lodash'
|
||||
EventEmitter = require 'events'
|
||||
fs = Promise.promisifyAll(require('fs'))
|
||||
express = require 'express'
|
||||
bodyParser = require 'body-parser'
|
||||
prettyMs = require 'pretty-ms'
|
||||
hostConfig = require './host-config'
|
||||
network = require './network'
|
||||
execAsync = Promise.promisify(require('child_process').exec)
|
||||
mkdirp = Promise.promisify(require('mkdirp'))
|
||||
path = require 'path'
|
||||
rimraf = Promise.promisify(require('rimraf'))
|
||||
|
||||
constants = require './lib/constants'
|
||||
validation = require './lib/validation'
|
||||
systemd = require './lib/systemd'
|
||||
updateLock = require './lib/update-lock'
|
||||
{ loadTargetFromFile } = require './device-state/preload'
|
||||
{ singleToMulticontainerApp } = require './lib/migration'
|
||||
{
|
||||
NotFoundError,
|
||||
UpdatesLockedError
|
||||
} = require './lib/errors'
|
||||
{ UpdatesLockedError } = require './lib/errors'
|
||||
|
||||
{ DeviceConfig } = require './device-config'
|
||||
ApplicationManager = require './application-manager'
|
||||
@ -323,44 +314,6 @@ module.exports = class DeviceState extends EventEmitter
|
||||
_.assign(@_currentVolatile, newState)
|
||||
@emitAsync('change')
|
||||
|
||||
restoreBackup: (targetState) =>
|
||||
@setTarget(targetState)
|
||||
.then =>
|
||||
appId = _.keys(targetState.local.apps)[0]
|
||||
if !appId?
|
||||
throw new Error('No appId in target state')
|
||||
volumes = targetState.local.apps[appId].volumes
|
||||
backupPath = path.join(constants.rootMountPoint, 'mnt/data/backup')
|
||||
rimraf(backupPath) # We clear this path in case it exists from an incomplete run of this function
|
||||
.then ->
|
||||
mkdirp(backupPath)
|
||||
.then ->
|
||||
execAsync("tar -xzf backup.tgz -C #{backupPath}", cwd: path.join(constants.rootMountPoint, 'mnt/data'))
|
||||
.then ->
|
||||
fs.readdirAsync(backupPath)
|
||||
.then (dirContents) =>
|
||||
Promise.mapSeries dirContents, (volumeName) =>
|
||||
fs.statAsync(path.join(backupPath, volumeName))
|
||||
.then (s) =>
|
||||
if !s.isDirectory()
|
||||
throw new Error("Invalid backup: #{volumeName} is not a directory")
|
||||
if volumes[volumeName]?
|
||||
log.debug("Creating volume #{volumeName} from backup")
|
||||
# If the volume exists (from a previous incomplete run of this restoreBackup), we delete it first
|
||||
@applications.volumes.get({ appId, name: volumeName })
|
||||
.then =>
|
||||
@applications.volumes.get({ appId, name: volumeName }).then (volume) ->
|
||||
volume.remove()
|
||||
.catch(NotFoundError, _.noop)
|
||||
.then =>
|
||||
@applications.volumes.createFromPath({ appId, name: volumeName }, volumes[volumeName], path.join(backupPath, volumeName))
|
||||
else
|
||||
throw new Error("Invalid backup: #{volumeName} is present in backup but not in target state")
|
||||
.then ->
|
||||
rimraf(backupPath)
|
||||
.then ->
|
||||
rimraf(path.join(constants.rootMountPoint, 'mnt/data', constants.migrationBackupFile))
|
||||
|
||||
reboot: (force, skipLock) =>
|
||||
@applications.stopAll({ force, skipLock })
|
||||
.then =>
|
||||
|
5
src/device-state.d.ts
vendored
5
src/device-state.d.ts
vendored
@ -17,6 +17,9 @@ class DeviceState extends EventEmitter {
|
||||
public config: Config;
|
||||
public eventTracker: EventTracker;
|
||||
|
||||
// FIXME: I should be removed once device-state is refactored
|
||||
public connected: boolean;
|
||||
|
||||
public constructor(args: {
|
||||
config: Config;
|
||||
db: Database;
|
||||
@ -31,6 +34,8 @@ class DeviceState extends EventEmitter {
|
||||
public setTarget(target: any): Promise<any>;
|
||||
public triggerApplyTarget(opts: any): Promise<any>;
|
||||
public reportCurrentState(state: any);
|
||||
public getCurrentForComparison(): Promise<any>;
|
||||
public getStatus(): Promise<any>;
|
||||
|
||||
public async init();
|
||||
}
|
||||
|
@ -84,9 +84,7 @@ export class DockerUtils extends DockerToolbelt {
|
||||
|
||||
if (!_.includes([2, 3], deltaOpts.deltaVersion)) {
|
||||
logFn(
|
||||
`Unsupported delta version: ${
|
||||
deltaOpts.deltaVersion
|
||||
}. Failling back to regular pull`,
|
||||
`Unsupported delta version: ${deltaOpts.deltaVersion}. Failling back to regular pull`,
|
||||
);
|
||||
return await this.fetchImageWithProgress(imgDest, deltaOpts, onProgress);
|
||||
}
|
||||
@ -117,9 +115,7 @@ export class DockerUtils extends DockerToolbelt {
|
||||
},
|
||||
};
|
||||
|
||||
const url = `${deltaOpts.deltaEndpoint}/api/v${
|
||||
deltaOpts.deltaVersion
|
||||
}/delta?src=${deltaOpts.deltaSource}&dest=${imgDest}`;
|
||||
const url = `${deltaOpts.deltaEndpoint}/api/v${deltaOpts.deltaVersion}/delta?src=${deltaOpts.deltaSource}&dest=${imgDest}`;
|
||||
|
||||
const [res, data] = await (await request.getRequestInstance()).getAsync(
|
||||
url,
|
||||
@ -140,9 +136,7 @@ export class DockerUtils extends DockerToolbelt {
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
`Got ${
|
||||
res.statusCode
|
||||
} when requesting an image from delta server.`,
|
||||
`Got ${res.statusCode} when requesting an image from delta server.`,
|
||||
);
|
||||
}
|
||||
const deltaUrl = res.headers['location'];
|
||||
@ -164,9 +158,7 @@ export class DockerUtils extends DockerToolbelt {
|
||||
case 3:
|
||||
if (res.statusCode !== 200) {
|
||||
throw new Error(
|
||||
`Got ${
|
||||
res.statusCode
|
||||
} when requesting v3 delta from delta server.`,
|
||||
`Got ${res.statusCode} when requesting v3 delta from delta server.`,
|
||||
);
|
||||
}
|
||||
let name;
|
||||
@ -340,16 +332,11 @@ export class DockerUtils extends DockerToolbelt {
|
||||
},
|
||||
json: true,
|
||||
};
|
||||
const tokenUrl = `${tokenEndpoint}?service=${
|
||||
dstInfo.registry
|
||||
}&scope=repository:${dstInfo.imageName}:pull&scope=repository:${
|
||||
srcInfo.imageName
|
||||
}:pull`;
|
||||
const tokenUrl = `${tokenEndpoint}?service=${dstInfo.registry}&scope=repository:${dstInfo.imageName}:pull&scope=repository:${srcInfo.imageName}:pull`;
|
||||
|
||||
const tokenResponseBody = (await (await request.getRequestInstance()).getAsync(
|
||||
tokenUrl,
|
||||
tokenOpts,
|
||||
))[1];
|
||||
const tokenResponseBody = (
|
||||
await (await request.getRequestInstance()).getAsync(tokenUrl, tokenOpts)
|
||||
)[1];
|
||||
const token = tokenResponseBody != null ? tokenResponseBody.token : null;
|
||||
|
||||
if (token == null) {
|
||||
|
@ -104,3 +104,4 @@ export class ContractViolationError extends TypedError {
|
||||
|
||||
export class AppsJsonParseError extends TypedError {}
|
||||
export class DatabaseParseError extends TypedError {}
|
||||
export class BackupError extends TypedError {}
|
||||
|
@ -1,15 +1,27 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as _ from 'lodash';
|
||||
import * as mkdirp from 'mkdirp';
|
||||
import { child_process, fs } from 'mz';
|
||||
import * as path from 'path';
|
||||
import { PinejsClientRequest } from 'pinejs-client-request';
|
||||
import * as rimraf from 'rimraf';
|
||||
|
||||
const mkdirpAsync = Bluebird.promisify(mkdirp);
|
||||
const rimrafAsync = Bluebird.promisify(rimraf);
|
||||
|
||||
import ApplicationManager from '../application-manager';
|
||||
import Config from '../config';
|
||||
import Database, { Transaction } from '../db';
|
||||
import { DatabaseParseError, NotFoundError } from '../lib/errors';
|
||||
import DeviceState = require('../device-state');
|
||||
import * as constants from '../lib/constants';
|
||||
import { BackupError, DatabaseParseError, NotFoundError } from '../lib/errors';
|
||||
import { pathExistsOnHost } from '../lib/fs-utils';
|
||||
import { log } from '../lib/supervisor-console';
|
||||
import {
|
||||
ApplicationDatabaseFormat,
|
||||
AppsJsonFormat,
|
||||
TargetApplication,
|
||||
TargetState,
|
||||
} from '../types/state';
|
||||
|
||||
export const defaultLegacyVolume = () => 'resin-data';
|
||||
@ -152,9 +164,7 @@ export async function normaliseLegacyDatabase(
|
||||
|
||||
if (releases.length === 0) {
|
||||
log.warn(
|
||||
`No compatible releases found in API, removing ${
|
||||
app.appId
|
||||
} from target state`,
|
||||
`No compatible releases found in API, removing ${app.appId} from target state`,
|
||||
);
|
||||
await db
|
||||
.models('app')
|
||||
@ -171,20 +181,18 @@ export async function normaliseLegacyDatabase(
|
||||
: `${image.is_stored_at__image_location}@${image.content_hash}`;
|
||||
|
||||
log.debug(
|
||||
`Found a release with releaseId ${release.id}, imageId ${
|
||||
image.id
|
||||
}, serviceId ${serviceId}\nImage location is ${imageUrl}`,
|
||||
`Found a release with releaseId ${release.id}, imageId ${image.id}, serviceId ${serviceId}\nImage location is ${imageUrl}`,
|
||||
);
|
||||
|
||||
const imageFromDocker = await application.docker
|
||||
.getImage(service.image)
|
||||
.inspect()
|
||||
.catch(e => {
|
||||
if (e instanceof NotFoundError) {
|
||||
.catch(error => {
|
||||
if (error instanceof NotFoundError) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw e;
|
||||
throw error;
|
||||
});
|
||||
const imagesFromDatabase = await db
|
||||
.models('image')
|
||||
@ -256,3 +264,88 @@ export async function normaliseLegacyDatabase(
|
||||
legacyAppsPresent: false,
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadBackupFromMigration(
|
||||
deviceState: DeviceState,
|
||||
targetState: TargetState,
|
||||
retryDelay: number,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const exists = await pathExistsOnHost(
|
||||
path.join('mnt/data', constants.migrationBackupFile),
|
||||
);
|
||||
if (!exists) {
|
||||
return;
|
||||
}
|
||||
log.info('Migration backup detected');
|
||||
|
||||
await deviceState.setTarget(targetState);
|
||||
|
||||
// multi-app warning!
|
||||
const appId = parseInt(_.keys(targetState.local?.apps)[0], 10);
|
||||
|
||||
if (isNaN(appId)) {
|
||||
throw new BackupError('No appId in target state');
|
||||
}
|
||||
|
||||
const volumes = targetState.local?.apps?.[appId].volumes;
|
||||
|
||||
const backupPath = path.join(constants.rootMountPoint, 'mnt/data/backup');
|
||||
// We clear this path in case it exists from an incomplete run of this function
|
||||
await rimrafAsync(backupPath);
|
||||
await mkdirpAsync(backupPath);
|
||||
await child_process.exec(`tar -xzf backup.tgz -C ${backupPath}`, {
|
||||
cwd: path.join(constants.rootMountPoint, 'mnt/data'),
|
||||
});
|
||||
|
||||
for (const volumeName of await fs.readdir(backupPath)) {
|
||||
const statInfo = await fs.stat(path.join(backupPath, volumeName));
|
||||
|
||||
if (!statInfo.isDirectory()) {
|
||||
throw new BackupError(
|
||||
`Invalid backup: ${volumeName} is not a directory`,
|
||||
);
|
||||
}
|
||||
|
||||
if (volumes[volumeName] != null) {
|
||||
log.debug(`Creating volume ${volumeName} from backup`);
|
||||
// If the volume exists (from a previous incomplete run of this restoreBackup), we delete it first
|
||||
await deviceState.applications.volumes
|
||||
.get({ appId, name: volumeName })
|
||||
.then(volume => {
|
||||
return volume.remove();
|
||||
})
|
||||
.catch(error => {
|
||||
if (error instanceof NotFoundError) {
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
|
||||
await deviceState.applications.volumes.createFromPath(
|
||||
{ appId, name: volumeName },
|
||||
volumes[volumeName],
|
||||
path.join(backupPath, volumeName),
|
||||
);
|
||||
} else {
|
||||
throw new BackupError(
|
||||
`Invalid backup: ${volumeName} is present in backup but not in target state`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await rimrafAsync(backupPath);
|
||||
await rimrafAsync(
|
||||
path.join(
|
||||
constants.rootMountPoint,
|
||||
'mnt/data',
|
||||
constants.migrationBackupFile,
|
||||
),
|
||||
);
|
||||
} catch (err) {
|
||||
log.error(`Error restoring migration backup, retrying: ${err}`);
|
||||
|
||||
await Bluebird.delay(retryDelay);
|
||||
return loadBackupFromMigration(deviceState, targetState, retryDelay);
|
||||
}
|
||||
}
|
||||
|
@ -60,9 +60,7 @@ export class BalenaLogBackend extends LogBackend {
|
||||
this.flush();
|
||||
if (this.dropCount > 0) {
|
||||
this.write({
|
||||
message: `Warning: Suppressed ${
|
||||
this.dropCount
|
||||
} message(s) due to high load`,
|
||||
message: `Warning: Suppressed ${this.dropCount} message(s) due to high load`,
|
||||
timestamp: Date.now(),
|
||||
isSystem: true,
|
||||
isStdErr: true,
|
||||
|
@ -38,34 +38,35 @@ export class ContainerLogs extends (EventEmitter as new () => LogsEventEmitter)
|
||||
const stdoutStream = await container.logs(stdoutLogOpts);
|
||||
const stderrStream = await container.logs(stderrLogOpts);
|
||||
|
||||
[[stdoutStream, true], [stderrStream, false]].forEach(
|
||||
([stream, isStdout]: [Stream.Readable, boolean]) => {
|
||||
stream
|
||||
.on('error', err => {
|
||||
this.emit(
|
||||
'error',
|
||||
new Error(`Error on container logs: ${err} ${err.stack}`),
|
||||
);
|
||||
})
|
||||
.pipe(es.split())
|
||||
.on('data', (logBuf: Buffer | string) => {
|
||||
if (_.isString(logBuf)) {
|
||||
logBuf = Buffer.from(logBuf);
|
||||
}
|
||||
const logMsg = ContainerLogs.extractMessage(logBuf);
|
||||
if (logMsg != null) {
|
||||
this.emit('log', { isStdout, ...logMsg });
|
||||
}
|
||||
})
|
||||
.on('error', err => {
|
||||
this.emit(
|
||||
'error',
|
||||
new Error(`Error on container logs: ${err} ${err.stack}`),
|
||||
);
|
||||
})
|
||||
.on('end', () => this.emit('closed'));
|
||||
},
|
||||
);
|
||||
[
|
||||
[stdoutStream, true],
|
||||
[stderrStream, false],
|
||||
].forEach(([stream, isStdout]: [Stream.Readable, boolean]) => {
|
||||
stream
|
||||
.on('error', err => {
|
||||
this.emit(
|
||||
'error',
|
||||
new Error(`Error on container logs: ${err} ${err.stack}`),
|
||||
);
|
||||
})
|
||||
.pipe(es.split())
|
||||
.on('data', (logBuf: Buffer | string) => {
|
||||
if (_.isString(logBuf)) {
|
||||
logBuf = Buffer.from(logBuf);
|
||||
}
|
||||
const logMsg = ContainerLogs.extractMessage(logBuf);
|
||||
if (logMsg != null) {
|
||||
this.emit('log', { isStdout, ...logMsg });
|
||||
}
|
||||
})
|
||||
.on('error', err => {
|
||||
this.emit(
|
||||
'error',
|
||||
new Error(`Error on container logs: ${err} ${err.stack}`),
|
||||
);
|
||||
})
|
||||
.on('end', () => this.emit('closed'));
|
||||
});
|
||||
}
|
||||
|
||||
private static extractMessage(
|
||||
|
@ -178,9 +178,9 @@ describe('LocalModeManager', () => {
|
||||
const stubEngineObjectMethods = (
|
||||
removeThrows: boolean,
|
||||
): Array<sinon.SinonStubbedInstance<EngineStubbedObject>> => {
|
||||
const resArray: Array<
|
||||
sinon.SinonStubbedInstance<EngineStubbedObject>
|
||||
> = [];
|
||||
const resArray: Array<sinon.SinonStubbedInstance<
|
||||
EngineStubbedObject
|
||||
>> = [];
|
||||
|
||||
const stub = <T>(
|
||||
c: sinon.StubbableType<EngineStubbedObject>,
|
||||
|
Loading…
Reference in New Issue
Block a user