diff --git a/.gitignore b/.gitignore index 1657ccf9..45b5f827 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ out gen node_modules/ -packages/*/test/*.private.js +/test/*.private.js diff --git a/packages/certd/.eslintrc b/packages/certd/.eslintrc index a5c17549..c6ce67f2 100644 --- a/packages/certd/.eslintrc +++ b/packages/certd/.eslintrc @@ -2,5 +2,13 @@ "extends": "standard", "env": { "mocha": true - } + }, + "overrides": [ + { + "files": ["*.test.js", "*.spec.js"], + "rules": { + "no-unused-expressions": "off" + } + } + ] } diff --git a/packages/certd/package.json b/packages/certd/package.json index 59b667e9..feb5b56c 100644 --- a/packages/certd/package.json +++ b/packages/certd/package.json @@ -2,8 +2,7 @@ "name": "@certd/certd", "version": "0.0.1", "description": "", - "main": "src/index.js", - "exports": "src/index.js", + "main": "./src/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, diff --git a/packages/certd/src/index.js b/packages/certd/src/index.js index cabc5992..9b77b230 100644 --- a/packages/certd/src/index.js +++ b/packages/certd/src/index.js @@ -27,8 +27,8 @@ export class Certd { options = this.options } const certOptions = options.cert - const providers = options.providers - const providerOptions = providers[certOptions.challenge.dnsProvider] + const accessProviders = options.accessProviders + const providerOptions = accessProviders[certOptions.challenge.dnsProvider] const dnsProvider = await DnsProviderFactory.createByType(providerOptions.providerType, providerOptions) const cert = await this.acme.order({ email: certOptions.email, diff --git a/packages/certd/src/utils/util.log.js b/packages/certd/src/utils/util.log.js index 2309aa59..0abd0555 100644 --- a/packages/certd/src/utils/util.log.js +++ b/packages/certd/src/utils/util.log.js @@ -1,11 +1,7 @@ -import util from './util.js' import log4js from 'log4js' -import path from 'path' -const level = process.env.NODE_ENV === 'development' ? 'debug' : 'info' -const filename = path.join(util.getUserBasePath(), '/logs/certd.log') log4js.configure({ - appenders: { std: { type: 'stdout' }, file: { type: 'file', pattern: 'yyyy-MM-dd', daysToKeep: 3, filename } }, - categories: { default: { appenders: ['file', 'std'], level: level } } + appenders: { std: { type: 'stdout' } }, + categories: { default: { appenders: ['std'], level: 'info' } } }) const logger = log4js.getLogger('certd') export default logger diff --git a/packages/certd/test/dns-provider/aliyun.test.js b/packages/certd/test/dns-provider/aliyun.test.js index 408886d8..0eec3899 100644 --- a/packages/certd/test/dns-provider/aliyun.test.js +++ b/packages/certd/test/dns-provider/aliyun.test.js @@ -4,28 +4,28 @@ import AliyunDnsProvider from '../../src/dns-provider/impl/aliyun.js' const { expect } = pkg describe('AliyunDnsProvider', function () { it('#getDomainList', async function () { - const aliyunDnsProvider = new AliyunDnsProvider(options.providers.aliyun) + const aliyunDnsProvider = new AliyunDnsProvider(options.accessProviders.aliyun) const domainList = await aliyunDnsProvider.getDomainList() console.log('domainList', domainList) expect(domainList.length).gt(0) }) it('#getRecords', async function () { - const aliyunDnsProvider = new AliyunDnsProvider(options.providers.aliyun) + const aliyunDnsProvider = new AliyunDnsProvider(options.accessProviders.aliyun) const recordList = await aliyunDnsProvider.getRecords('docmirror.cn', '*') console.log('recordList', recordList) expect(recordList.length).gt(0) }) it('#createRecord', async function () { - const aliyunDnsProvider = new AliyunDnsProvider(options.providers.aliyun) + const aliyunDnsProvider = new AliyunDnsProvider(options.accessProviders.aliyun) const recordId = await aliyunDnsProvider.createRecord('___certd___.__test__.docmirror.cn', 'TXT', 'aaaa') console.log('recordId', recordId) expect(recordId != null).ok }) it('#removeRecord', async function () { - const aliyunDnsProvider = new AliyunDnsProvider(options.providers.aliyun) + const aliyunDnsProvider = new AliyunDnsProvider(options.accessProviders.aliyun) const recordId = await aliyunDnsProvider.removeRecord('___certd___.__test__.docmirror.cn', 'TXT', 'aaaa') console.log('recordId', recordId) expect(recordId != null).ok diff --git a/packages/certd/test/options.js b/packages/certd/test/options.js index 74101ab9..bb4f1e43 100644 --- a/packages/certd/test/options.js +++ b/packages/certd/test/options.js @@ -1,7 +1,7 @@ import _ from 'lodash' -import optionsPrivate from './options.private.js' +import optionsPrivate from '../../../test/options.private.js' const defaultOptions = { - providers: { + accessProviders: { aliyun: { providerType: 'aliyun', accessKeyId: '', diff --git a/packages/deployer/package.json b/packages/deployer/package.json index 01a86303..0bc4f5ed 100644 --- a/packages/deployer/package.json +++ b/packages/deployer/package.json @@ -2,7 +2,7 @@ "name": "@certd/samples", "version": "0.0.1", "description": "", - "main": "src/index.js", + "main": "./src/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, @@ -11,7 +11,9 @@ "@alicloud/pop-core": "^1.7.10", "@types/node": "^14.14.13", "lodash": "^4.17.20", - "log4js": "^6.3.0" + "log4js": "^6.3.0", + "@certd/certd": "^0.0.1", + "@certd/plugins": "^0.0.1" }, "devDependencies": { "chai": "^4.2.0", diff --git a/packages/deployer/src/index.js b/packages/deployer/src/index.js index d218273b..fe06d3bd 100644 --- a/packages/deployer/src/index.js +++ b/packages/deployer/src/index.js @@ -1,30 +1,34 @@ import Certd from '@certd/certd' import CertdPlugins from '@certd/plugins' -import options from './options' -import log from './util.log' -export class DeployFlow { - async run () { +import log from './util.log.js' +export class Deployer { + async run (options) { const certd = new Certd() const cert = certd.certApply(options) + const context = {} for (const deploy of options.deploy) { log.info(`-------部署任务【${deploy.deployName}】开始-------`) for (const task of deploy.tasks) { - await this.runTask({ options, cert, task }) + await this.runTask({ options, cert, task, context }) } log.info(`-------部署任务【${deploy.deployName}】完成-------`) } + return { + cert, + context + } } - async runTask ({ options, task, cert }) { + async runTask ({ options, task, cert, context }) { const taskType = task.type const plugin = CertdPlugins[taskType] if (plugin == null) { throw new Error(`插件:${taskType}还未安装`) } - const context = {} + log.info(`--插件【${task.taskName}】开始执行-------`) - await plugin.execute({ cert, providers: options.providers, args: task, context }) + await plugin.execute({ cert, accessProviders: options.accessProviders, args: task, context }) log.info(`--插件【${task.taskName}】执行完成-------`) } } diff --git a/packages/deployer/src/util.log.js b/packages/deployer/src/util.log.js index eaa3c728..0abd0555 100644 --- a/packages/deployer/src/util.log.js +++ b/packages/deployer/src/util.log.js @@ -1,11 +1,7 @@ -import util from './util.js' import log4js from 'log4js' -import path from 'path' -const level = process.env.NODE_ENV === 'development' ? 'debug' : 'info' -const filename = path.join(util.getUserBasePath(), '/logs/certd.log') log4js.configure({ - appenders: { std: { type: 'stdout' }, file: { type: 'file', pattern: 'yyyy-MM-dd', daysToKeep: 3, filename } }, - categories: { default: { appenders: ['std'], level: level } } + appenders: { std: { type: 'stdout' } }, + categories: { default: { appenders: ['std'], level: 'info' } } }) const logger = log4js.getLogger('certd') export default logger diff --git a/packages/deployer/test/index.test.js b/packages/deployer/test/index.test.js new file mode 100644 index 00000000..92752d36 --- /dev/null +++ b/packages/deployer/test/index.test.js @@ -0,0 +1,13 @@ +import pkg from 'chai' +import options from './options.js' +import Deployer from '../src/index.js' +const { expect } = pkg +describe('AutoDeploy', function () { + it('#run', async function () { + const deploy = new Deployer() + const ret = deploy.run(options) + expect(ret).ok + expect(ret.cert).ok + expect(ret.AliyunCertId).ok + }) +}) diff --git a/packages/deployer/test/options.js b/packages/deployer/test/options.js index 74101ab9..c765ea6f 100644 --- a/packages/deployer/test/options.js +++ b/packages/deployer/test/options.js @@ -1,7 +1,7 @@ import _ from 'lodash' -import optionsPrivate from './options.private.js' +import optionsPrivate from '../../../test/options.private.js' const defaultOptions = { - providers: { + accessProviders: { aliyun: { providerType: 'aliyun', accessKeyId: '', @@ -17,7 +17,7 @@ const defaultOptions = { } }, cert: { - domains: ['*.docmirror.club', 'docmirror.club'], + domains: ['*.docmirror.club', 'docmirror.xyz'], email: 'xiaojunnuo@qq.com', challenge: { challengeType: 'dns', @@ -38,17 +38,17 @@ const defaultOptions = { tasks: [ { name: '上传证书到云', - taskType: 'uploadCertToCloud', + type: 'uploadCertToAliyun', certStore: 'aliyun' }, - { + { // CDN、SCDN、DCDN和负载均衡(SLB) name: '部署证书到SLB', - taskType: 'deployCertToAliyunSLB', + type: 'deployCertToAliyunSLB', certStore: 'aliyun' }, { name: '部署证书到阿里云集群Ingress', - taskType: 'deployCertToAliyunK8sIngress', + type: 'deployCertToAliyunK8sIngress', certStore: 'aliyun' } ] @@ -58,7 +58,7 @@ const defaultOptions = { tasks: [ { name: '上传证书到服务器,并重启nginx', - taskType: 'sshAndExecute', + type: 'sshAndExecute', ssh: 'myLinux', upload: [ { from: '{certPath}', to: '/xxx/xxx/xxx.cert.pem' }, @@ -73,7 +73,7 @@ const defaultOptions = { tasks: [ { name: '触发jenkins任务', - taskType: 'sshAndExecute', + type: 'sshAndExecute', ssh: 'myLinux', script: 'sudo systemctl restart nginx' } diff --git a/packages/plugins/package.json b/packages/plugins/package.json index 72ac6f6b..dcb66f13 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -2,7 +2,7 @@ "name": "@certd/plugins", "version": "0.0.1", "description": "", - "main": "src/index.js", + "main": "./src/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, @@ -10,8 +10,10 @@ "dependencies": { "@alicloud/pop-core": "^1.7.10", "@types/node": "^14.14.13", + "dayjs": "^1.9.7", "lodash": "^4.17.20", - "log4js": "^6.3.0" + "log4js": "^6.3.0", + "@certd/certd": "^0.0.1" }, "devDependencies": { "chai": "^4.2.0", diff --git a/packages/plugins/src/aliyun/abstract-aliyun.js b/packages/plugins/src/aliyun/abstract-aliyun.js new file mode 100644 index 00000000..e906f9f6 --- /dev/null +++ b/packages/plugins/src/aliyun/abstract-aliyun.js @@ -0,0 +1,23 @@ +import { AbstractPlugin } from '../abstract-plugin.js' + +export class AbstractAliyunPlugin extends AbstractPlugin { + format (pem) { + pem = pem.replace(/\r/g, '') + pem = pem.replace(/\n\n/g, '') + pem = pem.replace(/\n$/g, '') + return pem + } + + getAccessProvider (accessProvider, accessProviders) { + if (typeof accessProvider === 'string' && accessProviders) { + accessProvider = accessProviders[accessProvider] + } + return accessProvider + } + + checkRet (ret) { + if (ret.code != null) { + throw new Error('执行失败:', ret.Message) + } + } +} diff --git a/packages/plugins/src/aliyun/deploy-to-cdn/index.js b/packages/plugins/src/aliyun/deploy-to-cdn/index.js new file mode 100644 index 00000000..a1ebd280 --- /dev/null +++ b/packages/plugins/src/aliyun/deploy-to-cdn/index.js @@ -0,0 +1,93 @@ +import { AbstractPlugin } from '../../abstract-plugin/index.js' +import Core from '@alicloud/pop-core' +import dayjs from 'dayjs' +export class UploadCertToAliyunPlugin extends AbstractPlugin { + /** + * 插件定义 + * 名称 + * 入参 + * 出参 + */ + static define () { + return { + name: 'deployToCdn', + label: '部署到阿里云CDN', + input: { + domainName: { + label: 'cdn加速域名', + required: true + }, + certName: { + label: '证书名称' + }, + certType: { + label: '证书来源', + options: [ + { value: 'upload', label: '直接上传' }, + { value: 'cas', label: '从证书库(需要uploadCertToAliyun插件作为前置任务)' } + ], + required: true + }, + // serverCertificateStatus: { + // label: '启用https', + // options: [ + // { value: 'on', label: '开启HTTPS,并更新证书' }, + // { value: 'auto', label: '若HTTPS开启则更新,未开启不更新' } + // ], + // required:true + // }, + accessProvider: { + label: 'Access提供者', + type: [String, Object], + desc: 'AccessProviders的key 或 一个包含accessKeyId与accessKeySecret的对象', + options: 'accessProviders[type=aliyun]', + required: true + } + }, + output: { + + } + } + } + + getClient (aliyunProvider) { + return new Core({ + accessKeyId: aliyunProvider.accessKeyId, + accessKeySecret: aliyunProvider.accessKeySecret, + endpoint: 'https://cdn.aliyuncs.com', + apiVersion: '2018-05-10' + }) + } + + async execute ({ accessProviders, cert, args, context }) { + let { accessProvider } = args + if (typeof accessProvider === 'string' && accessProviders) { + accessProvider = accessProviders[accessProvider] + } + const client = this.getClient(accessProvider) + + const { certName, certType, domainName } = args + const CertName = certName + '-' + dayjs().format('YYYYMMDDHHmmss') + + const params = { + RegionId: 'cn-hangzhou', + DomainName: domainName, + ServerCertificateStatus: 'on', + CertName: CertName, + CertType: certType, + ServerCertificate: context.aliyunCertId + } + if (certType === 'upload') { + // eslint-disable-next-line no-unused-expressions + params.ServerCertificate = this.format(cert.crt.toString()), + params.PrivateKey = this.format(cert.key.toString()) + } + + const requestOption = { + method: 'POST' + } + const ret = await client.request('SetDomainServerCertificate', params, requestOption) + checkRet(ret) + console.log('设置cdn证书成功', ret) + } +} diff --git a/packages/plugins/src/aliyun/upload-to-aliyun/index.js b/packages/plugins/src/aliyun/upload-to-aliyun/index.js new file mode 100644 index 00000000..d15d359a --- /dev/null +++ b/packages/plugins/src/aliyun/upload-to-aliyun/index.js @@ -0,0 +1,65 @@ +import { AbstractPlugin } from '../../abstract-plugin/index.js' +import Core from '@alicloud/pop-core' +import dayjs from 'dayjs' +import { AbstractAliyunPlugin } from '../abstract-aliyun.js' +export class UploadToAliyunPlugin extends AbstractAliyunPlugin { + /** + * 插件定义 + * 名称 + * 入参 + * 出参 + */ + static define () { + return { + name: 'updateToAliyun', + label: '上传证书到阿里云', + input: { + name: { + label: '证书名称' + }, + accessProvider: { + label: 'Access提供者', + type: [String, Object], + desc: 'AccessProviders的key 或 一个包含accessKeyId与accessKeySecret的对象', + options: 'accessProviders[type=aliyun]' + } + }, + output: { + aliyunCertId: { + type: String, + desc: '上传成功后的阿里云CertId' + } + } + } + } + + getClient (aliyunProvider) { + return new Core({ + accessKeyId: aliyunProvider.accessKeyId, + accessKeySecret: aliyunProvider.accessKeySecret, + endpoint: 'https://cas.aliyuncs.com', + apiVersion: '2018-07-13' + }) + } + + async execute ({ accessProviders, cert, args, context }) { + const { name, provider } = args + const certName = name + '-' + dayjs().format('YYYYMMDDHHmmss') + const params = { + RegionId: 'cn-hangzhou', + Name: certName, + Cert: this.format(cert.crt.toString()), + Key: this.format(cert.key.toString()) + } + + const requestOption = { + method: 'POST' + } + + const accesseProvider = this.getAccessProvider(provider, accessProviders) + const client = this.getClient(accesseProvider) + const ret = await client.request('CreateUserCertificate', params, requestOption) + + context.aliyunCertId = ret.CertId + } +} diff --git a/packages/plugins/src/upload/upload-cert-to-aliyun/index.js b/packages/plugins/src/upload/upload-cert-to-aliyun/index.js deleted file mode 100644 index 2c710ce2..00000000 --- a/packages/plugins/src/upload/upload-cert-to-aliyun/index.js +++ /dev/null @@ -1,33 +0,0 @@ -import { AbstractPlugin } from '../../abstract-plugin/index.js' -import Core from '@alicloud/pop-core' -import dayjs from 'dayjs' -export class UploadCertToAliyunPlugin extends AbstractPlugin { - getClient (aliyunProvider) { - this.client = new Core({ - accessKeyId: aliyunProvider.accessKeyId, - accessKeySecret: aliyunProvider.accessKeySecret, - endpoint: 'https://alidns.aliyuncs.com', - apiVersion: '2015-01-09' - }) - } - - async execute ({ providers, cert, args, context }) { - const { name, provider } = args - const certName = name + '-' + dayjs().format('YYYYMMDDHHmmss') - const params = { - RegionId: 'cn-hangzhou', - Name: certName, - Cert: cert.crt.toString(), - Key: cert.key.toString() - } - - const requestOption = { - method: 'POST' - } - - const client = this.getClient(providers[provider]) - const ret = await client.request('CreateUserCertificate', params, requestOption) - - context.AliyunCertId = ret.CertId - } -} diff --git a/packages/plugins/src/utils/util.log.js b/packages/plugins/src/utils/util.log.js index 2309aa59..0abd0555 100644 --- a/packages/plugins/src/utils/util.log.js +++ b/packages/plugins/src/utils/util.log.js @@ -1,11 +1,7 @@ -import util from './util.js' import log4js from 'log4js' -import path from 'path' -const level = process.env.NODE_ENV === 'development' ? 'debug' : 'info' -const filename = path.join(util.getUserBasePath(), '/logs/certd.log') log4js.configure({ - appenders: { std: { type: 'stdout' }, file: { type: 'file', pattern: 'yyyy-MM-dd', daysToKeep: 3, filename } }, - categories: { default: { appenders: ['file', 'std'], level: level } } + appenders: { std: { type: 'stdout' } }, + categories: { default: { appenders: ['std'], level: 'info' } } }) const logger = log4js.getLogger('certd') export default logger diff --git a/packages/plugins/test/aliyun/upload-to-aliyun.test.js b/packages/plugins/test/aliyun/upload-to-aliyun.test.js new file mode 100644 index 00000000..124e0557 --- /dev/null +++ b/packages/plugins/test/aliyun/upload-to-aliyun.test.js @@ -0,0 +1,21 @@ +import pkg from 'chai' +import { UploadToAliyunPlugin } from '../../src/aliyun/upload-to-aliyun/index.js' +import options from '../options.js' +import { Certd } from '@certd/certd' +const { expect } = pkg +describe('PluginUploadToAliyun', function () { + it('#execute', async function () { + const plugin = new UploadToAliyunPlugin() + const certd = new Certd() + const cert = certd.readCurrentCert('xiaojunnuo@qq.com', ['*.docmirror.club', 'docmirror.club']) + const context = {} + await plugin.execute({ + accessProviders: options.accessProviders, + cert, + args: { name: '上传证书到阿里云测试', provider: 'aliyun' }, + context + }) + + console.log('context:', context) + }) +}) diff --git a/packages/plugins/test/options.js b/packages/plugins/test/options.js index 74101ab9..381fbe35 100644 --- a/packages/plugins/test/options.js +++ b/packages/plugins/test/options.js @@ -1,7 +1,7 @@ import _ from 'lodash' -import optionsPrivate from './options.private.js' +import optionsPrivate from '../../../test/options.private.mjs' const defaultOptions = { - providers: { + accessProviders: { aliyun: { providerType: 'aliyun', accessKeyId: '', diff --git a/packages/plugins/test/upload/upload-cert-to-aliyun.test.js b/packages/plugins/test/upload/upload-cert-to-aliyun.test.js deleted file mode 100644 index 6ce2f8c8..00000000 --- a/packages/plugins/test/upload/upload-cert-to-aliyun.test.js +++ /dev/null @@ -1,17 +0,0 @@ -import pkg from 'chai' -import UploadCertToAliyun from '../../src/upload/upload-cert-to-aliyun/index.js' -import options from '../options' -import Certd from '@certd/certd' -const { expect } = pkg -describe('PluginUploadCertToAliyun', function () { - it('#execute', function () { - const plugin = new UploadCertToAliyun() - const certd = new Certd() - const cert = certd.readCurrentCert('xiaojunnuo@qq.com', ['*.docmirror.cn']) - plugin.execute({ - providers: options.providers, - cert, - args: { name: '上传证书到阿里云', provider: 'aliyun' } - }) - }) -}) diff --git a/packages/plugins/yarn.lock b/packages/plugins/yarn.lock index ef921f16..03d13d69 100644 --- a/packages/plugins/yarn.lock +++ b/packages/plugins/yarn.lock @@ -333,6 +333,11 @@ date-format@^3.0.0: resolved "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95" integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w== +dayjs@^1.9.7: + version "1.9.7" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.9.7.tgz#4b260bb17dceed2d5f29038dfee03c65a6786fc0" + integrity sha512-IC877KBdMhBrCfBfJXHQlo0G8keZ0Opy7YIIq5QKtUbCuHMzim8S4PyiVK4YmihI3iOF9lhfUBW4AQWHTR5WHA== + debug@4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"