Dockerregistry converted

This commit is contained in:
Kasra Bigdeli
2018-11-19 20:22:09 -08:00
parent b91b9486e6
commit e99a2a8127
4 changed files with 374 additions and 308 deletions
+86 -62
View File
@@ -1,10 +1,9 @@
const CaptainConstants = require('../utils/CaptainConstants');
const Logger = require('../utils/Logger');
const EnvVars = require('../utils/EnvVars');
const fs = require('fs-extra');
const uuid = require('uuid/v4');
const ApiStatusCodes = require('../api/ApiStatusCodes');
const bcrypt = require('bcryptjs');
"use strict";
const CaptainConstants = require("../utils/CaptainConstants");
const Logger = require("../utils/Logger");
const fs = require("fs-extra");
const ApiStatusCodes = require("../api/ApiStatusCodes");
const bcrypt = require("bcryptjs");
class DockerRegistry {
constructor(dockerApi, dataStore, certbotManager, loadBalancerManager, captainManager) {
this.dockerApi = dockerApi;
@@ -12,11 +11,15 @@ class DockerRegistry {
this.certbotManager = certbotManager;
this.loadBalancerManager = loadBalancerManager;
this.captainManager = captainManager;
// this.dockerApi = dockerApi;
// this.dataStore = dataStore;
// this.certbotManager = certbotManager;
// this.loadBalancerManager = loadBalancerManager;
// this.captainManager = captainManager;
}
enableLocalDockerRegistry() {
const self = this;
return Promise.resolve()
.then(function () {
return Promise.resolve().then(function () {
return self.dataStore.setHasLocalRegistry(true);
});
}
@@ -28,9 +31,11 @@ class DockerRegistry {
})
.then(function (rootHasSsl) {
if (!rootHasSsl) {
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_OPERATION, 'Root must have SSL before enabling ssl for docker registry.');
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_OPERATION, "Root must have SSL before enabling ssl for docker registry.");
}
return self.certbotManager.enableSsl(CaptainConstants.registrySubDomain + '.' + self.dataStore.getRootDomain());
return self.certbotManager.enableSsl(CaptainConstants.registrySubDomain +
"." +
self.dataStore.getRootDomain());
})
.then(function () {
return self.dataStore.setHasRegistrySsl(true);
@@ -44,9 +49,11 @@ class DockerRegistry {
}
getLocalRegistryDomainAndPort() {
const self = this;
return CaptainConstants.registrySubDomain + '.' +
return (CaptainConstants.registrySubDomain +
"." +
self.dataStore.getRootDomain() +
':' + CaptainConstants.registrySubDomainPort;
":" +
CaptainConstants.registrySubDomainPort);
}
ensureDockerRegistryRunningOnThisNode() {
const dockerApi = this.dockerApi;
@@ -54,72 +61,86 @@ class DockerRegistry {
const myNodeId = this.captainManager.getMyNodeId();
const captainSalt = this.captainManager.getCaptainSalt();
function createRegistryServiceOnNode() {
return dockerApi.createServiceOnNodeId(CaptainConstants.registryImageName, CaptainConstants.registryServiceName, [{
protocol: 'tcp',
return dockerApi.createServiceOnNodeId(CaptainConstants.registryImageName, CaptainConstants.registryServiceName, [
{
protocol: "tcp",
containerPort: 5000,
hostPort: CaptainConstants.registrySubDomainPort
}], myNodeId, [{
containerPath: '/cert-files',
hostPath: CaptainConstants.letsEncryptEtcPath
hostPort: CaptainConstants.registrySubDomainPort,
},
], myNodeId, [
{
containerPath: "/cert-files",
hostPath: CaptainConstants.letsEncryptEtcPath,
},
{
containerPath: '/var/lib/registry',
hostPath: CaptainConstants.registryPathOnHost
containerPath: "/var/lib/registry",
hostPath: CaptainConstants.registryPathOnHost,
},
{
containerPath: '/etc/auth',
hostPath: CaptainConstants.registryAuthPathOnHost
}
], [{
key: 'REGISTRY_HTTP_TLS_CERTIFICATE',
value: '/cert-files/live/' + CaptainConstants.registrySubDomain + '.' + dataStore.getRootDomain() + '/fullchain.pem'
}, {
key: 'REGISTRY_HTTP_TLS_KEY',
value: '/cert-files/live/' + CaptainConstants.registrySubDomain + '.' + dataStore.getRootDomain() + '/privkey.pem'
}, {
key: 'REGISTRY_AUTH',
value: 'htpasswd'
}, {
key: 'REGISTRY_AUTH_HTPASSWD_REALM',
value: 'Registry Realm'
}, {
key: 'REGISTRY_AUTH_HTPASSWD_PATH',
value: '/etc/auth'
}]);
containerPath: "/etc/auth",
hostPath: CaptainConstants.registryAuthPathOnHost,
},
], [
{
key: "REGISTRY_HTTP_TLS_CERTIFICATE",
value: "/cert-files/live/" +
CaptainConstants.registrySubDomain +
"." +
dataStore.getRootDomain() +
"/fullchain.pem",
},
{
key: "REGISTRY_HTTP_TLS_KEY",
value: "/cert-files/live/" +
CaptainConstants.registrySubDomain +
"." +
dataStore.getRootDomain() +
"/privkey.pem",
},
{
key: "REGISTRY_AUTH",
value: "htpasswd",
},
{
key: "REGISTRY_AUTH_HTPASSWD_REALM",
value: "Registry Realm",
},
{
key: "REGISTRY_AUTH_HTPASSWD_PATH",
value: "/etc/auth",
},
], undefined);
}
return Promise.resolve()
.then(function () {
let authContent = CaptainConstants.captainRegistryUsername + ':' +
const authContent = CaptainConstants.captainRegistryUsername +
":" +
bcrypt.hashSync(captainSalt, bcrypt.genSaltSync(5));
return fs.outputFile(CaptainConstants.registryAuthPathOnHost, authContent);
})
.then(function () {
return dockerApi
.isServiceRunningByName(CaptainConstants.registryServiceName);
return dockerApi.isServiceRunningByName(CaptainConstants.registryServiceName);
})
.then(function (isRunning) {
if (isRunning) {
Logger.d('Captain Registry is already running.. ');
return dockerApi
.getNodeIdByServiceName(CaptainConstants.registryServiceName);
Logger.d("Captain Registry is already running.. ");
return dockerApi.getNodeIdByServiceName(CaptainConstants.registryServiceName);
}
else {
Logger.d('No Captain Registry service is running. Creating one...');
return createRegistryServiceOnNode(myNodeId)
.then(function () {
Logger.d("No Captain Registry service is running. Creating one...");
return createRegistryServiceOnNode().then(function () {
return myNodeId;
});
}
})
.then(function (nodeId) {
if (nodeId !== myNodeId) {
Logger.d('Captain Registry is running on a different node. Removing...');
Logger.d("Captain Registry is running on a different node. Removing...");
return dockerApi
.removeServiceByName(CaptainConstants.registryServiceName)
.then(function () {
Logger.d('Creating Registry on this node...');
return createRegistryServiceOnNode(myNodeId)
.then(function () {
Logger.d("Creating Registry on this node...");
return createRegistryServiceOnNode().then(function () {
return true;
});
});
@@ -132,9 +153,9 @@ class DockerRegistry {
updateRegistryAuthHeader(username, password, domain, currentVersion) {
const self = this;
const dockerApi = this.dockerApi;
let nextVersion = null;
let secretName = null;
let userEmailAddress;
let nextVersion = undefined;
let secretName = undefined;
let userEmailAddress = undefined;
return Promise.resolve()
.then(function () {
return self.dataStore.getUserEmailAddress();
@@ -148,15 +169,17 @@ class DockerRegistry {
})
.then(function (versionSaved) {
nextVersion = versionSaved + 1;
secretName = CaptainConstants.captainRegistryAuthHeaderSecretPrefix + nextVersion;
secretName =
CaptainConstants.captainRegistryAuthHeaderSecretPrefix +
nextVersion;
if (!username || !password || !domain) {
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'user, pass and domain are all required');
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, "user, pass and domain are all required");
}
return dockerApi.checkIfSecretExist(secretName);
})
.then(function (secretExist) {
if (secretExist) {
Logger.d('WARNING! Unexpected secret exist! Perhaps secret was created but Captain was not updated.');
Logger.d("WARNING! Unexpected secret exist! Perhaps secret was created but Captain was not updated.");
return self.updateRegistryAuthHeader(username, password, domain, nextVersion);
}
else {
@@ -164,11 +187,12 @@ class DockerRegistry {
.ensureSecret(secretName, JSON.stringify({
username: username,
password: password,
email: userEmailAddress || CaptainConstants.defaultEmail,
serveraddress: domain
email: userEmailAddress ||
CaptainConstants.defaultEmail,
serveraddress: domain,
}))
.then(function () {
Logger.d('Updating EnvVars to update docker registry auth.');
Logger.d("Updating EnvVars to update docker registry auth.");
return self.dataStore.setRegistryAuthSecretVersion(nextVersion);
});
}
File diff suppressed because one or more lines are too long
-245
View File
@@ -1,245 +0,0 @@
const CaptainConstants = require('../utils/CaptainConstants');
const Logger = require('../utils/Logger');
const EnvVars = require('../utils/EnvVars');
const fs = require('fs-extra');
const uuid = require('uuid/v4');
const ApiStatusCodes = require('../api/ApiStatusCodes');
const bcrypt = require('bcryptjs');
class DockerRegistry {
constructor(dockerApi, dataStore, certbotManager, loadBalancerManager, captainManager) {
this.dockerApi = dockerApi;
this.dataStore = dataStore;
this.certbotManager = certbotManager;
this.loadBalancerManager = loadBalancerManager;
this.captainManager = captainManager;
}
enableLocalDockerRegistry() {
const self = this;
return Promise.resolve()
.then(function () {
return self.dataStore.setHasLocalRegistry(true);
});
}
enableRegistrySsl() {
const self = this;
return Promise.resolve()
.then(function () {
return self.dataStore.getHasRootSsl();
})
.then(function (rootHasSsl) {
if (!rootHasSsl) {
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_OPERATION, 'Root must have SSL before enabling ssl for docker registry.');
}
return self.certbotManager.enableSsl(CaptainConstants.registrySubDomain + '.' + self.dataStore.getRootDomain());
})
.then(function () {
return self.dataStore.setHasRegistrySsl(true);
})
.then(function () {
return self.loadBalancerManager.rePopulateNginxConfigFile(self.dataStore);
})
.then(function () {
return self.loadBalancerManager.sendReloadSignal();
});
}
getLocalRegistryDomainAndPort() {
const self = this;
return CaptainConstants.registrySubDomain + '.' +
self.dataStore.getRootDomain() +
':' + CaptainConstants.registrySubDomainPort;
}
ensureDockerRegistryRunningOnThisNode() {
const dockerApi = this.dockerApi;
const dataStore = this.dataStore;
const myNodeId = this.captainManager.getMyNodeId();
const captainSalt = this.captainManager.getCaptainSalt();
function createRegistryServiceOnNode() {
return dockerApi.createServiceOnNodeId(CaptainConstants.registryImageName, CaptainConstants.registryServiceName, [{
protocol: 'tcp',
containerPort: 5000,
hostPort: CaptainConstants.registrySubDomainPort
}], myNodeId, [{
containerPath: '/cert-files',
hostPath: CaptainConstants.letsEncryptEtcPath
},
{
containerPath: '/var/lib/registry',
hostPath: CaptainConstants.registryPathOnHost
},
{
containerPath: '/etc/auth',
hostPath: CaptainConstants.registryAuthPathOnHost
}
], [{
key: 'REGISTRY_HTTP_TLS_CERTIFICATE',
value: '/cert-files/live/' + CaptainConstants.registrySubDomain + '.' + dataStore.getRootDomain() + '/fullchain.pem'
}, {
key: 'REGISTRY_HTTP_TLS_KEY',
value: '/cert-files/live/' + CaptainConstants.registrySubDomain + '.' + dataStore.getRootDomain() + '/privkey.pem'
}, {
key: 'REGISTRY_AUTH',
value: 'htpasswd'
}, {
key: 'REGISTRY_AUTH_HTPASSWD_REALM',
value: 'Registry Realm'
}, {
key: 'REGISTRY_AUTH_HTPASSWD_PATH',
value: '/etc/auth'
}]);
}
return Promise.resolve()
.then(function () {
let authContent = CaptainConstants.captainRegistryUsername + ':' +
bcrypt.hashSync(captainSalt, bcrypt.genSaltSync(5));
return fs.outputFile(CaptainConstants.registryAuthPathOnHost, authContent);
})
.then(function () {
return dockerApi
.isServiceRunningByName(CaptainConstants.registryServiceName);
})
.then(function (isRunning) {
if (isRunning) {
Logger.d('Captain Registry is already running.. ');
return dockerApi
.getNodeIdByServiceName(CaptainConstants.registryServiceName);
} else {
Logger.d('No Captain Registry service is running. Creating one...');
return createRegistryServiceOnNode(myNodeId)
.then(function () {
return myNodeId;
})
}
})
.then(function (nodeId) {
if (nodeId !== myNodeId) {
Logger.d('Captain Registry is running on a different node. Removing...');
return dockerApi
.removeServiceByName(CaptainConstants.registryServiceName)
.then(function () {
Logger.d('Creating Registry on this node...');
return createRegistryServiceOnNode(myNodeId)
.then(function () {
return true;
});
});
} else {
return true;
}
})
}
updateRegistryAuthHeader(username, password, domain, currentVersion) {
const self = this;
const dockerApi = this.dockerApi;
let nextVersion = null;
let secretName = null;
let userEmailAddress;
return Promise.resolve()
.then(function () {
return self.dataStore.getUserEmailAddress();
})
.then(function (emailAddress) {
userEmailAddress = emailAddress;
if (currentVersion) {
return currentVersion
}
return self.dataStore.getRegistryAuthSecretVersion();
})
.then(function (versionSaved) {
nextVersion = versionSaved + 1;
secretName = CaptainConstants.captainRegistryAuthHeaderSecretPrefix + nextVersion;
if (!username || !password || !domain) {
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'user, pass and domain are all required');
}
return dockerApi.checkIfSecretExist(secretName);
})
.then(function (secretExist) {
if (secretExist) {
Logger.d('WARNING! Unexpected secret exist! Perhaps secret was created but Captain was not updated.');
return self.updateRegistryAuthHeader(username, password, domain, nextVersion);
} else {
return dockerApi
.ensureSecret(secretName, JSON.stringify({
username: username,
password: password,
email: userEmailAddress || CaptainConstants.defaultEmail,
serveraddress: domain
}))
.then(function () {
Logger.d('Updating EnvVars to update docker registry auth.');
return self.dataStore.setRegistryAuthSecretVersion(nextVersion);
});
}
});
}
}
module.exports = DockerRegistry;
+287
View File
@@ -0,0 +1,287 @@
import CaptainConstants = require("../utils/CaptainConstants");
import Logger = require("../utils/Logger");
import EnvVars = require("../utils/EnvVars");
import fs = require("fs-extra");
import uuid = require("uuid/v4");
import ApiStatusCodes = require("../api/ApiStatusCodes");
import bcrypt = require("bcryptjs");
import DockerApi = require("../docker/DockerApi");
import DataStore = require("../datastore/DataStoreImpl");
import CertbotManager = require("../user/CertbotManager");
import LoadBalancerManager = require("../user/LoadBalancerManager");
import CaptainManager = require("../user/CaptainManager");
class DockerRegistry {
constructor(
private dockerApi: DockerApi,
private dataStore: DataStore,
private certbotManager: CertbotManager,
private loadBalancerManager: LoadBalancerManager,
private captainManager: CaptainManager
) {
// this.dockerApi = dockerApi;
// this.dataStore = dataStore;
// this.certbotManager = certbotManager;
// this.loadBalancerManager = loadBalancerManager;
// this.captainManager = captainManager;
}
enableLocalDockerRegistry() {
const self = this;
return Promise.resolve().then(function() {
return self.dataStore.setHasLocalRegistry(true);
});
}
enableRegistrySsl() {
const self = this;
return Promise.resolve()
.then(function() {
return self.dataStore.getHasRootSsl();
})
.then(function(rootHasSsl) {
if (!rootHasSsl) {
throw ApiStatusCodes.createError(
ApiStatusCodes.ILLEGAL_OPERATION,
"Root must have SSL before enabling ssl for docker registry."
);
}
return self.certbotManager.enableSsl(
CaptainConstants.registrySubDomain +
"." +
self.dataStore.getRootDomain()
);
})
.then(function() {
return self.dataStore.setHasRegistrySsl(true);
})
.then(function() {
return self.loadBalancerManager.rePopulateNginxConfigFile(
self.dataStore
);
})
.then(function() {
return self.loadBalancerManager.sendReloadSignal();
});
}
getLocalRegistryDomainAndPort() {
const self = this;
return (
CaptainConstants.registrySubDomain +
"." +
self.dataStore.getRootDomain() +
":" +
CaptainConstants.registrySubDomainPort
);
}
ensureDockerRegistryRunningOnThisNode() {
const dockerApi = this.dockerApi;
const dataStore = this.dataStore;
const myNodeId = this.captainManager.getMyNodeId();
const captainSalt = this.captainManager.getCaptainSalt();
function createRegistryServiceOnNode() {
return dockerApi.createServiceOnNodeId(
CaptainConstants.registryImageName,
CaptainConstants.registryServiceName,
[
{
protocol: "tcp",
containerPort: 5000,
hostPort: CaptainConstants.registrySubDomainPort,
},
],
myNodeId,
[
{
containerPath: "/cert-files",
hostPath: CaptainConstants.letsEncryptEtcPath,
},
{
containerPath: "/var/lib/registry",
hostPath: CaptainConstants.registryPathOnHost,
},
{
containerPath: "/etc/auth",
hostPath: CaptainConstants.registryAuthPathOnHost,
},
],
[
{
key: "REGISTRY_HTTP_TLS_CERTIFICATE",
value:
"/cert-files/live/" +
CaptainConstants.registrySubDomain +
"." +
dataStore.getRootDomain() +
"/fullchain.pem",
},
{
key: "REGISTRY_HTTP_TLS_KEY",
value:
"/cert-files/live/" +
CaptainConstants.registrySubDomain +
"." +
dataStore.getRootDomain() +
"/privkey.pem",
},
{
key: "REGISTRY_AUTH",
value: "htpasswd",
},
{
key: "REGISTRY_AUTH_HTPASSWD_REALM",
value: "Registry Realm",
},
{
key: "REGISTRY_AUTH_HTPASSWD_PATH",
value: "/etc/auth",
},
]
, undefined);
}
return Promise.resolve()
.then(function() {
const authContent =
CaptainConstants.captainRegistryUsername +
":" +
bcrypt.hashSync(captainSalt, bcrypt.genSaltSync(5));
return fs.outputFile(
CaptainConstants.registryAuthPathOnHost,
authContent
);
})
.then(function() {
return dockerApi.isServiceRunningByName(
CaptainConstants.registryServiceName
);
})
.then(function(isRunning) {
if (isRunning) {
Logger.d("Captain Registry is already running.. ");
return dockerApi.getNodeIdByServiceName(
CaptainConstants.registryServiceName
);
} else {
Logger.d(
"No Captain Registry service is running. Creating one..."
);
return createRegistryServiceOnNode().then(
function() {
return myNodeId;
}
);
}
})
.then(function(nodeId) {
if (nodeId !== myNodeId) {
Logger.d(
"Captain Registry is running on a different node. Removing..."
);
return dockerApi
.removeServiceByName(
CaptainConstants.registryServiceName
)
.then(function() {
Logger.d("Creating Registry on this node...");
return createRegistryServiceOnNode().then(
function() {
return true;
}
);
});
} else {
return true;
}
});
}
updateRegistryAuthHeader(username: string, password: string, domain: string, currentVersion: number): Promise<any> {
const self = this;
const dockerApi = this.dockerApi;
let nextVersion: number|undefined = undefined;
let secretName: string|undefined = undefined;
let userEmailAddress: string|undefined = undefined;
return Promise.resolve()
.then(function() {
return self.dataStore.getUserEmailAddress();
})
.then(function(emailAddress) {
userEmailAddress = emailAddress;
if (currentVersion) {
return currentVersion;
}
return self.dataStore.getRegistryAuthSecretVersion();
})
.then(function(versionSaved) {
nextVersion = versionSaved + 1;
secretName =
CaptainConstants.captainRegistryAuthHeaderSecretPrefix +
nextVersion;
if (!username || !password || !domain) {
throw ApiStatusCodes.createError(
ApiStatusCodes.STATUS_ERROR_GENERIC,
"user, pass and domain are all required"
);
}
return dockerApi.checkIfSecretExist(secretName);
})
.then(function(secretExist) {
if (secretExist) {
Logger.d(
"WARNING! Unexpected secret exist! Perhaps secret was created but Captain was not updated."
);
return self.updateRegistryAuthHeader(
username,
password,
domain,
nextVersion!
);
} else {
return dockerApi
.ensureSecret(
secretName,
JSON.stringify({
username: username,
password: password,
email:
userEmailAddress ||
CaptainConstants.defaultEmail,
serveraddress: domain,
})
)
.then(function() {
Logger.d(
"Updating EnvVars to update docker registry auth."
);
return self.dataStore.setRegistryAuthSecretVersion(
nextVersion
);
});
}
});
}
}
export = DockerRegistry;