首页
About Me
推荐
weibo
github
Search
1
linuxea:gitlab-ci之docker镜像质量品质报告
49,451 阅读
2
linuxea:如何复现查看docker run参数命令
23,044 阅读
3
Graylog收集文件日志实例
18,580 阅读
4
linuxea:jenkins+pipeline+gitlab+ansible快速安装配置(1)
18,275 阅读
5
git+jenkins发布和回滚示例
18,181 阅读
ops
Openvpn
Sys Basics
rsync
Mail
NFS
Other
Network
HeartBeat
server 08
Code
Awk
Shell
Python
Golang
virtualization
KVM
Docker
openstack
Xen
kubernetes
kubernetes-cni
Service Mesh
Data
Mariadb
PostgreSQL
MongoDB
Redis
MQ
Ceph
TimescaleDB
kafka
surveillance system
zabbix
ELK Stack/logs
Open-Falcon
Prometheus
victoriaMetrics
Web
apache
Tomcat
Nginx
自动化
Puppet
Ansible
saltstack
Proxy
HAproxy
Lvs
varnish
更多
互联咨询
最后的净土
软件交付
持续集成
gitops
devops
登录
Search
标签搜索
kubernetes
docker
zabbix
Golang
mariadb
持续集成工具
白话容器
elk
linux基础
nginx
dockerfile
Gitlab-ci/cd
最后的净土
基础命令
gitops
jenkins
docker-compose
Istio
haproxy
saltstack
marksugar
累计撰写
690
篇文章
累计收到
139
条评论
首页
栏目
ops
Openvpn
Sys Basics
rsync
Mail
NFS
Other
Network
HeartBeat
server 08
Code
Awk
Shell
Python
Golang
virtualization
KVM
Docker
openstack
Xen
kubernetes
kubernetes-cni
Service Mesh
Data
Mariadb
PostgreSQL
MongoDB
Redis
MQ
Ceph
TimescaleDB
kafka
surveillance system
zabbix
ELK Stack/logs
Open-Falcon
Prometheus
victoriaMetrics
Web
apache
Tomcat
Nginx
自动化
Puppet
Ansible
saltstack
Proxy
HAproxy
Lvs
varnish
更多
互联咨询
最后的净土
软件交付
持续集成
gitops
devops
页面
About Me
推荐
weibo
github
搜索到
12
篇与
的结果
2021-12-20
linuxea:jenkins清理workspace目录插件
清理workspace插件安装workspace cleanupmanage jenkins -> manage plugins -> 在可选插件框内输入workspace cleanup,并选中 --> install without restart而后在流水线语法里面,选择cleanws--> 高级,选择所有,而后生成流水线脚本生成cleanWs()而后将此语句放在post中post { always{ script { ..... cleanWs() } } failure{ script{ ..... cleanWs() } } }而后在java-maven-20211103_cd项目中运行结果中显示[Pipeline] cleanWs [WS-CLEANUP] Deleting project workspace... [WS-CLEANUP] Deferred wipeout is used... [WS-CLEANUP] done [Pipeline] }对应的目录也会清空[root@linuxea-172_16_100_48 ~]# ls /data/jenkins-latest/jenkins_home/workspace/11月/java-maven-20211103-cd [root@linuxea-172_16_100_48 ~]#
2021年12月20日
2,434 阅读
0 评论
0 点赞
2019-03-16
linuxea:单机运行多个jenkins[docker]
在很长一段时间里面,jenkins都没有升级,这些部署的工具事实上也不需要频繁的升级。如果一旦要升级,可能面临的不单单是主版本升级,而是插件升级。插件升级就意味着插件的配置很大可能会丢失。上次jenkins安装时间在一年半之前,想原封不动的平滑升级难度太大。为了解决这个问题,并且顺利的升级。我采用在一台机部署两个jenkins,而后将旧的配置迁移到新的jenkins之上,这样做的好处在于,我可以在升级的过程中继续使用jenkins,将空闲的项目一个个迁移到新版本的jenkins。阅读本章节,你将了解如何单机部署两个jenkins(并非主从),通过人为选择迁移,从而完成单机版jenkins升级。配置端口如果要在一台机器运行两个jenkins,那么端口必然要不同,为了简化操作,我使用官网提供的docker镜像包进行二次封装。其中,我们仅仅需要修改启动脚本jenkins.sh,在其中添加一条端口配置。--httpPort=58080添加的位置在exec中,如下exec java -Duser.home="$JENKINS_HOME" "${java_opts_array[@]}" -jar ${JENKINS_WAR} --httpPort=58080 "${jenkins_opts_array[@]}" "$@"最终的脚本如下:#! /bin/bash -e : "${JENKINS_WAR:="/usr/share/jenkins/jenkins.war"}" : "${JENKINS_HOME:="/var/jenkins_home"}" touch "${COPY_REFERENCE_FILE_LOG}" || { echo "Can not write to ${COPY_REFERENCE_FILE_LOG}. Wrong volume permissions?"; exit 1; } echo "--- Copying files at $(date)" >> "$COPY_REFERENCE_FILE_LOG" find /usr/share/jenkins/ref/ \( -type f -o -type l \) -exec bash -c '. /usr/local/bin/jenkins-support; for arg; do copy_reference_file "$arg"; done' _ {} + # if `docker run` first argument start with `--` the user is passing jenkins launcher arguments if [[ $# -lt 1 ]] || [[ "$1" == "--"* ]]; then # read JAVA_OPTS and JENKINS_OPTS into arrays to avoid need for eval (and associated vulnerabilities) java_opts_array=() while IFS= read -r -d '' item; do java_opts_array+=( "$item" ) done < <([[ $JAVA_OPTS ]] && xargs printf '%s\0' <<<"$JAVA_OPTS") if [[ "$DEBUG" ]] ; then java_opts_array+=( \ '-Xdebug' \ '-Xrunjdwp:server=y,transport=dt_socket,address=5005,suspend=y' \ ) fi jenkins_opts_array=( ) while IFS= read -r -d '' item; do jenkins_opts_array+=( "$item" ) done < <([[ $JENKINS_OPTS ]] && xargs printf '%s\0' <<<"$JENKINS_OPTS") exec java -Duser.home="$JENKINS_HOME" "${java_opts_array[@]}" -jar ${JENKINS_WAR} --httpPort=58080 "${jenkins_opts_array[@]}" "$@" fi # As argument is not jenkins, assume user want to run his own process, for example a `bash` shell to explore this image exec "$@"PS:我要运行更多的jenkins怎么办?那么如果你要在机器上运行2个以上jenkins,那你至少要配置不同的端口。我这里演示的是已经在镜像内固定了端口号,你也可以做变量传递,仍然通过这个脚本。HHTTPORT=${HHTTPORT:-58080}而后在这里调用exec java -Duser.home="$JENKINS_HOME" "${java_opts_array[@]}" -jar ${JENKINS_WAR} --httpPort="${HHTTPORT}" "${jenkins_opts_array[@]}" "$@"而后启动的时候传入HHTTPORT就可以设置端口了,如果不传递变量值,默认就使用58080安装软件包我使用的是dockerhub上jenkins以alpine为基础的镜像,在后续的使用中,我会使用到ansible,在这次构建也会安装。如果你有其他的安装,你也需要在此安装。FROM jenkins/jenkins:2.164.1-alpine USER root COPY jenkins.sh /usr/local/bin/jenkins.sh RUN apk update && apk add ansible bash sshpass\ && chown jenkins.jenkins /usr/local/bin/jenkins.sh \ && chmod +x /usr/local/bin/jenkins.sh USER jenkins这里的USER是要切换用户,这里的用户为jenkins,UID是1000,在此后的文件挂载中,jenkins如果要写入到挂载的目录,就需要授权1000目录属主权限。编译完成后,push到dockerhub,名称是:marksugar/jenkins:2.164.1-alpine-ansible-58080docker-compose如下:version: '2' services: jenkins: image: marksugar/jenkins:2.164.1-alpine-ansible-58080 container_name: jenkins restart: always network_mode: host volumes: - /data/2019_docker_jenkins/jenkins_home:/var/jenkins_home - /etc/ansible:/etc/ansible ports: - 58080:58080这里需要挂在本地目录到容器内的目录,这样一来,容器内安装的插件和配置在docker容器删除后,是不会丢失的。但在开始之前,你要授权这个目录为1000chown -R 1000:1000 2019_docker_jenkins/在启动之后,可以使用docker logs jenkins看到日志的中的密码文件INFO: ************************************************************* ************************************************************* ************************************************************* Jenkins initial setup is required. An admin user has been created and a password generated. Please use the following password to proceed to installation: 18a1a76e02654bb5afaa4f17b44fafa5 This may also be found at: /var/jenkins_home/secrets/initialAdminPassword ************************************************************* ************************************************************* ************************************************************* 而后通过58080端口访问即可查看更多如果你要查看更多关于jenkins和gitlab-ci/cd的文章,请关注一下分类。gitlab-ci/cdjenkins持续集成
2019年03月16日
3,748 阅读
0 评论
0 点赞
2019-03-15
linuxea:jenkins环境变量的几种配置方式
作为领先的开源自动化服务应用,Jenkins提供数百个插件来支持构建,部署和自动化任何项目。Jenkins使用Environment变量公开组件的一些常用的参数指标等。阅读此章,你将了解关于环境变量的所有信息。1,创建全局环境变量2,访问全局环境变量。3,在构建期间创建本地环境变量。1.创建全局环境变量1.1使用Jenkins控制台我们可以轻松创建全局环境变量。Manage Jenkins-->Configure System-->Global properties-->Environment variables,我们勾选Environment variables,配置一个名称为MarkDown_LinuxEA,值为linuxea.com的变量。如下:2. 访问全局环境变量2.1在詹金斯管道中要在管道中使用上面定义的变量,我们可以使用env.Key让我们访问jenkins全局变量。pipeline { agent any stages { stage('pull'){ steps { echo env.MarkDown_LinuxEA } } } }build在jenkins管道中,有很多其他有用的环境变量可以在构建执行期间访问。最有用的环境变量。ENV:环境变量是从Groovy代码的访问env.VARNAME,或简称为VARNAME。也可以写入此类属性(仅使用env.前缀);currentBuild:该currentBuild变量可用于引用当前运行的构建。params:将构建中定义的所有参数公开为具有各种类型值的只读映射。docker :该docker变量提供了从Pipeline脚本方便地访问与Docker相关的函数。2.2 在Shell/Batch脚本中在Shell脚本中,我们可以使用$ Key或$ {Key}来使用环境变量。同样在批处理中,我们可以使用%Key%来访问环境变量2.3在FreeStyle项目中在freestyle项目中,我们可以使用EnvEnject插件。搜索安装或者下载安装即可而后我配置一个一样的变量,并且打印观察日志3.在构建期间创建本地环境变量。3.1使用Declarative管道pipeline { agent any environment { MarkDown_LinuxEA = 'www.linuxea.com' } stages { stage('pull'){ steps { echo env.MarkDown_LinuxEA } } } }build这样一来,如果是相同的变量名称,而在pipeline中的将会生效。全局的变量失效。查看更多如果你要查看更多关于jenkins和gitlab-ci/cd的文章,请关注一下分类。gitlab-ci/cdjenkins持续集成
2019年03月15日
6,515 阅读
0 评论
0 点赞
2018-12-05
linuxea: jenkins基于用户角色的权限控制
jenkins基于用户角色的权限控制需要Role-based Authorization Strategy插件的控制,其中需要借助jenkins用户对角色授权,而在角色下添加项目的所属角色,而这个角色通过正则匹配绑定项目,那这也就是说你的项目要想做控制,必须有统一的一个标签来进行区分。那如果你事先没有准备,也没有关系,Rename即可。这些绑定后,在到全局roles中勾选对应的:角色和项目,相互对应即可完成权限控制。如果你可以打开官网,推荐阅读:https://wiki.jenkins.io/display/JENKINS/Role+Strategy+Plugin先决条件: jenkins version: 2.138.2 插件:Role-based Authorization Strategy安装插件登录jenkins,在左侧选择Manage Jenkins(管理詹金斯)找到管理插件(Manage Plugins)而后点击"Installed",在右侧"Filter"中输入"role",可以看到我这里已经安装了一个Role-based Authorization Strategy ,如下图而后仍然在Manage Jenkins页面中选择Configure Global Security,打开Role-Based Strategy创建用户在Manage Jenkins中选择Manage Users,点击Create User,创建用户Manage and Assign Roles这时候Manage Jenkins页面就多了一个Manage and Assign Roles,进入选择Manage RolesManage Roles而后在Global roles 中的role to add中add一个输入角色role。这个角色的权限只有read这里的图片太宽,请右击“在新的标签页打开”打开图片而后在add一个Project roles这里的Project roles和Global roles是相互对应,Global roles是全局角色,而Project roles是项目角色,为了便于区分,我建议将全局role或者项目role以及用户名设置成一样。而Pattern是一个正则匹配。以项目开头来区分分配。如: linuxea. 也就是说以linuxea开头的都划入linuxea角色中,linuxea项目具备linuxea角色的授权权限.如果你是linuxea-api-euk这样的项目,那么就应该写成linuxea-api-.之类的如下图而后保存。Assign Roles回到Manage Jenkins中一个Manage and Assign Roles页面,选择Assign Roles进入在Global roles和Item roles add 用户和item rolesitem roles关联了Project roles中的Pattern中的正则匹配和被正则匹配中的项目的项目权限。由此你可以控制你的项目权限
2018年12月05日
3,490 阅读
0 评论
0 点赞
2018-10-19
linuxea:jenkins pipeline阶段终止
在管道中,声明的阶段里面是连续的,其中,可以配置的方式很多。且不管每个阶段运行的状态如何,都可以为配置成想要的一些操作,或者行为。如:发邮件,多条件判断等。如果按照正常的运行,不做阶段处理,默认会依次执行多个阶段。这并不是我们想要的,因此,在每个阶段如果执行失败就终止,而不执行后面的是有必要的。当然,这里是有区分的,脚本管道可以使用try,而post作用范围更大使用try 和currentBuild.result = 'FAILURE'即可假如此阶段不通就停止了。相反的,假如此刻需求是,无论那个阶段执行失败都不应该终止,就默认即可。但是,需求如果变成,当执行失败后的还需要动作,参考external-workspace-manager
2018年10月19日
6,886 阅读
0 评论
0 点赞
2018-10-19
linuxea:解决jenkins忘记密码
进入jenkins机器中,在/var/lib/jenkins/users/USER/config.xml中修改我的用户是linuxea则是/var/lib/jenkins/users/linuxea/config.xml <hudson.security.HudsonPrivateSecurityRealm_-Details> <passwordHash>#jbcrypt:$2a$10$122PJF3EWR1af1Fj6ybZX.ZuvDwLaeHeBpn0vbYeQ9jMwQB/ptj8q</passwordHash> </hudson.security.HudsonPrivateSecurityRealm_-Details>主要修改password字段,修改字段需要注意缩进。否则会失败,这段的密码明文是linuxea.com<passwordHash>#jbcrypt:$2a$10$122PJF3EWR1af1Fj6ybZX.ZuvDwLaeHeBpn0vbYeQ9jMwQB/ptj8q</passwordHash>你也可以单单复制#jbcrypt:$2a$10$122PJF3EWR1af1Fj6ybZX.ZuvDwLaeHeBpn0vbYeQ9jMwQB/ptj8q粘贴,而后systemctl restart jenkins,再次登陆即可
2018年10月19日
3,095 阅读
0 评论
0 点赞
2017-10-08
linuxea:jenkins pipeline邮件提醒的两种方式(5)
可以通过Mailer Plugin和Email-ext plugin插件发送邮件在pipeline中可以在执行完成进行,通过直接的结果发送失败或者成功,也可以在执行阶段过程中,如果在那个阶段执行失败发送,想看第一中,只发送失败的详细结果:异常处理参考:https://jenkins.io/doc/pipeline/steps/workflow-basic-steps/ https://jenkins.io/doc/book/pipeline/syntax/Mailer Plugin邮件设置安装Mailer Plugin插件1,jenkins邮箱配置系统管理-->系统设置使用的gmail邮箱,添加置如下注意 这里需要开启允许不够安全的应用如下:2,gmail配置3,post字段加入如下字段post { always { step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: "myname@gmail.com", sendToIndividuals: true]) } }4,pipeline文件测试pipeline { agent any environment { def ITEMNAME = "webapp" def DESTPATH = "/data/wwwroot" def SRCPATH = "~/workspace/test" def BUILD_USER = "mark" } stages { stage('代码拉取'){ steps { echo "checkout from ${ITEMNAME}" git url: 'git@git.ds.com:mark/maxtest.git', branch: 'master' //git credentialsId:CRED_ID, url:params.repoUrl, branch:params.repoBranch } } stage('服务检查') { steps { echo "检查nginx进程是否存在" script{ def resultUpdateshell = sh script: 'ansible webapp -m shell -a "ps aux|grep nginx|grep -v grep"' currentBuild.result = 'FAILED' if (resultUpdateshell == 0) { skip = '0' return } } } } } post { always { step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: "myname@gmail.com", sendToIndividuals: true]) } } }这个简单的pipeline中我将nginx关闭,执行就会发送邮件这样的 邮件需要观察是否哪里出现问题阶段邮件1,方式一也可以在每个阶段进行添加,像这样 stage('服务检查') { steps { echo "检查nginx进程是否存在" script{ try { sh script: 'ansible webapp -m shell -a "ps aux|grep nginx|grep -v grep"' currentBuild.result = 'SUCCESS' } catch (any) { currentBuild.result = 'FAILURE' throw any } finally { println currentBuild.result step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: "myname@gmail.com", sendToIndividuals: true]) } } } }2,方式2或者这样 stage('服务检查') { steps { echo "检查nginx进程是否存在" script{ catchError { sh script: 'ansible webapp -m shell -a "ps aux|grep nginx|grep -v grep"' } step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: "myname@gmail.com", sendToIndividuals: true]) } } }3,预览完整的预览就是这样pipeline { agent any environment { def ITEMNAME = "webapp" def DESTPATH = "/data/wwwroot" def SRCPATH = "~/workspace/test" def BUILD_USER = "mark" } stages { stage('代码拉取'){ steps { echo "checkout from ${ITEMNAME}" git url: 'git@git.ds.com:mark/maxtest.git', branch: 'master' //git credentialsId:CRED_ID, url:params.repoUrl, branch:params.repoBranch } } stage('服务检查') { steps { echo "检查nginx进程是否存在" script{ catchError { sh script: 'ansible webapp -m shell -a "ps aux|grep nginx|grep -v grep"' } step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: "myname@gmail.com", sendToIndividuals: true]) } } } } }但是这样还是会有问题,如果构建正常,不会发送邮件,只有构建出错才会发送,我们希望每个阶段如果构建错误这发送错误的邮件,如果没有错误则发送构建完成这样的字样,那可以这样Email-ext plugin插件1,插件配置安装完成后打开配置页面,系统管理-->系统设置启用调试模式。Enable Debug Mode,默认的触发器也可以勾选参考:https://wiki.jenkins.io/display/JENKINS/Email-ext+plugin#Email-extplugin-TemplateExamples2,配置参考post返回success/failure也会发送邮件,这样你会收到两份邮件,正常和失败的,如下:post { success { emailext ( subject: "'${env.JOB_NAME} [${env.BUILD_NUMBER}]' 更新正常", body: """ 详情: SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' 状态:${env.JOB_NAME} jenkins 更新运行正常 URL :${env.BUILD_URL} 项目名称 :${env.JOB_NAME} 项目更新进度:${env.BUILD_NUMBER} """, to: "myname@gmail.com", recipientProviders: [[$class: 'DevelopersRecipientProvider']] ) } failure { emailext ( subject: "'${env.JOB_NAME} [${env.BUILD_NUMBER}]' 更新失败", body: """ 详情: FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' 状态:${env.JOB_NAME} jenkins 运行失败 URL :${env.BUILD_URL} 项目名称 :${env.JOB_NAME} 项目更新进度:${env.BUILD_NUMBER} """, to: "myname@gmail.com", recipientProviders: [[$class: 'DevelopersRecipientProvider']] ) } } }3,混合模式(Mailer Email-ext)Mailer 会将pipeline执行详细内容发送到邮件,成功这只发送编辑的信息pipeline { agent any environment { def ITEMNAME = "webapp" def DESTPATH = "/data/wwwroot" def SRCPATH = "~/workspace/test" def BUILD_USER = "mark" } stages { stage('代码拉取'){ steps { echo "checkout from ${ITEMNAME}" git url: 'git@git.ds.com:mark/maxtest.git', branch: 'master' //git credentialsId:CRED_ID, url:params.repoUrl, branch:params.repoBranch } } stage('服务检查') { steps { echo "检查nginx进程是否存在" script{ try { sh script: 'ansible webapp -m shell -a "ps aux|grep nginx|grep -v grep"' } catch (exc) { currentBuild.result = "FAILURE" println currentBuild.result step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: "myname@gmail.com", sendToIndividuals: true]) } } } } } post { success { emailext ( subject: "'${env.JOB_NAME} [${env.BUILD_NUMBER}]' 更新正常", body: """ 详情: SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' 状态:${env.JOB_NAME} jenkins 更新运行正常 URL :${env.BUILD_URL} 项目名称 :${env.JOB_NAME} 项目更新进度:${env.BUILD_NUMBER} """, to: "myname@gmail.com", recipientProviders: [[$class: 'DevelopersRecipientProvider']] ) } } }4,阶段邮件发送在前面Mailer 已经可以实现了,但是如果pipeline过长观看不是很理想,可以这样如果希望每个步骤如果失败则发送每一个失败的邮件提醒,如果正常也发送一封邮件提醒。不同的是内容可以自定义pipeline { agent any environment { def ITEMNAME = "webapp" def DESTPATH = "/data/wwwroot" def SRCPATH = "~/workspace/test" def BUILD_USER = "mark" def USERMAIL = "myname@gmail.com" } stages { stage('代码拉取'){ steps { echo "checkout from ${ITEMNAME}" git url: 'git@git.ds.com:mark/maxtest.git', branch: 'master' //git credentialsId:CRED_ID, url:params.repoUrl, branch:params.repoBranch } } stage('服务检查') { steps { echo "检查nginx进程是否存在" script{ try { sh script: 'ansible webapp -m shell -a "ps aux|grep nginx|grep -v grep"' } catch (exc) { currentBuild.result = "FAILURE" emailext ( subject: "'${env.JOB_NAME} [${env.BUILD_NUMBER}]' 更新失败", body: """ 详情: FAILURE: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' 状态:${env.JOB_NAME} jenkins 更新失败 URL :${env.BUILD_URL} 项目名称 :${env.JOB_NAME} 项目更新进度:${env.BUILD_NUMBER} 内容:nginx进程不存在 """, to: "${USERMAIL}", recipientProviders: [[$class: 'DevelopersRecipientProvider']] ) } } } } stage('目录检查') { steps { echo "检查${DESTPATH}目录是否存在" script{ try { sh script: 'ansible webapp -m shell -a "ls -d ${DESTPATH}"' } catch (exc) { currentBuild.result = "FAILURE" emailext ( subject: "'${env.JOB_NAME} [${env.BUILD_NUMBER}]' 更新失败", body: """ 详情: FAILURE: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' 状态:${env.JOB_NAME} jenkins 更新失败 URL :${env.BUILD_URL} 项目名称 :${env.JOB_NAME} 项目更新进度:${env.BUILD_NUMBER} 内容:${DESTPATH}目录不存在 """, to: "${USERMAIL}", recipientProviders: [[$class: 'DevelopersRecipientProvider']] ) } } } } } post { success { emailext ( subject: "'${env.JOB_NAME} [${env.BUILD_NUMBER}]' 更新正常", body: """ 详情: SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' 状态:${env.JOB_NAME} jenkins 更新运行正常 URL :${env.BUILD_URL} 项目名称 :${env.JOB_NAME} 项目更新进度:${env.BUILD_NUMBER} """, to: "${USERMAIL}", recipientProviders: [[$class: 'DevelopersRecipientProvider']] ) } } }
2017年10月08日
14,260 阅读
0 评论
0 点赞
1
2