diff --git a/modules/module-ci-execute/pom.xml b/modules/module-ci-execute/pom.xml index 3db1d4ef..0ea55e7a 100644 --- a/modules/module-ci-execute/pom.xml +++ b/modules/module-ci-execute/pom.xml @@ -27,6 +27,10 @@ cd.casic.boot spring-boot-starter-test + + cd.casic.boot + module-ci-machine + com.github.docker-java docker-java @@ -61,6 +65,10 @@ jakarta.ws.rs-api 3.1.0 + + com.jcraft + jsch + diff --git a/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/api/DockerImageController.java b/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/api/DockerImageController.java index 6c2e43e3..f2b30a02 100644 --- a/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/api/DockerImageController.java +++ b/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/api/DockerImageController.java @@ -7,6 +7,8 @@ import cd.casic.module.execute.docker.dataobject.model.DockerImage; import cd.casic.module.execute.docker.dataobject.vo.DockerImagePageReqVO; import cd.casic.module.execute.docker.service.IImageService; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; @@ -47,4 +49,14 @@ public class DockerImageController { return success(imageService.getLocalImagePage(pageVO)); } + @GetMapping("/localImagePush") + @Operation(summary = "镜像推送,或者部署到目标服务器") + @Parameters({ + @Parameter(name = "imageId", description = "镜像id"), + @Parameter(name = "machineId", description = "主机id") + }) + @PreAuthorize("@ss.hasPermission('docker:images:localImagePush')") + public CommonResult localImagePush(@RequestParam(value = "imageId") String imageId, @RequestParam("machineId") String machineId) { + return success(imageService.localImagePush(imageId,machineId)); + } } diff --git a/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/dataobject/convert/DockerImageConvert.java b/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/dataobject/convert/DockerImageConvert.java deleted file mode 100644 index ee288638..00000000 --- a/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/dataobject/convert/DockerImageConvert.java +++ /dev/null @@ -1,20 +0,0 @@ -package cd.casic.module.execute.docker.dataobject.convert; - -import cd.casic.module.execute.docker.dataobject.dto.DockerImageDo; -import cd.casic.module.execute.docker.dataobject.model.DockerImage; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -/** - * - * @author Yuru Pu - * @version 1.0 - * @since 2025/7/23 17:16 - */ -@Mapper(componentModel = "spring") -public interface DockerImageConvert { - DockerImageConvert INSTANCE = Mappers.getMapper(DockerImageConvert.class); - - DockerImageDo convert(DockerImage dockerImage); - -} diff --git a/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/dataobject/model/DockerImage.java b/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/dataobject/model/DockerImage.java index 02363a2c..2c5383c4 100644 --- a/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/dataobject/model/DockerImage.java +++ b/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/dataobject/model/DockerImage.java @@ -1,8 +1,6 @@ package cd.casic.module.execute.docker.dataobject.model; -import cd.casic.framework.commons.dataobject.BaseDO; import lombok.Data; -import lombok.experimental.Accessors; /** * 本地镜像 @@ -12,8 +10,7 @@ import lombok.experimental.Accessors; * @since 2025/7/23 16:22 */ @Data -@Accessors(chain = true) -public class DockerImage extends BaseDO { +public class DockerImage { private String id; diff --git a/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/service/IImageService.java b/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/service/IImageService.java index 3486116a..96eb3a01 100644 --- a/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/service/IImageService.java +++ b/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/service/IImageService.java @@ -109,4 +109,6 @@ public interface IImageService { int localImageUpload(DockerImage dockerImage); PageResult getLocalImagePage(DockerImagePageReqVO pageVO); + + Object localImagePush(String imageId, String machineId); } diff --git a/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/service/impl/ImageService.java b/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/service/impl/ImageService.java index c34d787d..251cbab5 100644 --- a/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/service/impl/ImageService.java +++ b/modules/module-ci-execute/src/main/java/cd/casic/module/execute/docker/service/impl/ImageService.java @@ -1,22 +1,33 @@ package cd.casic.module.execute.docker.service.impl; +import cd.casic.ci.commons.properties.TargetFileUploadProperties; import cd.casic.framework.commons.pojo.PageResult; import cd.casic.framework.mybatis.core.query.LambdaQueryWrapperX; import cd.casic.module.execute.docker.DockerClientFactory; import cd.casic.module.execute.docker.dao.DockerImageDao; -import cd.casic.module.execute.docker.dataobject.convert.DockerImageConvert; import cd.casic.module.execute.docker.dataobject.dto.DockerImageDo; import cd.casic.module.execute.docker.dataobject.model.DockerImage; import cd.casic.module.execute.docker.dataobject.vo.DockerImagePageReqVO; import cd.casic.module.execute.docker.service.IImageService; +import cd.casic.module.machine.dal.dataobject.MachineInfoDO; +import cd.casic.module.machine.dal.dataobject.SecretKeyDO; +import cd.casic.module.machine.dal.mysql.MachineInfoMapper; +import cd.casic.module.machine.dal.mysql.SecretKeyMapper; +import cd.casic.module.machine.enums.AuthenticationType; +import cd.casic.module.machine.utils.CryptogramUtil; +import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; +import cn.hutool.extra.ftp.FtpConfig; +import cn.hutool.extra.ssh.JschUtil; +import cn.hutool.extra.ssh.Sftp; import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.InspectImageResponse; import com.github.dockerjava.api.command.SaveImageCmd; import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.Image; import com.github.dockerjava.api.model.PruneType; +import com.jcraft.jsch.Session; import jakarta.annotation.Nonnull; import jakarta.annotation.Resource; import lombok.RequiredArgsConstructor; @@ -24,11 +35,11 @@ import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.Objects; +import java.util.UUID; /** * @description: 镜像的服务类 @@ -46,6 +57,15 @@ public class ImageService implements IImageService { @Resource private DockerImageDao dockerImageDao; + @Resource + private MachineInfoMapper machineInfoMapper; + + @Resource + private TargetFileUploadProperties fileUploadProperties; + + @Resource + private SecretKeyMapper secretKeyMapper; + @Override public List list(@Nonnull String clientId) { DockerClient dockerClient = dockerClientFactory.getdockerClient(clientId); @@ -144,7 +164,9 @@ public class ImageService implements IImageService { @Transactional(rollbackFor = Exception.class) @Override public int localImageUpload(DockerImage dockerImage) { - return dockerImageDao.insert(DockerImageConvert.INSTANCE.convert(dockerImage)); + DockerImageDo imageDo = new DockerImageDo(); + BeanUtil.copyProperties(dockerImage,imageDo); + return dockerImageDao.insert(imageDo); } @Override @@ -153,4 +175,59 @@ public class ImageService implements IImageService { PageResult page = dockerImageDao.selectPage(pageVO, queryWrapperX.likeIfPresent(DockerImageDo::getName, pageVO.getName()).orderByDesc(DockerImageDo::getCreateTime)); return page; } + + /** + * 先下载后上传到目标主机 + * @param imageId + * @param machineId + * @return + */ + @Override + public Object localImagePush(String imageId, String machineId) { + if (Objects.isNull(imageId) || Objects.isNull(machineId)) { + return null; + } + DockerImageDo imageDo = dockerImageDao.selectById(imageId); + //1 远程下载 + // 1.1 建立连接 下载 + Sftp sftp = getSftp(FtpConfig.create().setHost(fileUploadProperties.getRemoteHost()).setPort(fileUploadProperties.getRemotePort()).setUser(fileUploadProperties.getUsername()).setPassword(fileUploadProperties.getPassword())); + String srcPath = imageDo.getPath(); + ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + sftp.download(srcPath, byteOut); //远程下载到中间缓存区 + sftp.close(); + // 2 建立连接 区分密码还是秘钥 + MachineInfoDO machineInfoDO = machineInfoMapper.selectById(machineId); + FtpConfig targetFtp = FtpConfig.create().setHost(machineInfoDO.getHostIp()).setPort(machineInfoDO.getSshPort()).setUser(machineInfoDO.getUsername()).setPassword(CryptogramUtil.doDecrypt(machineInfoDO.getPassword())); + if (AuthenticationType.of(machineInfoDO.getAuthenticationType()).equals(AuthenticationType.SECRET_KEY)) { + SecretKeyDO secretKeyDO = secretKeyMapper.selectById(machineInfoDO.getSecretKeyId()); + targetFtp.setSystemKey(CryptogramUtil.doDecrypt(secretKeyDO.getPrivateKey())); + targetFtp.setPassword(CryptogramUtil.doDecrypt(secretKeyDO.getPassword())); + } + Sftp uploadSftp = getSftp(targetFtp); + // 2.1 上传 + String suffix = FileUtil.getSuffix(srcPath); + String fileName = imageDo.getName() + "." + suffix; + ByteArrayInputStream arrayInputStream = new ByteArrayInputStream(byteOut.toByteArray()); //转换 + String romPath = "/home/image/" + UUID.randomUUID()+"/"; + uploadSftp.mkDirs(romPath);//递归创建 + uploadSftp.upload(romPath, fileName, arrayInputStream); + uploadSftp.close(); + return null; + } + + /** + * + * 获取Sftp连接 + * @param config 连接配置 + * @return + */ + private Sftp getSftp(FtpConfig config){ + Session session; + if (Objects.nonNull(config.getSystemKey())) { + session = JschUtil.getSession(config.getHost(), config.getPort(), config.getUser(), config.getSystemKey().getBytes(StandardCharsets.UTF_8), config.getPassword().getBytes()); + } else { + session = JschUtil.getSession(config.getHost(), config.getPort(), config.getUser(), config.getPassword()); + } + return new Sftp(session); + } }