registry config: create table and migrate existing setting
This commit is contained in:
@@ -1,10 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
exports = module.exports = {
|
||||
removePrivateFields,
|
||||
getRegistryConfig,
|
||||
setRegistryConfig,
|
||||
|
||||
ping,
|
||||
|
||||
info,
|
||||
@@ -41,42 +37,19 @@ const apps = require('./apps.js'),
|
||||
dashboard = require('./dashboard.js'),
|
||||
debug = require('debug')('box:docker'),
|
||||
Docker = require('dockerode'),
|
||||
dockerRegistries = require('./dockerregistries.js'),
|
||||
fs = require('fs'),
|
||||
mailServer = require('./mailserver.js'),
|
||||
os = require('os'),
|
||||
paths = require('./paths.js'),
|
||||
promiseRetry = require('./promise-retry.js'),
|
||||
services = require('./services.js'),
|
||||
settings = require('./settings.js'),
|
||||
shell = require('./shell.js')('docker'),
|
||||
safe = require('safetydance'),
|
||||
timers = require('timers/promises'),
|
||||
tld = require('tldjs'),
|
||||
volumes = require('./volumes.js');
|
||||
|
||||
const DOCKER_SOCKET_PATH = '/var/run/docker.sock';
|
||||
const gConnection = new Docker({ socketPath: DOCKER_SOCKET_PATH });
|
||||
|
||||
async function testRegistryConfig(config) {
|
||||
assert.strictEqual(typeof config, 'object');
|
||||
|
||||
if (config.provider === 'noop') return;
|
||||
|
||||
const [error] = await safe(gConnection.checkAuth(config)); // this returns a 500 even for auth errors
|
||||
if (error) throw new BoxError(BoxError.BAD_FIELD, `Invalid serverAddress: ${error.message}`);
|
||||
}
|
||||
|
||||
function injectPrivateFields(newConfig, currentConfig) {
|
||||
if (newConfig.password === constants.SECRET_PLACEHOLDER) newConfig.password = currentConfig.password;
|
||||
}
|
||||
|
||||
function removePrivateFields(registryConfig) {
|
||||
assert.strictEqual(typeof registryConfig, 'object');
|
||||
|
||||
if (registryConfig.password) registryConfig.password = constants.SECRET_PLACEHOLDER;
|
||||
|
||||
return registryConfig;
|
||||
}
|
||||
const gConnection = new Docker({ socketPath: paths.DOCKER_SOCKET_PATH });
|
||||
|
||||
function parseImageRef(imageRef) {
|
||||
assert.strictEqual(typeof imageRef, 'string');
|
||||
@@ -101,7 +74,7 @@ function parseImageRef(imageRef) {
|
||||
|
||||
async function ping() {
|
||||
// do not let the request linger
|
||||
const connection = new Docker({ socketPath: DOCKER_SOCKET_PATH, timeout: 1000 });
|
||||
const connection = new Docker({ socketPath: paths.DOCKER_SOCKET_PATH, timeout: 1000 });
|
||||
|
||||
const [error, result] = await safe(connection.ping());
|
||||
if (error) throw new BoxError(BoxError.DOCKER_ERROR, error);
|
||||
@@ -119,24 +92,27 @@ async function getAuthConfig(imageRef) {
|
||||
// images in our cloudron namespace are always unauthenticated to not interfere with any user limits
|
||||
if (parsedRef.registry === null && parsedRef.fullRepositoryName.startsWith('cloudron/')) return null;
|
||||
|
||||
const registryConfig = await getRegistryConfig();
|
||||
if (registryConfig.provider === 'noop') return null;
|
||||
const registries = await dockerRegistries.list();
|
||||
|
||||
if (registryConfig.serverAddress !== parsedRef.registry) { // ideally they match but there's too many docker registry domains!
|
||||
if (!registryConfig.serverAddress.includes('.docker.')) return null;
|
||||
if (parsedRef.registry !== null && !parsedRef.includes('.docker.')) return null;
|
||||
for (const registry of registries) {
|
||||
if (registry.serverAddress !== parsedRef.registry) { // ideally they match but there's too many docker registry domains!
|
||||
if (!registry.serverAddress.includes('.docker.')) continue;
|
||||
if (parsedRef.registry !== null && !parsedRef.includes('.docker.')) continue;
|
||||
}
|
||||
|
||||
// https://github.com/apocas/dockerode#pull-from-private-repos
|
||||
const authConfig = {
|
||||
username: registry.username,
|
||||
password: registry.password,
|
||||
auth: registry.auth || '', // the auth token at login time
|
||||
email: registry.email || '',
|
||||
serveraddress: registry.serverAddress
|
||||
};
|
||||
|
||||
return authConfig;
|
||||
}
|
||||
|
||||
// https://github.com/apocas/dockerode#pull-from-private-repos
|
||||
const autoConfig = {
|
||||
username: registryConfig.username,
|
||||
password: registryConfig.password,
|
||||
auth: registryConfig.auth || '', // the auth token at login time
|
||||
email: registryConfig.email || '',
|
||||
serveraddress: registryConfig.serverAddress
|
||||
};
|
||||
|
||||
return autoConfig;
|
||||
return null;
|
||||
}
|
||||
|
||||
async function pullImage(imageRef) {
|
||||
@@ -722,36 +698,3 @@ async function update(name, memory) {
|
||||
|
||||
throw new BoxError(BoxError.DOCKER_ERROR, 'Unable to update container');
|
||||
}
|
||||
|
||||
async function getRegistryConfig() {
|
||||
const value = await settings.getJson(settings.REGISTRY_CONFIG_KEY);
|
||||
return value || { provider: 'noop' };
|
||||
}
|
||||
|
||||
function validateServerAddress(serverAddress) {
|
||||
assert.strictEqual(typeof serverAddress, 'string');
|
||||
|
||||
// workaround https://github.com/oncletom/tld.js/issues/73
|
||||
const tmp = serverAddress.replace('_', '-');
|
||||
if (!tld.isValid(tmp)) return new BoxError(BoxError.BAD_FIELD, 'Hostname is not a valid domain name');
|
||||
if (tmp.length > 253) return new BoxError(BoxError.BAD_FIELD, 'Hostname length exceeds 253 characters');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function setRegistryConfig(registryConfig) {
|
||||
assert.strictEqual(typeof registryConfig, 'object');
|
||||
|
||||
if (registryConfig.provider !== 'noop') {
|
||||
const error = validateServerAddress(registryConfig.serverAddress);
|
||||
if (error) throw error;
|
||||
}
|
||||
|
||||
const currentConfig = await getRegistryConfig();
|
||||
|
||||
injectPrivateFields(registryConfig, currentConfig);
|
||||
|
||||
await testRegistryConfig(registryConfig);
|
||||
|
||||
await settings.setJson(settings.REGISTRY_CONFIG_KEY, registryConfig);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user