feat: 自动化流程

master
xiaojunnuo 2020-12-21 00:32:17 +08:00
parent e84f5e8b0a
commit 6529fd2fdb
27 changed files with 343 additions and 184 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "packages/node-acme-client"]
path = packages/node-acme-client
url = https://github.com/certd/node-acme-client

View File

@ -4,5 +4,8 @@
"devDependencies": { "devDependencies": {
"lerna": "^3.18.4" "lerna": "^3.18.4"
}, },
"scripts": {
"submodule":"git submodule update --init --recursive"
},
"license": "MIT" "license": "MIT"
} }

View File

@ -12,10 +12,10 @@
"dependencies": { "dependencies": {
"@alicloud/pop-core": "^1.7.10", "@alicloud/pop-core": "^1.7.10",
"@types/node": "^14.14.13", "@types/node": "^14.14.13",
"acme-client": "^4.1.2",
"dayjs": "^1.9.7", "dayjs": "^1.9.7",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"log4js": "^6.3.0" "log4js": "^6.3.0",
"@certd/acme-client": "^0.0.1"
}, },
"devDependencies": { "devDependencies": {
"chai": "^4.2.0", "chai": "^4.2.0",

View File

@ -1,5 +1,5 @@
import acme from './acme/index.cjs'
import log from './utils/util.log.js' import log from './utils/util.log.js'
import acme from '@certd/acme-client'
import _ from 'lodash' import _ from 'lodash'
import sleep from './utils/util.sleep.js' import sleep from './utils/util.sleep.js'
export class AcmeService { export class AcmeService {
@ -27,7 +27,10 @@ export class AcmeService {
const key = await this.getAccountKey(email) const key = await this.getAccountKey(email)
const client = new acme.Client({ const client = new acme.Client({
directoryUrl: acme.directory.letsencrypt.staging, directoryUrl: acme.directory.letsencrypt.staging,
accountKey: key accountKey: key,
backoffAttempts: 10,
backoffMin: 5000,
backoffMax: 30000
}) })
return client return client
} }
@ -125,7 +128,7 @@ export class AcmeService {
csr, csr,
email: email, email: email,
termsOfServiceAgreed: true, termsOfServiceAgreed: true,
challengePriority: ['dns-01', 'http-01'], challengePriority: ['dns-01'],
challengeCreateFn: (authz, challenge, keyAuthorization) => { challengeCreateFn: (authz, challenge, keyAuthorization) => {
return this.challengeCreateFn(authz, challenge, keyAuthorization, dnsProvider) return this.challengeCreateFn(authz, challenge, keyAuthorization, dnsProvider)
}, },

View File

@ -1,4 +1,3 @@
process.env.DEBUG = '*'
/** /**
* acme-client * acme-client
*/ */

View File

@ -1,13 +1,12 @@
import logger from './utils/util.log.js'
import { AcmeService } from './acme.js' import { AcmeService } from './acme.js'
import { FileStore } from './store/file-store.js' import { FileStore } from './store/file-store.js'
import { DnsProviderFactory } from './dns-provider/dns-provider-factory.js' import { DnsProviderFactory } from './dns-provider/dns-provider-factory.js'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import path from 'path' import path from 'path'
import _ from 'lodash'
import fs from 'fs' import fs from 'fs'
import util from './utils/util.js' import util from './utils/util.js'
import forge from 'node-forge' import forge from 'node-forge'
process.env.DEBUG = '*'
export class Certd { export class Certd {
constructor (options) { constructor (options) {
this.store = new FileStore() this.store = new FileStore()
@ -15,73 +14,150 @@ export class Certd {
this.options = options this.options = options
} }
getMainDomain (domains) {
if (domains == null) {
return null
}
if (typeof domains === 'string') {
return domains
}
if (domains.length > 0) {
return domains[0]
}
}
buildDomainFileName (domains) {
const domain = this.getMainDomain(domains)
return domain.replace(/\*/g, '_')
}
buildCertDir (email, domains) { buildCertDir (email, domains) {
let domainStr = _.join(domains) const domainFileName = this.buildDomainFileName(domains)
domainStr = domainStr.replace(/\*/g, '') return path.join(email, '/certs/', domainFileName)
const dir = path.join(email, '/certs/', domainStr)
return dir
} }
async certApply (options) { async certApply (options) {
if (options == null) { if (options == null) {
options = this.options options = this.options
} }
const certOptions = options.cert let oldCert
try {
oldCert = this.readCurrentCert(options.cert.email, options.cert.domains)
} catch (e) {
logger.warn('读取cert失败', e)
}
if (oldCert == null) {
logger.info('还未申请过,准备申请新证书')
} else {
const ret = this.isWillExpire(oldCert.expires, options.cert.renewDays)
if (!ret.isWillExpire) {
logger.info('证书还未过期:', oldCert.expires, ',剩余', ret.leftDays, '天')
if (options.args.forceCert) {
logger.info('准备强制更新证书')
} else {
logger.info('暂不更新证书')
oldCert.isNew = false
return oldCert
}
} else {
logger.info('即将过期,准备更新证书')
}
}
// 执行证书申请步骤
return await this.doCertApply(options)
}
async doCertApply (options) {
const accessProviders = options.accessProviders const accessProviders = options.accessProviders
const providerOptions = accessProviders[certOptions.challenge.dnsProvider] const providerOptions = accessProviders[options.cert.challenge.dnsProvider]
const dnsProvider = await DnsProviderFactory.createByType(providerOptions.providerType, providerOptions) const dnsProvider = await DnsProviderFactory.createByType(providerOptions.providerType, providerOptions)
const cert = await this.acme.order({ const cert = await this.acme.order({
email: certOptions.email, email: options.cert.email,
domains: certOptions.domains, domains: options.cert.domains,
dnsProvider: dnsProvider, dnsProvider: dnsProvider,
csrInfo: certOptions.csrInfo csrInfo: options.cert.csrInfo
}) })
this.writeCert(certOptions.email, certOptions.domains, cert) const certDir = this.writeCert(options.cert.email, options.cert.domains, cert)
const { detail, expires } = this.getDetailFromCrt(cert.crt) const { detail, expires } = this.getCrtDetail(cert.crt)
return { return {
...cert, ...cert,
detail, detail,
expires expires,
certDir,
isNew: true
} }
} }
writeCert (email, domains, cert) { writeCert (email, domains, cert) {
const certFilesRootDir = this.buildCertDir(email, domains) const certFilesRootDir = this.buildCertDir(email, domains)
const dirPath = path.join(certFilesRootDir, dayjs().format('YYYY.MM.DD.HHmmss')) const dirPath = path.join(certFilesRootDir, dayjs().format('YYYY.MM.DD.HHmmss'))
this.store.set(path.join(dirPath, '/cert.crt'), cert.crt)
this.store.set(path.join(dirPath, '/cert.key'), cert.key) const domainFileName = this.buildDomainFileName(domains)
this.store.set(path.join(dirPath, '/cert.csr'), cert.csr)
this.store.set(path.join(dirPath, `/${domainFileName}.crt`), cert.crt)
this.store.set(path.join(dirPath, `/${domainFileName}.key`), cert.key)
this.store.set(path.join(dirPath, `/${domainFileName}.csr`), cert.csr)
const linkPath = path.join(util.getUserBasePath(), certFilesRootDir, 'current') const linkPath = path.join(util.getUserBasePath(), certFilesRootDir, 'current')
const lastPath = path.join(util.getUserBasePath(), dirPath) const lastPath = path.join(util.getUserBasePath(), dirPath)
// if (!fs.existsSync(linkPath)) { if (fs.existsSync(linkPath)) {
// fs.mkdirSync(linkPath) try {
// } fs.unlinkSync(linkPath)
fs.symlinkSync(lastPath, linkPath) } catch (e) {
logger.error('unlink error:', e)
}
}
fs.symlinkSync(lastPath, linkPath, 'dir')
return linkPath
} }
readCurrentCert (email, domains) { readCurrentCert (email, domains) {
const certFilesRootDir = this.buildCertDir(email, domains) const certFilesRootDir = this.buildCertDir(email, domains)
const currentPath = path.join(certFilesRootDir, 'current') const currentPath = path.join(certFilesRootDir, 'current')
const domainFileName = this.buildDomainFileName(domains)
const crt = this.store.get(currentPath + '/cert.crt') const crt = this.store.get(currentPath + `/${domainFileName}.crt`)
const key = this.store.get(currentPath + '/cert.key') if (crt == null) {
const csr = this.store.get(currentPath + '/cert.csr') return null
}
const { detail, expires } = this.getDetailFromCrt(crt) const key = this.store.get(currentPath + `/${domainFileName}.key`)
const csr = this.store.get(currentPath + `/${domainFileName}.csr`)
const cert = {
crt, key, csr, detail, expires const { detail, expires } = this.getCrtDetail(crt)
const certDir = path.join(util.getUserBasePath(), currentPath)
return {
crt, key, csr, detail, expires, certDir
} }
return cert
} }
getDetailFromCrt (crt) { getCrtDetail (crt) {
const pki = forge.pki const pki = forge.pki
const detail = pki.certificateFromPem(crt.toString()) const detail = pki.certificateFromPem(crt.toString())
const expires = detail.validity.notAfter const expires = detail.validity.notAfter
return { detail, expires } return { detail, expires }
} }
/**
* 检查是否过期默认提前20天
* @param expires
* @param maxDays
* @returns {boolean}
*/
isWillExpire (expires, maxDays = 20) {
if (expires == null) {
throw new Error('过期时间不能为空')
}
// 检查有效期
const leftDays = dayjs(expires).diff(dayjs(), 'day')
return {
isWillExpire: leftDays < maxDays,
leftDays
}
}
} }

View File

@ -1,5 +1,5 @@
import _ from 'lodash' import _ from 'lodash'
import optionsPrivate from '../../../test/options.private.js' import optionsPrivate from '../../../test/options.private.mjs'
const defaultOptions = { const defaultOptions = {
accessProviders: { accessProviders: {
aliyun: { aliyun: {

View File

@ -1,34 +0,0 @@
import Certd from '@certd/certd'
import CertdPlugins from '@certd/plugins'
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, context })
}
log.info(`-------部署任务【${deploy.deployName}】完成-------`)
}
return {
cert,
context
}
}
async runTask ({ options, task, cert, context }) {
const taskType = task.type
const plugin = CertdPlugins[taskType]
if (plugin == null) {
throw new Error(`插件:${taskType}还未安装`)
}
log.info(`--插件【${task.taskName}】开始执行-------`)
await plugin.execute({ cert, accessProviders: options.accessProviders, args: task, context })
log.info(`--插件【${task.taskName}】执行完成-------`)
}
}

View File

@ -1,13 +0,0 @@
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
})
})

View File

@ -1,5 +1,5 @@
{ {
"name": "@certd/samples", "name": "@certd/executor",
"version": "0.0.1", "version": "0.0.1",
"description": "", "description": "",
"main": "./src/index.js", "main": "./src/index.js",

View File

@ -0,0 +1,103 @@
import { Certd } from '@certd/certd'
import DefaultPlugins from '@certd/plugins'
import logger from './util.log.js'
import _ from 'lodash'
export class Executor {
constructor (args = {}) {
this.certd = new Certd()
const { plugins } = args
this.initPlugins(plugins)
}
use (plugin) {
if (plugin == null) {
return
}
this.plugins[plugin.name] = plugin
if (plugin.define) {
const define = plugin.define()
this.plugins[define.name] = plugin
}
}
initPlugins (customPlugins) {
this.plugins = {}
for (const key in DefaultPlugins) {
this.use(DefaultPlugins[key])
}
if (customPlugins) {
for (const plugin of customPlugins) {
this.use(plugin)
}
}
}
async run (options, args) {
if (args != null) {
_.merge(options.args, args)
}
logger.info('任务开始')
const cert = await this.runCertd(options)
if (cert == null) {
throw new Error('申请证书失败')
}
logger.info('证书保存路径:', cert.certDir)
if (!cert.isNew) {
// 如果没有更新
if (!options.args.forceDeploy) {
// 且不需要强制运行deploy
logger.info('证书无更新,无需重新部署')
logger.info('任务完成')
return { cert }
}
}
const context = {}
await this.runDeploys({ options, cert, context })
logger.info('任务完成')
return {
cert,
context
}
}
async runCertd (options) {
logger.info(`申请证书${JSON.stringify(options.cert.domains)}开始`)
const cert = await this.certd.certApply(options)
logger.info(`申请证书${JSON.stringify(options.cert.domains)}完成`)
return cert
}
async runDeploys ({ options, cert, context }) {
if (cert == null) {
cert = this.certd.readCurrentCert(options.cert.email, options.cert.domains)
}
for (const deploy of options.deploy) {
logger.info(`--部署任务【${deploy.name}】开始`)
if (deploy.disabled === true) {
logger.info('----此任务已被禁用,跳过')
break
}
for (const task of deploy.tasks) {
await this.runTask({ options, cert, task, context })
}
logger.info(`--部署任务【${deploy.name}】完成`)
}
}
async runTask ({ options, task, cert, context }) {
const taskType = task.type
const Plugin = this.plugins[taskType]
if (Plugin == null) {
throw new Error(`----插件:${taskType}还未安装`)
}
logger.info(`----任务【${task.name}】开始执行`)
let instance = Plugin
if (Plugin instanceof Function) {
instance = new Plugin()
}
await instance.execute({ cert, accessProviders: options.accessProviders, args: task, context })
logger.info(`----任务【${task.name}】执行完成`)
}
}

View File

@ -5,3 +5,12 @@ log4js.configure({
}) })
const logger = log4js.getLogger('certd') const logger = log4js.getLogger('certd')
export default logger export default logger
// import debug from 'debug'
// debug.enable('info,debug,error,warn')
// export default {
// debug: debug('debug'),
// info: debug('info'),
// error: debug('error'),
// warn: debug('warn')
// }

View File

@ -0,0 +1,25 @@
import pkg from 'chai'
import options from './options.js'
import { Executor } from '../src/index.js'
const { expect } = pkg
describe('AutoDeploy', function () {
it('#run', async function () {
const executor = new Executor()
const ret = await executor.run(options)
expect(ret).ok
expect(ret.cert).ok
})
it('#forceCert', async function () {
const executor = new Executor()
const ret = await executor.run(options, { forceCert: true, forceDeploy: false })
expect(ret).ok
expect(ret.cert).ok
})
it('#forceDeploy', async function () {
const executor = new Executor()
const ret = await executor.run(options, { forceCert: false, forceDeploy: true })
expect(ret).ok
expect(ret.cert).ok
})
})

View File

@ -1,6 +1,11 @@
import _ from 'lodash' import _ from 'lodash'
import optionsPrivate from '../../../test/options.private.js' import optionsPrivate from '../../../test/options.private.mjs'
const defaultOptions = { const defaultOptions = {
args: {
forceCert: false, // 强制更新证书
skipCert: false, // 是否跳过证书申请环节
forceDeploy: false
},
accessProviders: { accessProviders: {
aliyun: { aliyun: {
providerType: 'aliyun', providerType: 'aliyun',
@ -17,7 +22,7 @@ const defaultOptions = {
} }
}, },
cert: { cert: {
domains: ['*.docmirror.club', 'docmirror.xyz'], domains: ['*.docmirror.cn'],
email: 'xiaojunnuo@qq.com', email: 'xiaojunnuo@qq.com',
challenge: { challenge: {
challengeType: 'dns', challengeType: 'dns',
@ -34,27 +39,31 @@ const defaultOptions = {
}, },
deploy: [ deploy: [
{ {
deployName: '流程1-部署到阿里云系列产品', name: '流程1-部署到阿里云系列产品',
tasks: [ tasks: [
{ {
name: '上传证书到云', name: '上传证书到云',
type: 'uploadCertToAliyun', type: 'uploadCertToAliyun',
certStore: 'aliyun' accessProvider: 'aliyun'
}, },
{ // CDN、SCDN、DCDN和负载均衡SLB { // CDN、SCDN、DCDN和负载均衡SLB
name: '部署证书到SLB', name: '部署证书到CDN',
type: 'deployCertToAliyunSLB', type: 'deployCertToAliyunCDN',
certStore: 'aliyun' domainName: 'certd-cdn-upload.docmirror.cn',
}, certName: 'certd部署测试(upload)',
{ certType: 'upload',
name: '部署证书到阿里云集群Ingress', accessProvider: 'aliyun'
type: 'deployCertToAliyunK8sIngress',
certStore: 'aliyun'
} }
// {
// name: '部署证书到阿里云集群Ingress',
// type: 'deployCertToAliyunK8sIngress',
// accessProvider: 'aliyun'
// }
] ]
}, },
{ {
deployName: '流程2-部署到nginx服务器', name: '流程2-部署到nginx服务器',
disabled: true,
tasks: [ tasks: [
{ {
name: '上传证书到服务器,并重启nginx', name: '上传证书到服务器,并重启nginx',

@ -0,0 +1 @@
Subproject commit 5a3f7446222fa123b645e51e954ccfd981f459bd

View File

@ -1,5 +1,10 @@
import fs from 'fs' import fs from 'fs'
import logger from '../utils/util.log.js'
export class AbstractPlugin { export class AbstractPlugin {
constructor () {
this.logger = logger
}
async executeFromContextFile (options = {}) { async executeFromContextFile (options = {}) {
const { contextPath } = options const { contextPath } = options
const contextJson = fs.readFileSync(contextPath) const contextJson = fs.readFileSync(contextPath)
@ -13,5 +18,10 @@ export class AbstractPlugin {
return context return context
} }
getAccessProvider (accessProvider, accessProviders) {
if (typeof accessProvider === 'string' && accessProviders) {
accessProvider = accessProviders[accessProvider]
}
return accessProvider
}
} }

View File

@ -1,20 +1,13 @@
import { AbstractPlugin } from '../abstract-plugin.js' import { AbstractPlugin } from '../abstract-plugin/index.js'
export class AbstractAliyunPlugin extends AbstractPlugin { export class AbstractAliyunPlugin extends AbstractPlugin {
format (pem) { format (pem) {
pem = pem.replace(/\r/g, '') pem = pem.replace(/\r/g, '')
pem = pem.replace(/\n\n/g, '') pem = pem.replace(/\n\n/g, '\n')
pem = pem.replace(/\n$/g, '') pem = pem.replace(/\n$/g, '')
return pem return pem
} }
getAccessProvider (accessProvider, accessProviders) {
if (typeof accessProvider === 'string' && accessProviders) {
accessProvider = accessProviders[accessProvider]
}
return accessProvider
}
checkRet (ret) { checkRet (ret) {
if (ret.code != null) { if (ret.code != null) {
throw new Error('执行失败:', ret.Message) throw new Error('执行失败:', ret.Message)

View File

@ -1,7 +1,7 @@
import { AbstractPlugin } from '../../abstract-plugin/index.js' import { AbstractAliyunPlugin } from '../../aliyun/abstract-aliyun.js'
import Core from '@alicloud/pop-core' import Core from '@alicloud/pop-core'
import dayjs from 'dayjs' import dayjs from 'dayjs'
export class UploadCertToAliyunPlugin extends AbstractPlugin { export class DeployCertToAliyunCDN extends AbstractAliyunPlugin {
/** /**
* 插件定义 * 插件定义
* 名称 * 名称
@ -10,7 +10,7 @@ export class UploadCertToAliyunPlugin extends AbstractPlugin {
*/ */
static define () { static define () {
return { return {
name: 'deployToCdn', name: 'deployCertToAliyunCDN',
label: '部署到阿里云CDN', label: '部署到阿里云CDN',
input: { input: {
domainName: { domainName: {
@ -21,10 +21,11 @@ export class UploadCertToAliyunPlugin extends AbstractPlugin {
label: '证书名称' label: '证书名称'
}, },
certType: { certType: {
value: 'upload',
label: '证书来源', label: '证书来源',
options: [ options: [
{ value: 'upload', label: '直接上传' }, { value: 'upload', label: '直接上传' },
{ value: 'cas', label: '从证书库需要uploadCertToAliyun插件作为前置任务' } { value: 'cas', label: '从证书库', desc: '需要uploadCertToAliyun作为前置任务' }
], ],
required: true required: true
}, },
@ -76,12 +77,8 @@ export class UploadCertToAliyunPlugin extends AbstractPlugin {
ServerCertificateStatus: 'on', ServerCertificateStatus: 'on',
CertName: CertName, CertName: CertName,
CertType: certType, CertType: certType,
ServerCertificate: context.aliyunCertId ServerCertificate: super.format(cert.crt.toString()),
} PrivateKey: super.format(cert.key.toString())
if (certType === 'upload') {
// eslint-disable-next-line no-unused-expressions
params.ServerCertificate = this.format(cert.crt.toString()),
params.PrivateKey = this.format(cert.key.toString())
} }
return params return params
} }
@ -92,6 +89,6 @@ export class UploadCertToAliyunPlugin extends AbstractPlugin {
} }
const ret = await client.request('SetDomainServerCertificate', params, requestOption) const ret = await client.request('SetDomainServerCertificate', params, requestOption)
this.checkRet(ret) this.checkRet(ret)
console.log('设置cdn证书成功', ret) this.logger.info('设置cdn证书成功:', ret.RequestId)
} }
} }

View File

@ -1,8 +1,7 @@
import { AbstractPlugin } from '../../abstract-plugin/index.js'
import Core from '@alicloud/pop-core' import Core from '@alicloud/pop-core'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { AbstractAliyunPlugin } from '../abstract-aliyun.js' import { AbstractAliyunPlugin } from '../abstract-aliyun.js'
export class UploadToAliyunPlugin extends AbstractAliyunPlugin { export class UploadCertToAliyun extends AbstractAliyunPlugin {
/** /**
* 插件定义 * 插件定义
* 名称 * 名称
@ -11,7 +10,7 @@ export class UploadToAliyunPlugin extends AbstractAliyunPlugin {
*/ */
static define () { static define () {
return { return {
name: 'updateToAliyun', name: 'uploadCertToAliyun',
label: '上传证书到阿里云', label: '上传证书到阿里云',
input: { input: {
name: { name: {
@ -42,8 +41,8 @@ export class UploadToAliyunPlugin extends AbstractAliyunPlugin {
}) })
} }
async execute ({ accessProviders, cert, args, context }) { async execute ({ accessProviders, cert, args, context, logger }) {
const { name, provider } = args const { name, accessProvider } = args
const certName = name + '-' + dayjs().format('YYYYMMDDHHmmss') const certName = name + '-' + dayjs().format('YYYYMMDDHHmmss')
const params = { const params = {
RegionId: 'cn-hangzhou', RegionId: 'cn-hangzhou',
@ -56,10 +55,11 @@ export class UploadToAliyunPlugin extends AbstractAliyunPlugin {
method: 'POST' method: 'POST'
} }
const accesseProvider = this.getAccessProvider(provider, accessProviders) const provider = super.getAccessProvider(accessProvider, accessProviders)
const client = this.getClient(accesseProvider) const client = this.getClient(provider)
const ret = await client.request('CreateUserCertificate', params, requestOption) const ret = await client.request('CreateUserCertificate', params, requestOption)
this.checkRet(ret) this.checkRet(ret)
this.logger.info('证书上传成功certId=', ret.CertId)
context.aliyunCertId = ret.CertId context.aliyunCertId = ret.CertId
} }
} }

View File

@ -1,4 +1,5 @@
import { UploadCertToAliyun } from './upload/upload-cert-to-aliyun/index.js' import { UploadCertToAliyun } from './aliyun/upload-to-aliyun/index.js'
import { DeployCertToAliyunCDN } from './aliyun/deploy-to-cdn/index.js'
export default { export default {
UploadCertToAliyun UploadCertToAliyun, DeployCertToAliyunCDN
} }

View File

@ -0,0 +1,21 @@
import pkg from 'chai'
import { DeployCertToAliyunCDN } from '../../src/aliyun/deploy-to-cdn/index.js'
import options from '../options.js'
import { Certd } from '@certd/certd'
const { expect } = pkg
describe('DeployToAliyunCDN', function () {
it('#execute', async function () {
const plugin = new DeployCertToAliyunCDN()
const certd = new Certd()
const cert = certd.readCurrentCert('xiaojunnuo@qq.com', ['*.docmirror.cn'])
const ret = await plugin.execute({
accessProviders: options.accessProviders,
cert,
args: { domainName: 'certd-cdn-upload.docmirror.cn', certName: 'certd部署测试', certType: 'cas', accessProvider: 'aliyun' },
context: {
aliyunCertId: '4947435'
}
})
console.log('context:', context)
})
})

View File

@ -1,19 +1,19 @@
import pkg from 'chai' import pkg from 'chai'
import { UploadToAliyunPlugin } from '../../src/aliyun/upload-to-aliyun/index.js' import { UploadCertToAliyun } from '../../src/aliyun/upload-to-aliyun/index.js'
import options from '../options.js' import options from '../options.js'
import { Certd } from '@certd/certd' import { Certd } from '@certd/certd'
const { expect } = pkg const { expect } = pkg
describe('PluginUploadToAliyun', function () { describe('PluginUploadToAliyun', function () {
it('#execute', async function () { it('#execute', async function () {
const plugin = new UploadToAliyunPlugin() const plugin = new UploadCertToAliyun()
const certd = new Certd() const certd = new Certd()
const cert = certd.readCurrentCert('xiaojunnuo@qq.com', ['*.docmirror.club', 'docmirror.club']) const cert = certd.readCurrentCert('xiaojunnuo@qq.com', ['_.docmirror.cn'])
const context = {} const context = {}
await plugin.execute({ await plugin.execute({
accessProviders: options.accessProviders, accessProviders: options.accessProviders,
cert, cert,
args: { name: '上传证书到阿里云测试', provider: 'aliyun' }, context,
context args: { name: 'certd部署测试', provider: 'aliyun' }
}) })
console.log('context:', context) console.log('context:', context)

View File

@ -1,6 +1,7 @@
import _ from 'lodash' import _ from 'lodash'
import optionsPrivate from '../../../test/options.private.mjs' import optionsPrivate from '../../../test/options.private.mjs'
const defaultOptions = { const defaultOptions = {
version: '1.0.0',
accessProviders: { accessProviders: {
aliyun: { aliyun: {
providerType: 'aliyun', providerType: 'aliyun',
@ -31,55 +32,7 @@ const defaultOptions = {
organizationUnit: 'IT Department', organizationUnit: 'IT Department',
emailAddress: 'xiaojunnuo@qq.com' emailAddress: 'xiaojunnuo@qq.com'
} }
}, }
deploy: [
{
deployName: '流程1-部署到阿里云系列产品',
tasks: [
{
name: '上传证书到云',
taskType: 'uploadCertToCloud',
certStore: 'aliyun'
},
{
name: '部署证书到SLB',
taskType: 'deployCertToAliyunSLB',
certStore: 'aliyun'
},
{
name: '部署证书到阿里云集群Ingress',
taskType: 'deployCertToAliyunK8sIngress',
certStore: 'aliyun'
}
]
},
{
deployName: '流程2-部署到nginx服务器',
tasks: [
{
name: '上传证书到服务器,并重启nginx',
taskType: 'sshAndExecute',
ssh: 'myLinux',
upload: [
{ from: '{certPath}', to: '/xxx/xxx/xxx.cert.pem' },
{ from: '{keyPath}', to: '/xxx/xxx/xxx.key' }
],
script: 'sudo systemctl restart nginx'
}
]
},
{
deployName: '流程3-触发jenkins任务',
tasks: [
{
name: '触发jenkins任务',
taskType: 'sshAndExecute',
ssh: 'myLinux',
script: 'sudo systemctl restart nginx'
}
]
}
]
} }
_.merge(defaultOptions, optionsPrivate) _.merge(defaultOptions, optionsPrivate)