Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.dromara.jpom.build.DockerYmlDsl Maven / Gradle / Ivy
/*
* Copyright (c) 2019 Of Him Code Technology Studio
* Jpom is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.jpom.build;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.*;
import cn.hutool.setting.yaml.YamlUtil;
import cn.keepbx.jpom.model.BaseJsonModel;
import cn.keepbx.jpom.plugins.IPlugin;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.dromara.jpom.IDockerConfigPlugin;
import org.dromara.jpom.common.i18n.I18nMessageUtil;
import org.dromara.jpom.func.assets.server.MachineDockerServer;
import org.dromara.jpom.model.docker.DockerInfoModel;
import org.dromara.jpom.plugin.PluginFactory;
import org.dromara.jpom.service.docker.DockerInfoService;
import org.dromara.jpom.util.StringUtil;
import org.springframework.util.Assert;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* docker 构建 配置
*
* https://www.jianshu.com/p/54cfa5721d5f
*
* @author bwcx_jzy
* @since 2022/1/25
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Slf4j
public class DockerYmlDsl extends BaseJsonModel {
/**
* 基础镜像
*/
private String runsOn;
/**
* 使用对应到 docker tag 构建
*/
private String fromTag;
/**
* 构建步骤
*/
private List> steps;
/**
* 将本地文件复制到 容器
*
* ::true
*
* * If this flag is set to true, all children of the local directory will be copied to the remote without the root directory. For ex: if
* * I have root/titi and root/tata and the remote path is /var/data. dirChildrenOnly = true will create /var/data/titi and /var/data/tata
* * dirChildrenOnly = false will create /var/data/root/titi and /var/data/root/tata
* *
* * @param dirChildrenOnly
* * if root directory is ignored
*/
private List copy;
/**
* bind mounts 将宿主机上的任意位置的文件或者目录挂在到容器 (--mount type=bind,src=源目录,dst=目标目录)
* /host:/container:ro
*/
private List binds;
/**
* 环境变量
*/
private Map env;
/**
* https://docs.docker.com/engine/api/v1.43/#tag/Container/operation/ContainerCreate
*
* cpuCount
*
* cpuPercent
*
* memoryReservation
*
* cpusetCpus 允许执行的CPU(例如,0-3, 0,1)。
*
* cpuShares
*/
private Map hostConfig;
/**
* 验证信息是否正确
*
* @param dockerInfoService 容器server
* @param machineDockerServer 机器server
* @param workspaceId 工作空间id
* @param plugin 插件
*/
public void check(DockerInfoService dockerInfoService,
MachineDockerServer machineDockerServer,
String workspaceId,
IDockerConfigPlugin plugin) {
Assert.hasText(runsOn, I18nMessageUtil.get("i18n.please_fill_in_runs_on.c2ff"));
Validator.validateMatchRegex(StringUtil.GENERAL_STR, runsOn, I18nMessageUtil.get("i18n.invalid_runs_on_image_name.4b96"));
Assert.state(CollUtil.isNotEmpty(steps), I18nMessageUtil.get("i18n.please_fill_in_steps.229d"));
this.stepsCheck(dockerInfoService, machineDockerServer, workspaceId, plugin);
}
/**
* 检查 steps
*/
private void stepsCheck(DockerInfoService dockerInfoService, MachineDockerServer machineDockerServer,
String workspaceId,
IDockerConfigPlugin plugin) {
Set usesSet = new HashSet<>();
boolean containsRun = false;
for (Map step : steps) {
if (!containsRun && step.containsKey("run")) {
containsRun = true;
}
if (step.containsKey("env")) {
Object env1 = step.get("env");
Assert.isInstanceOf(Map.class, env1, I18nMessageUtil.get("i18n.env_must_be_map_type.f8ad"));
}
if (step.containsKey("uses")) {
Object uses1 = step.get("uses");
Assert.isInstanceOf(String.class, uses1, I18nMessageUtil.get("i18n.uses_only_supports_string_type.ac54"));
String uses = (String) step.get("uses");
if ("node".equals(uses)) {
nodePluginCheck(step);
} else if ("java".equals(uses)) {
javaPluginCheck(step);
} else if ("gradle".equals(uses)) {
gradlePluginCheck(step);
} else if ("maven".equals(uses)) {
mavenPluginCheck(step, dockerInfoService, machineDockerServer, workspaceId);
} else if ("cache".equals(uses)) {
cachePluginCheck(step);
} else if ("go".equals(uses)) {
goPluginCheck(step);
} else if ("python3".equals(uses)) {
python3PluginCheck(step);
} else {
// 其他自定义插件
File tmpDir = FileUtil.file(FileUtil.getTmpDir(), "check-users");
File pluginInstallResource = null;
try {
pluginInstallResource = plugin.getResourceToFile("uses/" + uses + "/install.sh", tmpDir);
Assert.notNull(pluginInstallResource, StrUtil.format(I18nMessageUtil.get("i18n.unsupported_plugin_message.2889"), uses));
} finally {
FileUtil.del(pluginInstallResource);
}
}
usesSet.add(uses);
}
}
if (usesSet.contains("maven") && !usesSet.contains("java")) {
throw new IllegalArgumentException(I18nMessageUtil.get("i18n.maven_plugin_depends_on_java.23f8"));
}
if (usesSet.contains("gradle") && !usesSet.contains("java")) {
throw new IllegalArgumentException(I18nMessageUtil.get("i18n.gradle_plugin_depends_on_java.2bb3"));
}
Assert.isTrue(containsRun, I18nMessageUtil.get("i18n.no_run_found_in_steps.a141"));
}
private void cachePluginCheck(Map step) {
Object path = step.get("path");
Assert.notNull(path, I18nMessageUtil.get("i18n.cache_plugin_path_required.2093"));
}
/**
* 检查 maven 插件
*
* @param step 参数
*/
private void mavenPluginCheck(Map step, DockerInfoService dockerInfoService, MachineDockerServer machineDockerServer, String workspaceId) {
Object version1 = step.get("version");
Assert.notNull(version1, I18nMessageUtil.get("i18n.maven_plugin_version_required.71f1"));
String version = String.valueOf(version1);
String link = String.format("https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/%s/binaries/apache-maven-%s-bin.tar.gz", version, version);
HttpRequest request = HttpUtil.createRequest(Method.HEAD, link);
try (HttpResponse httpResponse = request.execute()) {
boolean success = httpResponse.isOk()
|| httpResponse.getStatus() == HttpStatus.HTTP_MOVED_TEMP
|| httpResponse.getStatus() == HttpStatus.HTTP_BAD_METHOD;
if (success) {
return;
}
}
// 判断容器中是否存在
try {
// 根据 tag 查询
List dockerInfoModels =
dockerInfoService
.queryByTag(workspaceId, fromTag);
Map map = machineDockerServer.dockerParameter(dockerInfoModels);
if (map != null) {
map.put("pluginName", "maven");
map.put("version", version);
IPlugin plugin = PluginFactory.getPlugin(DockerInfoService.DOCKER_CHECK_PLUGIN_NAME);
boolean exists = Convert.toBool(plugin.execute("hasDependPlugin", map), false);
if (exists) {
return;
}
}
} catch (Exception e) {
log.warn(I18nMessageUtil.get("i18n.check_docker_dependency_error.60f7"), e.getMessage());
}
// 提示远程版本
Collection pluginVersion = this.listMavenPluginVersion();
throw new IllegalArgumentException(I18nMessageUtil.get("i18n.please_fill_in_correct_maven_version.468c") + CollUtil.join(pluginVersion, StrUtil.COMMA));
}
private Collection listMavenPluginVersion() {
String html = HttpUtil.get("https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/");
//使用正则获取所有可用版本
List versions = ReUtil.findAll("(.*?) ", html, 1);
Set set = versions.stream()
.map(s -> StrUtil.removeSuffix(s, StrUtil.SLASH))
.filter(StrUtil::isNotEmpty)
.collect(Collectors.toSet());
Assert.notEmpty(set, I18nMessageUtil.get("i18n.no_available_maven_versions.dffe"));
return set;
}
/**
* 检查 go 插件
*
* @param step 参数
*/
private void javaPluginCheck(Map step) {
Object version1 = step.get("version");
Assert.notNull(version1, I18nMessageUtil.get("i18n.java_plugin_version_required.de39"));
Integer version = Integer.valueOf(String.valueOf(version1));
List supportedVersions = ListUtil.of(8, 11, 17, 18);
Assert.isTrue(supportedVersions.contains(version), String.format(I18nMessageUtil.get("i18n.supported_java_plugin_versions.bd70"), supportedVersions));
}
/**
* 检查 gradle 插件
*
* @param step 参数
*/
private void gradlePluginCheck(Map step) {
Object version1 = step.get("version");
Assert.notNull(version1, I18nMessageUtil.get("i18n.gradle_plugin_version_required.b983"));
String version = String.valueOf(version1);
String link = String.format("https://downloads.gradle-dn.com/distributions/gradle-%s-bin.zip", version);
HttpUtil.createRequest(Method.HEAD, link).thenFunction(httpResponse -> {
Assert.isTrue(httpResponse.isOk() ||
httpResponse.getStatus() == HttpStatus.HTTP_MOVED_TEMP ||
httpResponse.getStatus() == HttpStatus.HTTP_SEE_OTHER, I18nMessageUtil.get("i18n.please_fill_in_correct_gradle_version.6e19"));
return null;
});
}
/**
* 检查 node 插件
*
* @param step 参数
*/
private void nodePluginCheck(Map step) {
Object version1 = step.get("version");
Assert.notNull(version1, I18nMessageUtil.get("i18n.node_plugin_version_required.2318"));
String version = String.valueOf(version1);
String link = String.format("https://registry.npmmirror.com/-/binary/node/v%s/node-v%s-linux-x64.tar.gz", version, version);
HttpResponse httpResponse = HttpUtil.createRequest(Method.HEAD, link).execute();
Assert.isTrue(httpResponse.isOk() || httpResponse.getStatus() == HttpStatus.HTTP_MOVED_TEMP, I18nMessageUtil.get("i18n.please_fill_in_correct_node_version.8483"));
}
/**
* 检查 go 插件
*
* @param step 参数
*/
private void goPluginCheck(Map step) {
Object version1 = step.get("version");
Assert.notNull(version1, I18nMessageUtil.get("i18n.go_plugin_version_required.ccf6"));
String version = String.valueOf(version1);
String link = String.format("https://studygolang.com/dl/golang/go%s.linux-amd64.tar.gz", version);
HttpUtil.createRequest(Method.HEAD, link).thenFunction(new Function() {
@Override
public Object apply(HttpResponse httpResponse) {
Assert.isTrue(httpResponse.isOk() ||
httpResponse.getStatus() == HttpStatus.HTTP_MOVED_TEMP ||
httpResponse.getStatus() == HttpStatus.HTTP_SEE_OTHER, I18nMessageUtil.get("i18n.please_fill_in_correct_go_version.44ed"));
return null;
}
});
}
/**
* 检查 python3 插件
*
* @param step 参数
*/
private void python3PluginCheck(Map step) {
Object version1 = step.get("version");
Assert.notNull(version1, I18nMessageUtil.get("i18n.python3_plugin_version_required.a0ce"));
String version = String.valueOf(version1);
Assert.state(StrUtil.startWith(version, "3."), () -> {
//
return I18nMessageUtil.get("i18n.please_fill_in_correct_python3_version.abb1");
});
String link = String.format("https://repo.huaweicloud.com/python/%s/Python-%s.tar.xz", version, version);
HttpUtil.createRequest(Method.HEAD, link).thenFunction(new Function() {
@Override
public Object apply(HttpResponse httpResponse) {
Assert.isTrue(httpResponse.isOk() ||
httpResponse.getStatus() == HttpStatus.HTTP_MOVED_TEMP, I18nMessageUtil.get("i18n.please_fill_in_correct_python3_version.abb1"));
return null;
}
});
}
/**
* 构建对象
*
* @param yml yml 内容
* @return DockerYmlDsl
*/
public static DockerYmlDsl build(String yml) {
yml = StrUtil.replace(yml, StrUtil.TAB, StrUtil.SPACE + StrUtil.SPACE);
InputStream inputStream = new ByteArrayInputStream(yml.getBytes());
return YamlUtil.load(inputStream, DockerYmlDsl.class);
}
}