| @@ -140,7 +140,12 @@ | |||
| <dependency> | |||
| <groupId>com.github.docker-java</groupId> | |||
| <artifactId>docker-java</artifactId> | |||
| <version>3.1.1</version> | |||
| <version>3.2.13</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.github.docker-java</groupId> | |||
| <artifactId>docker-java-transport-httpclient5</artifactId> | |||
| <version>3.2.13</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.baomidou</groupId> | |||
| @@ -216,6 +221,12 @@ | |||
| <version>3.0.8</version> | |||
| <scope>compile</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>commons-lang</groupId> | |||
| <artifactId>commons-lang</artifactId> | |||
| <version>2.6</version> | |||
| <scope>compile</scope> | |||
| </dependency> | |||
| </dependencies> | |||
| @@ -0,0 +1,10 @@ | |||
| package com.ruoyi.platform.constant; | |||
| public class Constant { | |||
| public final static int Image_Type_Pub = 1; // 公共镜像 | |||
| public final static int Image_Type_Pri = 0; // 私有镜像 | |||
| public final static int State_valid = 1; // 有效 | |||
| public final static int State_invalid = 0; // 无效 | |||
| } | |||
| @@ -0,0 +1,61 @@ | |||
| package com.ruoyi.platform.controller.codeConfig; | |||
| import com.ruoyi.common.core.web.controller.BaseController; | |||
| import com.ruoyi.common.core.web.domain.GenericsAjaxResult; | |||
| import com.ruoyi.platform.domain.CodeConfig; | |||
| import com.ruoyi.platform.service.CodeConfigService; | |||
| import io.swagger.annotations.Api; | |||
| import org.springframework.web.bind.annotation.*; | |||
| import org.springframework.data.domain.Page; | |||
| import org.springframework.data.domain.PageRequest; | |||
| import org.springframework.http.ResponseEntity; | |||
| import javax.annotation.Resource; | |||
| @RestController | |||
| @RequestMapping("codeConfig") | |||
| @Api("代码配置") | |||
| public class CodeConfigController extends BaseController { | |||
| @Resource | |||
| private CodeConfigService codeConfigService; | |||
| /** | |||
| * 分页查询 | |||
| * @param codeConfig 筛选条件 | |||
| * @param page 页数 | |||
| * @param size 每页大小 | |||
| * @return 查询结果 | |||
| */ | |||
| @GetMapping | |||
| public GenericsAjaxResult<Page<CodeConfig>> queryByPage(CodeConfig codeConfig, int page, int size) { | |||
| PageRequest pageRequest = PageRequest.of(page,size); | |||
| return genericsSuccess(this.codeConfigService.queryByPage(codeConfig, pageRequest)); | |||
| } | |||
| /** | |||
| * 通过主键查询单条数据 | |||
| * @param id | |||
| * @return 单条数据 | |||
| */ | |||
| @GetMapping("{id}") | |||
| public ResponseEntity<CodeConfig> queryById(@PathVariable("id") Long id) { | |||
| return ResponseEntity.ok(this.codeConfigService.queryById(id)); | |||
| } | |||
| @PostMapping | |||
| public GenericsAjaxResult<CodeConfig> add(@RequestBody CodeConfig codeConfig){ | |||
| return genericsSuccess(this.codeConfigService.insert(codeConfig)); | |||
| } | |||
| @PutMapping | |||
| public GenericsAjaxResult<CodeConfig> update(@RequestBody CodeConfig codeConfig){ | |||
| return genericsSuccess(this.codeConfigService.update(codeConfig)); | |||
| } | |||
| @DeleteMapping("{id}") | |||
| public GenericsAjaxResult<String> delete(@PathVariable("id") Long id){ | |||
| return genericsSuccess(this.codeConfigService.removeById(id)); | |||
| } | |||
| } | |||
| @@ -78,16 +78,15 @@ public class ImageController extends BaseController { | |||
| * @param image 实体 | |||
| * @return 新增结果 | |||
| */ | |||
| @PostMapping | |||
| @ApiOperation("新增镜像,不包含镜像版本") | |||
| public GenericsAjaxResult<Image> add(@RequestBody Image image) { | |||
| return genericsSuccess(this.imageService.insert(image)); | |||
| } | |||
| /** | |||
| * 新增镜像和版本 | |||
| * | |||
| * @param imageVo 实体 | |||
| * 新增镜像和版本 @PostMapping | |||
| * @return 新增结果 | |||
| */ | |||
| @PostMapping("/addImageAndVersion") | |||
| @@ -149,5 +148,11 @@ public class ImageController extends BaseController { | |||
| return genericsSuccess(this.imageService.uploadImageFiles(file)); | |||
| } | |||
| @PostMapping("/saveImage") | |||
| @ApiOperation(value = "保存环境为镜像", notes = "docker commit方式保存,并推送到horbor") | |||
| public GenericsAjaxResult<String> saveImage(@RequestBody ImageVo imageVo){ | |||
| return genericsSuccess(this.imageService.saveImage(imageVo)); | |||
| } | |||
| } | |||
| @@ -0,0 +1,56 @@ | |||
| package com.ruoyi.platform.domain; | |||
| import com.fasterxml.jackson.databind.PropertyNamingStrategy; | |||
| import com.fasterxml.jackson.databind.annotation.JsonNaming; | |||
| import io.swagger.annotations.ApiModelProperty; | |||
| import lombok.Data; | |||
| import java.io.Serializable; | |||
| import java.util.Date; | |||
| @Data | |||
| @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) | |||
| public class CodeConfig implements Serializable { | |||
| private Long id; | |||
| @ApiModelProperty(value = "代码仓库名称") | |||
| private String codeRepoName; | |||
| @ApiModelProperty(value = "代码仓库可见性(1-公开,0-私有)") | |||
| private Integer codeRepoVis; | |||
| @ApiModelProperty(value = "Git地址") | |||
| private String gitUrl; | |||
| @ApiModelProperty(value = "代码分支/Tag") | |||
| private String gitBranch; | |||
| @ApiModelProperty(value = "验证方式(0-用户名密码,1-SSH-Key)") | |||
| private Integer verifyMode; | |||
| @ApiModelProperty(value = "Git用户名") | |||
| private String gitUserName; | |||
| @ApiModelProperty(value = "Git密码") | |||
| private String gitPassword; | |||
| private String createBy; | |||
| /** | |||
| * 创建时间 | |||
| */ | |||
| private Date createTime; | |||
| /** | |||
| * 更新者 | |||
| */ | |||
| private String updateBy; | |||
| /** | |||
| * 更新时间 | |||
| */ | |||
| private Date updateTime; | |||
| /** | |||
| * 状态,0失效1生效 | |||
| */ | |||
| private Integer state; | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| package com.ruoyi.platform.mapper; | |||
| import com.ruoyi.platform.domain.CodeConfig; | |||
| import org.apache.ibatis.annotations.Param; | |||
| import org.springframework.data.domain.Pageable; | |||
| import java.util.List; | |||
| public interface CodeConfigDao { | |||
| long count(@Param("codeConfig") CodeConfig codeConfig); | |||
| List<CodeConfig> queryAllByLimit(@Param("codeConfig") CodeConfig codeConfig, @Param("pageable") Pageable pageable); | |||
| CodeConfig queryById(Long id); | |||
| int insert(@Param("codeConfig") CodeConfig codeConfig); | |||
| int update(@Param("codeConfig") CodeConfig codeConfig); | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| package com.ruoyi.platform.service; | |||
| import com.ruoyi.platform.domain.CodeConfig; | |||
| import org.springframework.data.domain.Page; | |||
| import org.springframework.data.domain.PageRequest; | |||
| public interface CodeConfigService { | |||
| Page<CodeConfig> queryByPage(CodeConfig codeConfig, PageRequest pageRequest); | |||
| CodeConfig queryById(Long id); | |||
| CodeConfig insert(CodeConfig codeConfig); | |||
| CodeConfig update(CodeConfig codeConfig); | |||
| String removeById(Long id); | |||
| } | |||
| @@ -93,6 +93,5 @@ public interface ImageService { | |||
| Map<String, String> createImageFromNet(String imageName, String imageTag, String NetPath) throws Exception; | |||
| Map<String, String> uploadImageFiles(MultipartFile file) throws Exception; | |||
| String saveImage(ImageVo imageVo); | |||
| } | |||
| @@ -0,0 +1,74 @@ | |||
| package com.ruoyi.platform.service.impl; | |||
| import com.ruoyi.common.security.utils.SecurityUtils; | |||
| import com.ruoyi.platform.constant.Constant; | |||
| import com.ruoyi.platform.domain.CodeConfig; | |||
| import com.ruoyi.platform.mapper.CodeConfigDao; | |||
| import com.ruoyi.platform.service.CodeConfigService; | |||
| import com.ruoyi.system.api.model.LoginUser; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.springframework.data.domain.Page; | |||
| import org.springframework.data.domain.PageImpl; | |||
| import org.springframework.data.domain.PageRequest; | |||
| import org.springframework.stereotype.Service; | |||
| import javax.annotation.Resource; | |||
| import java.util.Date; | |||
| import java.util.List; | |||
| @Service("codeConfigService") | |||
| public class CodeConfigServiceImpl implements CodeConfigService { | |||
| @Resource | |||
| private CodeConfigDao codeConfigDao; | |||
| @Override | |||
| public Page<CodeConfig> queryByPage(CodeConfig codeConfig, PageRequest pageRequest) { | |||
| long total = this.codeConfigDao.count(codeConfig); | |||
| List<CodeConfig> codeConfigList = this.codeConfigDao.queryAllByLimit(codeConfig, pageRequest); | |||
| return new PageImpl<>(codeConfigList, pageRequest, total); | |||
| } | |||
| @Override | |||
| public CodeConfig queryById(Long id) { | |||
| return this.codeConfigDao.queryById(id); | |||
| } | |||
| @Override | |||
| public CodeConfig insert(CodeConfig codeConfig) { | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| codeConfig.setCreateBy(loginUser.getUsername()); | |||
| codeConfig.setUpdateBy(loginUser.getUsername()); | |||
| codeConfig.setCreateTime(new Date()); | |||
| codeConfig.setUpdateTime(new Date()); | |||
| this.codeConfigDao.insert(codeConfig); | |||
| return codeConfig; | |||
| } | |||
| @Override | |||
| public CodeConfig update(CodeConfig codeConfig) { | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| codeConfig.setUpdateBy(loginUser.getUsername()); | |||
| codeConfig.setUpdateTime(new Date()); | |||
| this.codeConfigDao.update(codeConfig); | |||
| return this.codeConfigDao.queryById(codeConfig.getId()); | |||
| } | |||
| @Override | |||
| public String removeById(Long id) { | |||
| CodeConfig codeConfig = this.codeConfigDao.queryById(id); | |||
| if (codeConfig == null) { | |||
| return "代码配置不存在"; | |||
| } | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| String username = loginUser.getUsername(); | |||
| String createBy = codeConfig.getCreateBy(); | |||
| if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createBy))) { | |||
| return "无权限删除该代码配置"; | |||
| } | |||
| codeConfig.setState(Constant.State_invalid); | |||
| return this.codeConfigDao.update(codeConfig) > 0 ? "删除成功" : "删除失败"; | |||
| } | |||
| } | |||
| @@ -2,24 +2,29 @@ package com.ruoyi.platform.service.impl; | |||
| import com.alibaba.fastjson2.util.DateUtils; | |||
| import com.ruoyi.common.security.utils.SecurityUtils; | |||
| import com.ruoyi.platform.constant.Constant; | |||
| import com.ruoyi.platform.domain.DevEnvironment; | |||
| import com.ruoyi.platform.domain.Image; | |||
| import com.ruoyi.platform.domain.ImageVersion; | |||
| import com.ruoyi.platform.mapper.DevEnvironmentDao; | |||
| import com.ruoyi.platform.mapper.ImageDao; | |||
| import com.ruoyi.platform.mapper.ImageVersionDao; | |||
| import com.ruoyi.platform.service.ImageService; | |||
| import com.ruoyi.platform.service.ImageVersionService; | |||
| import com.ruoyi.platform.service.MinioService; | |||
| import com.ruoyi.platform.utils.DockerClientUtil; | |||
| import com.ruoyi.platform.utils.FileUtil; | |||
| import com.ruoyi.platform.utils.K8sClientUtil; | |||
| import com.ruoyi.platform.vo.ImageVo; | |||
| import com.ruoyi.system.api.model.LoginUser; | |||
| import io.kubernetes.client.openapi.models.V1PersistentVolumeClaim; | |||
| import io.kubernetes.client.openapi.models.V1Pod; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.springframework.beans.BeanUtils; | |||
| import org.springframework.beans.factory.annotation.Value; | |||
| import org.springframework.data.domain.Page; | |||
| import org.springframework.data.domain.PageImpl; | |||
| import org.springframework.data.domain.PageRequest; | |||
| import org.springframework.scheduling.annotation.Async; | |||
| import org.springframework.stereotype.Service; | |||
| import org.springframework.transaction.annotation.Transactional; | |||
| import org.springframework.web.multipart.MultipartFile; | |||
| @@ -40,16 +45,19 @@ import java.util.concurrent.CompletableFuture; | |||
| public class ImageServiceImpl implements ImageService { | |||
| @Resource | |||
| private ImageDao imageDao; | |||
| @Resource | |||
| private ImageVersionDao imageVersionDao; | |||
| @Resource | |||
| private DevEnvironmentDao devEnvironmentDao; | |||
| @Resource | |||
| private ImageVersionService imageVersionService; | |||
| @Resource | |||
| private ImageVersionDao imageVersionDao; | |||
| @Resource | |||
| private K8sClientUtil k8sClientUtil; | |||
| @Resource | |||
| private DockerClientUtil dockerClientUtil; | |||
| @Resource | |||
| private MinioService minioService; | |||
| @Value("${harbor.bucketName}") | |||
| @@ -75,6 +83,8 @@ public class ImageServiceImpl implements ImageService { | |||
| private String proxyUrl; | |||
| @Value("${minio.pvcName}") | |||
| private String pvcName; | |||
| @Value("${jupyter.namespace}") | |||
| private String namespace; | |||
| /** | |||
| * 通过ID查询单条数据 | |||
| * | |||
| @@ -350,4 +360,57 @@ public class ImageServiceImpl implements ImageService { | |||
| String path = loginUser.getUsername()+"/"+file.getOriginalFilename(); | |||
| return minioService.uploadFile(bucketName, path, file); | |||
| } | |||
| @Override | |||
| @Transactional | |||
| public String saveImage(ImageVo imageVo) { | |||
| if(imageDao.getByName(imageVo.getName()) != null){ | |||
| throw new IllegalStateException("镜像名称已存在"); | |||
| } | |||
| LoginUser loginUser = SecurityUtils.getLoginUser(); | |||
| String username = loginUser.getUsername().toLowerCase(); | |||
| String podName = username +"-editor-pod" + "-" + imageVo.getDevEnvironmentId().toString(); | |||
| try { | |||
| String containerId = k8sClientUtil.getPodContainerId(podName, namespace); | |||
| String hostIp = k8sClientUtil.getHostIp(podName, namespace); | |||
| dockerClientUtil.commitImage(imageVo,containerId,hostIp,username); | |||
| HashMap<String, String> resultMap = dockerClientUtil.pushImageToHorbor(imageVo, hostIp); | |||
| Image image = new Image(); | |||
| BeanUtils.copyProperties(imageVo,image); | |||
| image.setImageType(Constant.Image_Type_Pri); | |||
| image.setCreateBy(username); | |||
| image.setUpdateBy(username); | |||
| image.setUpdateTime(new Date()); | |||
| image.setCreateTime(new Date()); | |||
| image.setState(1); | |||
| imageDao.insert(image); | |||
| ImageVersion imageVersion = new ImageVersion(); | |||
| imageVersion.setImageId(image.getId()); | |||
| imageVersion.setVersion(imageVo.getVersion()); | |||
| imageVersion.setUrl(resultMap.get("imageName")); | |||
| imageVersion.setTagName(imageVo.getTagName()); | |||
| imageVersion.setFileSize(resultMap.get("size")); | |||
| imageVersion.setCreateBy(username); | |||
| imageVersion.setUpdateBy(username); | |||
| imageVersion.setUpdateTime(new Date()); | |||
| imageVersion.setCreateTime(new Date()); | |||
| imageVersion.setState(1); | |||
| imageVersion.setStatus("available"); | |||
| imageVersionDao.insert(imageVersion); | |||
| //更新dev环境的镜像信息 | |||
| DevEnvironment devEnvironment = new DevEnvironment(); | |||
| devEnvironment.setId(imageVo.getDevEnvironmentId()); | |||
| devEnvironment.setImage(resultMap.get("imageName")); | |||
| devEnvironmentDao.update(devEnvironment); | |||
| return "保存镜像成功"; | |||
| } catch (Exception e) { | |||
| throw new RuntimeException("保存镜像失败:" +e); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,109 @@ | |||
| package com.ruoyi.platform.utils; | |||
| import com.github.dockerjava.api.DockerClient; | |||
| import com.github.dockerjava.api.command.CommitCmd; | |||
| import com.github.dockerjava.api.command.InspectImageResponse; | |||
| import com.github.dockerjava.api.model.AuthConfig; | |||
| import com.github.dockerjava.core.DefaultDockerClientConfig; | |||
| import com.github.dockerjava.core.DockerClientConfig; | |||
| import com.github.dockerjava.core.DockerClientImpl; | |||
| import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; | |||
| import com.github.dockerjava.transport.DockerHttpClient; | |||
| import com.ruoyi.platform.vo.ImageVo; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.springframework.beans.factory.annotation.Value; | |||
| import org.springframework.stereotype.Component; | |||
| import java.time.Duration; | |||
| import java.util.HashMap; | |||
| @Slf4j | |||
| @Component | |||
| public class DockerClientUtil { | |||
| @Value("${harbor.bucketName}") | |||
| private String bucketName; | |||
| @Value("${harbor.repository}") | |||
| private String repository; | |||
| @Value("${harbor.harborUrl}") | |||
| private String harborUrl; | |||
| @Value("${harbor.harborUser}") | |||
| private String harborUser; | |||
| @Value("${harbor.harborpassword}") | |||
| private String harborpassword; | |||
| public DockerClient getDockerClient(String dockerServerUrl) { | |||
| //创建DefaultDockerClientConfig(指定docker服务器的配置) | |||
| DockerClientConfig config = DefaultDockerClientConfig | |||
| .createDefaultConfigBuilder() | |||
| .withDockerHost("tcp://" + dockerServerUrl + ":2375") | |||
| .withDockerTlsVerify(false) | |||
| .withApiVersion("1.40") | |||
| // .withDockerCertPath(dcokerCertPath) | |||
| // .withRegistryUsername(registryUser) | |||
| // .withRegistryPassword(registryPass) | |||
| // .withRegistryEmail(registryMail) | |||
| // .withRegistryUrl(registryUrl) | |||
| .build(); | |||
| //创建DockerHttpClient | |||
| DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder() | |||
| .dockerHost(config.getDockerHost()) | |||
| .sslConfig(config.getSSLConfig()) | |||
| .maxConnections(1000) | |||
| .connectionTimeout(Duration.ofSeconds(300)) | |||
| .responseTimeout(Duration.ofSeconds(450)) | |||
| .build(); | |||
| //创建DockerClient | |||
| return DockerClientImpl.getInstance(config, httpClient); | |||
| } | |||
| public String commitImage(ImageVo imageVo, String containerId, String hostIp, String userName) { | |||
| DockerClient dockerClient = getDockerClient(hostIp); | |||
| // 提交容器为镜像,这里的"new_image"和"new_tag"是新镜像的名字和标签 | |||
| CommitCmd commitCmd = dockerClient.commitCmd(containerId) | |||
| .withRepository(imageVo.getName()) | |||
| .withTag(imageVo.getTagName()) | |||
| .withAuthor(userName) | |||
| .withMessage(imageVo.getDescription()); | |||
| return commitCmd.exec(); | |||
| } | |||
| public HashMap<String, String> pushImageToHorbor(ImageVo imageVo, String hostIp) { | |||
| DockerClient dockerClient = getDockerClient(hostIp); | |||
| //Harbor登录信息 | |||
| AuthConfig autoConfig = new AuthConfig().withRegistryAddress(harborUrl).withUsername(harborUser).withPassword(harborpassword); | |||
| String localImageName = imageVo.getName() + ":" + imageVo.getTagName(); | |||
| String imageName = harborUrl + "/" + bucketName + "/" + imageVo.getName(); | |||
| //给镜像打上tag | |||
| dockerClient.tagImageCmd(localImageName, imageName, imageVo.getTagName()).exec(); | |||
| //推送镜像至镜像仓库 | |||
| try { | |||
| dockerClient.pushImageCmd(imageName).withAuthConfig(autoConfig).start().awaitCompletion(); | |||
| //push成功后,删除本地加载的镜像 | |||
| dockerClient.removeImageCmd(localImageName).exec(); | |||
| String totalImageName = imageName + ":" + imageVo.getTagName(); | |||
| InspectImageResponse exec = dockerClient.inspectImageCmd(totalImageName).exec(); | |||
| String size = String.format("%.1f GB", (float) exec.getSize() / 1073741824.0); | |||
| HashMap<String, String> resultMap = new HashMap<>(); | |||
| resultMap.put("imageName",totalImageName); | |||
| resultMap.put("size",size); | |||
| return resultMap; | |||
| } catch (InterruptedException e) { | |||
| throw new RuntimeException("推送镜像失败:" + e); | |||
| } | |||
| } | |||
| } | |||
| @@ -136,7 +136,7 @@ public class K8sClientUtil { | |||
| if (v1ServiceList!=null) { | |||
| for (V1Service svc : v1ServiceList.getItems()) { | |||
| if (StringUtils.equals(svc.getMetadata().getName(), serviceName)) { | |||
| // PVC 已存在 | |||
| // SVC 已存在 | |||
| return svc; | |||
| } | |||
| } | |||
| @@ -377,9 +377,48 @@ public class K8sClientUtil { | |||
| // 创建配置好的Pod | |||
| public Integer createConfiguredPod(String podName, String namespace, Integer port, String mountPath, V1PersistentVolumeClaim pvc, String image, String dataPvcName, String datasetPath, String modelPath) { | |||
| //设置选择节点,pod反亲和性 | |||
| Map<String, String> selector = new LinkedHashMap<>(); | |||
| selector.put("k8s-jupyter", podName); | |||
| selector.put("k8s-jupyter", "CPU-GPU"); | |||
| Map<String, String> nodeSelector = new LinkedHashMap<>(); | |||
| nodeSelector.put("resource-type", "CPU-GPU"); | |||
| V1LabelSelectorRequirement labelSelectorRequirement = new V1LabelSelectorRequirement() | |||
| .key("k8s-jupyter").operator("NotIn").values(Collections.singletonList("CPU-GPU")); | |||
| V1LabelSelector labelSelector = new V1LabelSelector() | |||
| .matchExpressions(Collections.singletonList(labelSelectorRequirement)); | |||
| V1PodAffinityTerm podAffinityTerm = new V1PodAffinityTerm() | |||
| .labelSelector(labelSelector) | |||
| .namespaces(Collections.singletonList(namespace)) | |||
| .topologyKey("kubernetes.io/hostname"); | |||
| V1PodAffinity podAffinity = new V1PodAffinity() | |||
| .requiredDuringSchedulingIgnoredDuringExecution(Collections.singletonList(podAffinityTerm)); | |||
| V1LabelSelectorRequirement antiLabelSelectorRequirement = new V1LabelSelectorRequirement() | |||
| .key("k8s-jupyter").operator("In").values(Collections.singletonList("CPU-GPU")); | |||
| V1LabelSelector antiLabelSelector = new V1LabelSelector() | |||
| .matchExpressions(Collections.singletonList(antiLabelSelectorRequirement)); | |||
| V1PodAffinityTerm antiPodAffinityTerm = new V1PodAffinityTerm() | |||
| .labelSelector(antiLabelSelector) | |||
| .namespaces(Collections.singletonList(namespace)) | |||
| .topologyKey("kubernetes.io/hostname"); | |||
| // V1WeightedPodAffinityTerm weightedPodAffinityTerm = new V1WeightedPodAffinityTerm().weight(100).podAffinityTerm(podAffinityTerm); | |||
| V1PodAntiAffinity podAntiAffinity = new V1PodAntiAffinity() | |||
| .requiredDuringSchedulingIgnoredDuringExecution(Collections.singletonList(antiPodAffinityTerm)); | |||
| V1Affinity v1Affinity = new V1Affinity() | |||
| .podAffinity(podAffinity) | |||
| .podAntiAffinity(podAntiAffinity); | |||
| // 创建Pod | |||
| CoreV1Api api = new CoreV1Api(apiClient); | |||
| V1PodList v1PodList = null; | |||
| try { | |||
| @@ -402,8 +441,8 @@ public class K8sClientUtil { | |||
| // 配置卷和卷挂载 | |||
| List<V1VolumeMount> volumeMounts = new ArrayList<>(); | |||
| volumeMounts.add(new V1VolumeMount().name("workspace").mountPath(mountPath)); | |||
| volumeMounts.add(new V1VolumeMount().name("minio-pvc").mountPath("/datasets").subPath(datasetPath).readOnly(true)); | |||
| volumeMounts.add(new V1VolumeMount().name("minio-pvc").mountPath("/model").subPath(modelPath).readOnly(true)); | |||
| volumeMounts.add(new V1VolumeMount().name("minio-pvc").mountPath("/opt/data").subPath(datasetPath).readOnly(true)); | |||
| volumeMounts.add(new V1VolumeMount().name("minio-pvc").mountPath("/opt/model").subPath(modelPath).readOnly(true)); | |||
| List<V1Volume> volumes = new ArrayList<>(); | |||
| volumes.add(new V1Volume().name("workspace").persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvc.getMetadata().getName()))); | |||
| @@ -423,6 +462,8 @@ public class K8sClientUtil { | |||
| .withVolumeMounts(volumeMounts) | |||
| .endContainer() | |||
| .withVolumes(volumes) | |||
| .withNodeSelector(nodeSelector) | |||
| .withAffinity(v1Affinity) | |||
| .endSpec() | |||
| .build(); | |||
| @@ -506,6 +547,28 @@ public class K8sClientUtil { | |||
| return pod.getStatus().getPhase(); | |||
| } | |||
| /** | |||
| * 根据Pod的名称和Namespace查询Pod的容器信息 | |||
| * @param podName Pod的名称 | |||
| * @param namespace Pod所在的Namespace | |||
| */ | |||
| public String getPodContainerId(String podName, String namespace) throws Exception { | |||
| CoreV1Api api = new CoreV1Api(apiClient); | |||
| V1Pod pod = api.readNamespacedPod(podName, namespace, null, null, null); | |||
| if(pod.getStatus().getContainerStatuses().size() !=1){ | |||
| throw new RuntimeException("容器错误"); | |||
| } | |||
| String containerId = pod.getStatus().getContainerStatuses().get(0).getContainerID().split("//")[1]; | |||
| return containerId; | |||
| } | |||
| public String getHostIp(String podName, String namespace) throws Exception { | |||
| CoreV1Api api = new CoreV1Api(apiClient); | |||
| V1Pod pod = api.readNamespacedPod(podName, namespace, null, null, null); | |||
| return pod.getStatus().getHostIP(); | |||
| } | |||
| public String getPodLogs(String podName,String namespace,String container,int line) { | |||
| CoreV1Api api = new CoreV1Api(apiClient); | |||
| try { | |||
| @@ -57,13 +57,16 @@ public class ImageVo implements Serializable { | |||
| @ApiModelProperty(name = "status") | |||
| private String status; | |||
| @ApiModelProperty(value = "上传方式, 基于公网上传0,基于本地上传1") | |||
| @ApiModelProperty(name = "upload_type", value = "上传方式, 基于公网上传0,基于本地上传1") | |||
| private Integer uploadType; | |||
| @ApiModelProperty(value = "镜像上传路径") | |||
| private String path; | |||
| @ApiModelProperty(name = "dev_environment_id", value = "环境id") | |||
| private Integer devEnvironmentId; | |||
| // public Integer getId() { | |||
| // return id; | |||
| // } | |||
| @@ -147,7 +150,17 @@ public class ImageVo implements Serializable { | |||
| public String getPath() { | |||
| return path; | |||
| } | |||
| public void setPath(String path) { | |||
| this.path = path; | |||
| } | |||
| public Integer getDevEnvironmentId() { | |||
| return devEnvironmentId; | |||
| } | |||
| public void setDevEnvironmentId(Integer devEnvironmentId) { | |||
| this.devEnvironmentId = devEnvironmentId; | |||
| } | |||
| } | |||
| @@ -0,0 +1,108 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |||
| <mapper namespace="com.ruoyi.platform.mapper.CodeConfigDao"> | |||
| <insert id="insert"> | |||
| insert into code_config(code_repo_name, code_repo_vis, git_url, git_branch, verify_mode, git_user_name, git_password, create_by, create_time, update_by, update_time) | |||
| values(#{codeConfig.codeRepoName}, #{codeConfig.codeRepoVis}, #{codeConfig.gitUrl}, #{codeConfig.gitBranch}, #{codeConfig.verifyMode}, #{codeConfig.gitUserName}, #{codeConfig.gitPassword}, #{codeConfig.createBy}, #{codeConfig.createTime}, #{codeConfig.updateBy}, #{codeConfig.updateTime}) | |||
| </insert> | |||
| <update id="update"> | |||
| update code_config | |||
| <set> | |||
| <if test="codeConfig.codeRepoName != null and codeConfig.codeRepoName != ''"> | |||
| code_repo_name = #{codeConfig.codeRepoName}, | |||
| </if> | |||
| <if test="codeConfig.codeRepoVis != null"> | |||
| code_repo_vis = #{codeConfig.codeRepoVis}, | |||
| </if> | |||
| <if test="codeConfig.gitUrl != null"> | |||
| git_url = #{codeConfig.gitUrl}, | |||
| </if> | |||
| <if test="codeConfig.gitBranch != null and codeConfig.gitBranch != ''"> | |||
| git_branch = #{codeConfig.gitBranch}, | |||
| </if> | |||
| <if test="codeConfig.verifyMode != null"> | |||
| verify_mode = #{codeConfig.verifyMode}, | |||
| </if> | |||
| <if test="codeConfig.gitUserName != null and codeConfig.gitUserName != ''"> | |||
| git_user_name = #{codeConfig.gitUserName}, | |||
| </if> | |||
| <if test="codeConfig.createBy != null and codeConfig.createBy != ''"> | |||
| create_by = #{codeConfig.createBy}, | |||
| </if> | |||
| <if test="codeConfig.createTime != null"> | |||
| create_time = #{codeConfig.createTime}, | |||
| </if> | |||
| <if test="codeConfig.updateBy != null and codeConfig.updateBy != ''"> | |||
| update_by = #{codeConfig.updateBy}, | |||
| </if> | |||
| <if test="codeConfig.updateTime != null"> | |||
| update_time = #{codeConfig.updateTime}, | |||
| </if> | |||
| <if test="codeConfig.state != null"> | |||
| state = #{codeConfig.state}, | |||
| </if> | |||
| </set> | |||
| where id = #{codeConfig.id} | |||
| </update> | |||
| <select id="count" resultType="java.lang.Long"> | |||
| select count(1) | |||
| from code_config | |||
| <include refid="common_condition"></include> | |||
| </select> | |||
| <select id="queryAllByLimit" resultType="com.ruoyi.platform.domain.CodeConfig"> | |||
| select * from code_config | |||
| <include refid="common_condition"></include> | |||
| limit #{pageable.offset}, #{pageable.pageSize} | |||
| </select> | |||
| <select id="queryById" resultType="com.ruoyi.platform.domain.CodeConfig"> | |||
| select * | |||
| from code_config | |||
| where id = #{id} | |||
| and state = 1 | |||
| </select> | |||
| <sql id="common_condition"> | |||
| <where> | |||
| state = 1 | |||
| <if test="codeConfig.id != null"> | |||
| and id = #{codeConfig.id} | |||
| </if> | |||
| <if test="codeConfig.codeRepoName != null and codeConfig.codeRepoName != ''"> | |||
| and code_repo_name = #{codeConfig.codeRepoName} | |||
| </if> | |||
| <if test="codeConfig.codeRepoVis != null"> | |||
| and code_repo_vis = #{codeConfig.codeRepoVis} | |||
| </if> | |||
| <if test="codeConfig.gitUrl != null"> | |||
| and git_url = #{codeConfig.gitUrl} | |||
| </if> | |||
| <if test="codeConfig.gitBranch != null and codeConfig.gitBranch != ''"> | |||
| and git_branch = #{codeConfig.gitBranch} | |||
| </if> | |||
| <if test="codeConfig.verifyMode != null"> | |||
| and verify_mode = #{codeConfig.verifyMode} | |||
| </if> | |||
| <if test="codeConfig.gitUserName != null and codeConfig.gitUserName != ''"> | |||
| and git_user_name = #{codeConfig.gitUserName} | |||
| </if> | |||
| <if test="codeConfig.createBy != null and codeConfig.createBy != ''"> | |||
| and create_by = #{codeConfig.createBy} | |||
| </if> | |||
| <if test="codeConfig.createTime != null"> | |||
| and create_time = #{codeConfig.createTime} | |||
| </if> | |||
| <if test="codeConfig.updateBy != null and codeConfig.updateBy != ''"> | |||
| and update_by = #{codeConfig.updateBy} | |||
| </if> | |||
| <if test="codeConfig.updateTime != null"> | |||
| and update_time = #{codeConfig.updateTime} | |||
| </if> | |||
| </where> | |||
| </sql> | |||
| </mapper> | |||
| @@ -65,7 +65,7 @@ | |||
| <groupId>com.mysql</groupId> | |||
| <artifactId>mysql-connector-j</artifactId> | |||
| </dependency> | |||
| <!-- RuoYi Common Log --> | |||
| <dependency> | |||
| <groupId>com.ruoyi</groupId> | |||
| @@ -77,7 +77,13 @@ | |||
| <groupId>com.ruoyi</groupId> | |||
| <artifactId>ruoyi-common-swagger</artifactId> | |||
| </dependency> | |||
| <!-- RuoYi Common DataSource --> | |||
| <dependency> | |||
| <groupId>com.ruoyi</groupId> | |||
| <artifactId>ruoyi-common-datasource</artifactId> | |||
| </dependency> | |||
| </dependencies> | |||
| <build> | |||