mirror of
https://github.com/koush/scrypted.git
synced 2026-02-03 14:13:28 +00:00
core: use new builtin docker image updater
This commit is contained in:
@@ -12,7 +12,7 @@ import { AutomationCore, AutomationCoreNativeId } from './automations-core';
|
||||
import { ClusterCore, ClusterCoreNativeId } from './cluster';
|
||||
import { LauncherMixin } from './launcher-mixin';
|
||||
import { MediaCore } from './media-core';
|
||||
import { checkLegacyLxc, checkLxc } from './platform/lxc';
|
||||
import { checkLegacyLxc, checkLxc, checkLxcVersionUpdateNeeded } from './platform/lxc';
|
||||
import { ConsoleServiceNativeId, PluginSocketService, ReplServiceNativeId } from './plugin-socket-service';
|
||||
import { ScriptCore, ScriptCoreNativeId, newScript } from './script-core';
|
||||
import { TerminalService, TerminalServiceNativeId, newTerminalService } from './terminal-service';
|
||||
@@ -215,9 +215,14 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Dev
|
||||
);
|
||||
})();
|
||||
|
||||
// check on workers once an hour.
|
||||
// check on workers immediately and once an hour.
|
||||
this.updateWorkers();
|
||||
setInterval(() => this.updateWorkers(), 1000 * 60 * 60);
|
||||
setInterval(() => this.updateWorkers(), 60 * 1000 * 60);
|
||||
|
||||
// check on worker images once an hour.
|
||||
// checking immediately is problematic as a failed update may cause a restart loop on startup.
|
||||
// images are also pruned 1 minute after startup, so avoid that.
|
||||
setInterval(() => this.updateWorkerImages(), 60 * 1000 * 60);
|
||||
}
|
||||
|
||||
async updateWorkers() {
|
||||
@@ -242,6 +247,38 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Dev
|
||||
}
|
||||
}
|
||||
|
||||
async updateWorkerImages() {
|
||||
const workers = await sdk.clusterManager?.getClusterWorkers();
|
||||
if (!workers)
|
||||
return;
|
||||
for (const [id, worker] of Object.entries(workers)) {
|
||||
const forked = sdk.fork<ReturnType<typeof fork>>({
|
||||
clusterWorkerId: id,
|
||||
runtime: 'node',
|
||||
});
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const result = await forked.result;
|
||||
if (!await result.checkLxcVersionUpdateNeeded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// restart the worker to pick up the new image.
|
||||
const clusterFork = await sdk.systemManager.getComponent('cluster-fork');
|
||||
const serviceControl = await clusterFork.getServiceControl(worker.id);
|
||||
await serviceControl.restart().catch(() => { });
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
finally {
|
||||
await sleep(1000);
|
||||
forked.worker.terminate();
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
async getSettings(): Promise<Setting[]> {
|
||||
try {
|
||||
const service = await sdk.systemManager.getComponent('addresses');
|
||||
@@ -361,6 +398,7 @@ export async function fork() {
|
||||
tsCompile,
|
||||
newScript,
|
||||
newTerminalService,
|
||||
checkLxcVersionUpdateNeeded,
|
||||
checkLxc: async () => {
|
||||
try {
|
||||
// console.warn('Checking for LXC installation...');
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import fs, { writeFileSync } from 'fs';
|
||||
import sdk from '@scrypted/sdk';
|
||||
import yaml from 'yaml';
|
||||
import { Deferred } from '@scrypted/common/src/deferred';
|
||||
import { readFileAsString } from '@scrypted/common/src/eval/scrypted-eval';
|
||||
import sdk from '@scrypted/sdk';
|
||||
import fs, { writeFileSync } from 'fs';
|
||||
import http from 'http';
|
||||
import yaml from 'yaml';
|
||||
|
||||
export const SCRYPTED_INSTALL_ENVIRONMENT_LXC = 'lxc';
|
||||
export const SCRYPTED_INSTALL_ENVIRONMENT_LXC_DOCKER = 'lxc-docker';
|
||||
@@ -24,6 +26,100 @@ export async function checkLxc() {
|
||||
await checkLxcScript();
|
||||
}
|
||||
|
||||
|
||||
async function dockerRequest(options: http.RequestOptions, body?: string) {
|
||||
const deferred = new Deferred<string>();
|
||||
|
||||
const req = http.request({
|
||||
socketPath: '/var/run/docker.sock',
|
||||
method: options.method,
|
||||
path: options.path,
|
||||
headers: {
|
||||
'Host': 'localhost',
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
|
||||
req.on('response', (res) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
res.on('end', () => {
|
||||
deferred.resolve(data);
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (err) => {
|
||||
deferred.reject(err);
|
||||
});
|
||||
|
||||
if (body) {
|
||||
req.write(body);
|
||||
}
|
||||
|
||||
req.end();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
async function dockerPullScryptedTag(tag: string) {
|
||||
return dockerRequest({
|
||||
method: 'POST',
|
||||
path: `/v1.41/images/create?fromImage=ghcr.io%2Fkoush%2Fscrypted&tag=${tag}`,
|
||||
});
|
||||
}
|
||||
|
||||
async function dockerImageLsScryptedTag(tag: string) {
|
||||
// List all images and find the specific one
|
||||
const data = await dockerRequest({
|
||||
method: 'GET',
|
||||
path: '/v1.41/images/json'
|
||||
});
|
||||
const images = JSON.parse(data);
|
||||
// Filter for your specific image
|
||||
const targetImage = images.find(image => {
|
||||
return image.RepoTags && image.RepoTags.some(t =>
|
||||
t === `ghcr.io/koush/scrypted:${tag}`
|
||||
);
|
||||
});
|
||||
if (!targetImage) {
|
||||
throw new Error('Image not found');
|
||||
}
|
||||
|
||||
return targetImage.Id;
|
||||
}
|
||||
|
||||
async function dockerGetScryptedContainerImageId() {
|
||||
// List running containers filtered by name
|
||||
const data = await dockerRequest({
|
||||
method: 'GET',
|
||||
path: '/v1.41/containers/json?filters={"name":["scrypted"],"status":["running"]}'
|
||||
});
|
||||
const containers = JSON.parse(data);
|
||||
if (!containers.length)
|
||||
throw new Error('No running container named "scrypted" found');
|
||||
const container = containers[0];
|
||||
return container.ImageID;
|
||||
}
|
||||
|
||||
export async function checkLxcVersionUpdateNeeded() {
|
||||
if (process.env.SCRYPTED_INSTALL_ENVIRONMENT !== SCRYPTED_INSTALL_ENVIRONMENT_LXC_DOCKER)
|
||||
return;
|
||||
|
||||
const dockerCompose = yaml.parseDocument(readFileAsString('/root/.scrypted/docker-compose.yml'));
|
||||
// @ts-ignore
|
||||
const image: string = dockerCompose.contents.get('services').get('scrypted').get('image');
|
||||
const label = image.split(':')[1] || 'latest';
|
||||
|
||||
await dockerPullScryptedTag(label);
|
||||
const imageId = await dockerImageLsScryptedTag(label);
|
||||
const containerImageId = await dockerGetScryptedContainerImageId();
|
||||
console.warn('LXC Scrypted latest image ID:', imageId);
|
||||
console.warn('LXC Scrypted running image ID:', containerImageId);
|
||||
return containerImageId !== imageId;
|
||||
}
|
||||
|
||||
async function checkLxcCompose() {
|
||||
// the lxc-docker used watchtower for automatic updates but watchtower started crashing in the lxc environment
|
||||
// after a docker update.
|
||||
|
||||
Reference in New Issue
Block a user