mirror of https://github.com/certd/certd
feat: 自动化流程
parent
e84f5e8b0a
commit
6529fd2fdb
|
@ -0,0 +1,3 @@
|
|||
[submodule "packages/node-acme-client"]
|
||||
path = packages/node-acme-client
|
||||
url = https://github.com/certd/node-acme-client
|
|
@ -4,5 +4,8 @@
|
|||
"devDependencies": {
|
||||
"lerna": "^3.18.4"
|
||||
},
|
||||
"scripts": {
|
||||
"submodule":"git submodule update --init --recursive"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
"dependencies": {
|
||||
"@alicloud/pop-core": "^1.7.10",
|
||||
"@types/node": "^14.14.13",
|
||||
"acme-client": "^4.1.2",
|
||||
"dayjs": "^1.9.7",
|
||||
"lodash": "^4.17.20",
|
||||
"log4js": "^6.3.0"
|
||||
"log4js": "^6.3.0",
|
||||
"@certd/acme-client": "^0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import acme from './acme/index.cjs'
|
||||
import log from './utils/util.log.js'
|
||||
import acme from '@certd/acme-client'
|
||||
import _ from 'lodash'
|
||||
import sleep from './utils/util.sleep.js'
|
||||
export class AcmeService {
|
||||
|
@ -27,7 +27,10 @@ export class AcmeService {
|
|||
const key = await this.getAccountKey(email)
|
||||
const client = new acme.Client({
|
||||
directoryUrl: acme.directory.letsencrypt.staging,
|
||||
accountKey: key
|
||||
accountKey: key,
|
||||
backoffAttempts: 10,
|
||||
backoffMin: 5000,
|
||||
backoffMax: 30000
|
||||
})
|
||||
return client
|
||||
}
|
||||
|
@ -125,7 +128,7 @@ export class AcmeService {
|
|||
csr,
|
||||
email: email,
|
||||
termsOfServiceAgreed: true,
|
||||
challengePriority: ['dns-01', 'http-01'],
|
||||
challengePriority: ['dns-01'],
|
||||
challengeCreateFn: (authz, challenge, keyAuthorization) => {
|
||||
return this.challengeCreateFn(authz, challenge, keyAuthorization, dnsProvider)
|
||||
},
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
process.env.DEBUG = '*'
|
||||
/**
|
||||
* acme-client
|
||||
*/
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import logger from './utils/util.log.js'
|
||||
import { AcmeService } from './acme.js'
|
||||
import { FileStore } from './store/file-store.js'
|
||||
import { DnsProviderFactory } from './dns-provider/dns-provider-factory.js'
|
||||
import dayjs from 'dayjs'
|
||||
import path from 'path'
|
||||
import _ from 'lodash'
|
||||
import fs from 'fs'
|
||||
import util from './utils/util.js'
|
||||
import forge from 'node-forge'
|
||||
process.env.DEBUG = '*'
|
||||
export class Certd {
|
||||
constructor (options) {
|
||||
this.store = new FileStore()
|
||||
|
@ -15,73 +14,150 @@ export class Certd {
|
|||
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) {
|
||||
let domainStr = _.join(domains)
|
||||
domainStr = domainStr.replace(/\*/g, '')
|
||||
const dir = path.join(email, '/certs/', domainStr)
|
||||
return dir
|
||||
const domainFileName = this.buildDomainFileName(domains)
|
||||
return path.join(email, '/certs/', domainFileName)
|
||||
}
|
||||
|
||||
async certApply (options) {
|
||||
if (options == null) {
|
||||
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 providerOptions = accessProviders[certOptions.challenge.dnsProvider]
|
||||
const providerOptions = accessProviders[options.cert.challenge.dnsProvider]
|
||||
const dnsProvider = await DnsProviderFactory.createByType(providerOptions.providerType, providerOptions)
|
||||
const cert = await this.acme.order({
|
||||
email: certOptions.email,
|
||||
domains: certOptions.domains,
|
||||
email: options.cert.email,
|
||||
domains: options.cert.domains,
|
||||
dnsProvider: dnsProvider,
|
||||
csrInfo: certOptions.csrInfo
|
||||
|
||||
csrInfo: options.cert.csrInfo
|
||||
})
|
||||
|
||||
this.writeCert(certOptions.email, certOptions.domains, cert)
|
||||
const { detail, expires } = this.getDetailFromCrt(cert.crt)
|
||||
const certDir = this.writeCert(options.cert.email, options.cert.domains, cert)
|
||||
const { detail, expires } = this.getCrtDetail(cert.crt)
|
||||
return {
|
||||
...cert,
|
||||
detail,
|
||||
expires
|
||||
expires,
|
||||
certDir,
|
||||
isNew: true
|
||||
}
|
||||
}
|
||||
|
||||
writeCert (email, domains, cert) {
|
||||
const certFilesRootDir = this.buildCertDir(email, domains)
|
||||
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)
|
||||
this.store.set(path.join(dirPath, '/cert.csr'), cert.csr)
|
||||
|
||||
const domainFileName = this.buildDomainFileName(domains)
|
||||
|
||||
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 lastPath = path.join(util.getUserBasePath(), dirPath)
|
||||
// if (!fs.existsSync(linkPath)) {
|
||||
// fs.mkdirSync(linkPath)
|
||||
// }
|
||||
fs.symlinkSync(lastPath, linkPath)
|
||||
if (fs.existsSync(linkPath)) {
|
||||
try {
|
||||
fs.unlinkSync(linkPath)
|
||||
} catch (e) {
|
||||
logger.error('unlink error:', e)
|
||||
}
|
||||
}
|
||||
fs.symlinkSync(lastPath, linkPath, 'dir')
|
||||
|
||||
return linkPath
|
||||
}
|
||||
|
||||
readCurrentCert (email, domains) {
|
||||
const certFilesRootDir = this.buildCertDir(email, domains)
|
||||
const currentPath = path.join(certFilesRootDir, 'current')
|
||||
const domainFileName = this.buildDomainFileName(domains)
|
||||
|
||||
const crt = this.store.get(currentPath + '/cert.crt')
|
||||
const key = this.store.get(currentPath + '/cert.key')
|
||||
const csr = this.store.get(currentPath + '/cert.csr')
|
||||
|
||||
const { detail, expires } = this.getDetailFromCrt(crt)
|
||||
|
||||
const cert = {
|
||||
crt, key, csr, detail, expires
|
||||
const crt = this.store.get(currentPath + `/${domainFileName}.crt`)
|
||||
if (crt == null) {
|
||||
return null
|
||||
}
|
||||
const key = this.store.get(currentPath + `/${domainFileName}.key`)
|
||||
const csr = this.store.get(currentPath + `/${domainFileName}.csr`)
|
||||
|
||||
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 detail = pki.certificateFromPem(crt.toString())
|
||||
const expires = detail.validity.notAfter
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import _ from 'lodash'
|
||||
import optionsPrivate from '../../../test/options.private.js'
|
||||
import optionsPrivate from '../../../test/options.private.mjs'
|
||||
const defaultOptions = {
|
||||
accessProviders: {
|
||||
aliyun: {
|
||||
|
|
|
@ -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}】执行完成-------`)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
})
|
||||
})
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "@certd/samples",
|
||||
"name": "@certd/executor",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "./src/index.js",
|
|
@ -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}】执行完成`)
|
||||
}
|
||||
}
|
|
@ -5,3 +5,12 @@ log4js.configure({
|
|||
})
|
||||
const logger = log4js.getLogger('certd')
|
||||
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')
|
||||
// }
|
|
@ -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
|
||||
})
|
||||
})
|
|
@ -1,6 +1,11 @@
|
|||
import _ from 'lodash'
|
||||
import optionsPrivate from '../../../test/options.private.js'
|
||||
import optionsPrivate from '../../../test/options.private.mjs'
|
||||
const defaultOptions = {
|
||||
args: {
|
||||
forceCert: false, // 强制更新证书
|
||||
skipCert: false, // 是否跳过证书申请环节
|
||||
forceDeploy: false
|
||||
},
|
||||
accessProviders: {
|
||||
aliyun: {
|
||||
providerType: 'aliyun',
|
||||
|
@ -17,7 +22,7 @@ const defaultOptions = {
|
|||
}
|
||||
},
|
||||
cert: {
|
||||
domains: ['*.docmirror.club', 'docmirror.xyz'],
|
||||
domains: ['*.docmirror.cn'],
|
||||
email: 'xiaojunnuo@qq.com',
|
||||
challenge: {
|
||||
challengeType: 'dns',
|
||||
|
@ -34,27 +39,31 @@ const defaultOptions = {
|
|||
},
|
||||
deploy: [
|
||||
{
|
||||
deployName: '流程1-部署到阿里云系列产品',
|
||||
name: '流程1-部署到阿里云系列产品',
|
||||
tasks: [
|
||||
{
|
||||
name: '上传证书到云',
|
||||
type: 'uploadCertToAliyun',
|
||||
certStore: 'aliyun'
|
||||
accessProvider: 'aliyun'
|
||||
},
|
||||
{ // CDN、SCDN、DCDN和负载均衡(SLB)
|
||||
name: '部署证书到SLB',
|
||||
type: 'deployCertToAliyunSLB',
|
||||
certStore: 'aliyun'
|
||||
},
|
||||
{
|
||||
name: '部署证书到阿里云集群Ingress',
|
||||
type: 'deployCertToAliyunK8sIngress',
|
||||
certStore: 'aliyun'
|
||||
name: '部署证书到CDN',
|
||||
type: 'deployCertToAliyunCDN',
|
||||
domainName: 'certd-cdn-upload.docmirror.cn',
|
||||
certName: 'certd部署测试(upload)',
|
||||
certType: 'upload',
|
||||
accessProvider: 'aliyun'
|
||||
}
|
||||
// {
|
||||
// name: '部署证书到阿里云集群Ingress',
|
||||
// type: 'deployCertToAliyunK8sIngress',
|
||||
// accessProvider: 'aliyun'
|
||||
// }
|
||||
]
|
||||
},
|
||||
{
|
||||
deployName: '流程2-部署到nginx服务器',
|
||||
name: '流程2-部署到nginx服务器',
|
||||
disabled: true,
|
||||
tasks: [
|
||||
{
|
||||
name: '上传证书到服务器,并重启nginx',
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 5a3f7446222fa123b645e51e954ccfd981f459bd
|
|
@ -1,5 +1,10 @@
|
|||
import fs from 'fs'
|
||||
import logger from '../utils/util.log.js'
|
||||
export class AbstractPlugin {
|
||||
constructor () {
|
||||
this.logger = logger
|
||||
}
|
||||
|
||||
async executeFromContextFile (options = {}) {
|
||||
const { contextPath } = options
|
||||
const contextJson = fs.readFileSync(contextPath)
|
||||
|
@ -13,5 +18,10 @@ export class AbstractPlugin {
|
|||
return context
|
||||
}
|
||||
|
||||
|
||||
getAccessProvider (accessProvider, accessProviders) {
|
||||
if (typeof accessProvider === 'string' && accessProviders) {
|
||||
accessProvider = accessProviders[accessProvider]
|
||||
}
|
||||
return accessProvider
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,13 @@
|
|||
import { AbstractPlugin } from '../abstract-plugin.js'
|
||||
import { AbstractPlugin } from '../abstract-plugin/index.js'
|
||||
|
||||
export class AbstractAliyunPlugin extends AbstractPlugin {
|
||||
format (pem) {
|
||||
pem = pem.replace(/\r/g, '')
|
||||
pem = pem.replace(/\n\n/g, '')
|
||||
pem = pem.replace(/\n\n/g, '\n')
|
||||
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)
|
||||
|
|
|
@ -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 dayjs from 'dayjs'
|
||||
export class UploadCertToAliyunPlugin extends AbstractPlugin {
|
||||
export class DeployCertToAliyunCDN extends AbstractAliyunPlugin {
|
||||
/**
|
||||
* 插件定义
|
||||
* 名称
|
||||
|
@ -10,7 +10,7 @@ export class UploadCertToAliyunPlugin extends AbstractPlugin {
|
|||
*/
|
||||
static define () {
|
||||
return {
|
||||
name: 'deployToCdn',
|
||||
name: 'deployCertToAliyunCDN',
|
||||
label: '部署到阿里云CDN',
|
||||
input: {
|
||||
domainName: {
|
||||
|
@ -21,10 +21,11 @@ export class UploadCertToAliyunPlugin extends AbstractPlugin {
|
|||
label: '证书名称'
|
||||
},
|
||||
certType: {
|
||||
value: 'upload',
|
||||
label: '证书来源',
|
||||
options: [
|
||||
{ value: 'upload', label: '直接上传' },
|
||||
{ value: 'cas', label: '从证书库(需要uploadCertToAliyun插件作为前置任务)' }
|
||||
{ value: 'cas', label: '从证书库', desc: '需要uploadCertToAliyun作为前置任务' }
|
||||
],
|
||||
required: true
|
||||
},
|
||||
|
@ -76,12 +77,8 @@ export class UploadCertToAliyunPlugin extends AbstractPlugin {
|
|||
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())
|
||||
ServerCertificate: super.format(cert.crt.toString()),
|
||||
PrivateKey: super.format(cert.key.toString())
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
@ -92,6 +89,6 @@ export class UploadCertToAliyunPlugin extends AbstractPlugin {
|
|||
}
|
||||
const ret = await client.request('SetDomainServerCertificate', params, requestOption)
|
||||
this.checkRet(ret)
|
||||
console.log('设置cdn证书成功', ret)
|
||||
this.logger.info('设置cdn证书成功:', ret.RequestId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
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 {
|
||||
export class UploadCertToAliyun extends AbstractAliyunPlugin {
|
||||
/**
|
||||
* 插件定义
|
||||
* 名称
|
||||
|
@ -11,7 +10,7 @@ export class UploadToAliyunPlugin extends AbstractAliyunPlugin {
|
|||
*/
|
||||
static define () {
|
||||
return {
|
||||
name: 'updateToAliyun',
|
||||
name: 'uploadCertToAliyun',
|
||||
label: '上传证书到阿里云',
|
||||
input: {
|
||||
name: {
|
||||
|
@ -42,8 +41,8 @@ export class UploadToAliyunPlugin extends AbstractAliyunPlugin {
|
|||
})
|
||||
}
|
||||
|
||||
async execute ({ accessProviders, cert, args, context }) {
|
||||
const { name, provider } = args
|
||||
async execute ({ accessProviders, cert, args, context, logger }) {
|
||||
const { name, accessProvider } = args
|
||||
const certName = name + '-' + dayjs().format('YYYYMMDDHHmmss')
|
||||
const params = {
|
||||
RegionId: 'cn-hangzhou',
|
||||
|
@ -56,10 +55,11 @@ export class UploadToAliyunPlugin extends AbstractAliyunPlugin {
|
|||
method: 'POST'
|
||||
}
|
||||
|
||||
const accesseProvider = this.getAccessProvider(provider, accessProviders)
|
||||
const client = this.getClient(accesseProvider)
|
||||
const provider = super.getAccessProvider(accessProvider, accessProviders)
|
||||
const client = this.getClient(provider)
|
||||
const ret = await client.request('CreateUserCertificate', params, requestOption)
|
||||
this.checkRet(ret)
|
||||
this.logger.info('证书上传成功:certId=', ret.CertId)
|
||||
context.aliyunCertId = ret.CertId
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
UploadCertToAliyun
|
||||
UploadCertToAliyun, DeployCertToAliyunCDN
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
|
@ -1,19 +1,19 @@
|
|||
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 { Certd } from '@certd/certd'
|
||||
const { expect } = pkg
|
||||
describe('PluginUploadToAliyun', function () {
|
||||
it('#execute', async function () {
|
||||
const plugin = new UploadToAliyunPlugin()
|
||||
const plugin = new UploadCertToAliyun()
|
||||
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 = {}
|
||||
await plugin.execute({
|
||||
accessProviders: options.accessProviders,
|
||||
cert,
|
||||
args: { name: '上传证书到阿里云测试', provider: 'aliyun' },
|
||||
context
|
||||
context,
|
||||
args: { name: 'certd部署测试', provider: 'aliyun' }
|
||||
})
|
||||
|
||||
console.log('context:', context)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import _ from 'lodash'
|
||||
import optionsPrivate from '../../../test/options.private.mjs'
|
||||
const defaultOptions = {
|
||||
version: '1.0.0',
|
||||
accessProviders: {
|
||||
aliyun: {
|
||||
providerType: 'aliyun',
|
||||
|
@ -31,55 +32,7 @@ const defaultOptions = {
|
|||
organizationUnit: 'IT Department',
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue