上传镜像到目标主机功能

This commit is contained in:
蒲先生 2025-07-25 18:22:47 +08:00
parent 6d6c2c8f58
commit cb22cd43c6
6 changed files with 106 additions and 30 deletions

View File

@ -27,6 +27,10 @@
<groupId>cd.casic.boot</groupId> <groupId>cd.casic.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cd.casic.boot</groupId>
<artifactId>module-ci-machine</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.github.docker-java</groupId> <groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId> <artifactId>docker-java</artifactId>
@ -61,6 +65,10 @@
<artifactId>jakarta.ws.rs-api</artifactId> <artifactId>jakarta.ws.rs-api</artifactId>
<version>3.1.0</version> <version>3.1.0</version>
</dependency> </dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
</dependency>
</dependencies> </dependencies>

View File

@ -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.dataobject.vo.DockerImagePageReqVO;
import cd.casic.module.execute.docker.service.IImageService; import cd.casic.module.execute.docker.service.IImageService;
import io.swagger.v3.oas.annotations.Operation; 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 io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.validation.Valid; import jakarta.validation.Valid;
@ -47,4 +49,14 @@ public class DockerImageController {
return success(imageService.getLocalImagePage(pageVO)); 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));
}
} }

View File

@ -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);
}

View File

@ -1,8 +1,6 @@
package cd.casic.module.execute.docker.dataobject.model; package cd.casic.module.execute.docker.dataobject.model;
import cd.casic.framework.commons.dataobject.BaseDO;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors;
/** /**
* 本地镜像 * 本地镜像
@ -12,8 +10,7 @@ import lombok.experimental.Accessors;
* @since 2025/7/23 16:22 * @since 2025/7/23 16:22
*/ */
@Data @Data
@Accessors(chain = true) public class DockerImage {
public class DockerImage extends BaseDO {
private String id; private String id;

View File

@ -109,4 +109,6 @@ public interface IImageService {
int localImageUpload(DockerImage dockerImage); int localImageUpload(DockerImage dockerImage);
PageResult<DockerImageDo> getLocalImagePage(DockerImagePageReqVO pageVO); PageResult<DockerImageDo> getLocalImagePage(DockerImagePageReqVO pageVO);
Object localImagePush(String imageId, String machineId);
} }

View File

@ -1,22 +1,33 @@
package cd.casic.module.execute.docker.service.impl; 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.commons.pojo.PageResult;
import cd.casic.framework.mybatis.core.query.LambdaQueryWrapperX; import cd.casic.framework.mybatis.core.query.LambdaQueryWrapperX;
import cd.casic.module.execute.docker.DockerClientFactory; import cd.casic.module.execute.docker.DockerClientFactory;
import cd.casic.module.execute.docker.dao.DockerImageDao; 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.dto.DockerImageDo;
import cd.casic.module.execute.docker.dataobject.model.DockerImage; import cd.casic.module.execute.docker.dataobject.model.DockerImage;
import cd.casic.module.execute.docker.dataobject.vo.DockerImagePageReqVO; import cd.casic.module.execute.docker.dataobject.vo.DockerImagePageReqVO;
import cd.casic.module.execute.docker.service.IImageService; 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.FileUtil;
import cn.hutool.core.io.IoUtil; 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.DockerClient;
import com.github.dockerjava.api.command.InspectImageResponse; import com.github.dockerjava.api.command.InspectImageResponse;
import com.github.dockerjava.api.command.SaveImageCmd; import com.github.dockerjava.api.command.SaveImageCmd;
import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.exception.NotFoundException;
import com.github.dockerjava.api.model.Image; import com.github.dockerjava.api.model.Image;
import com.github.dockerjava.api.model.PruneType; import com.github.dockerjava.api.model.PruneType;
import com.jcraft.jsch.Session;
import jakarta.annotation.Nonnull; import jakarta.annotation.Nonnull;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -24,11 +35,11 @@ import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.io.FileOutputStream; import java.io.*;
import java.io.IOException; import java.nio.charset.StandardCharsets;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.UUID;
/** /**
* @description: 镜像的服务类 * @description: 镜像的服务类
@ -46,6 +57,15 @@ public class ImageService implements IImageService {
@Resource @Resource
private DockerImageDao dockerImageDao; private DockerImageDao dockerImageDao;
@Resource
private MachineInfoMapper machineInfoMapper;
@Resource
private TargetFileUploadProperties fileUploadProperties;
@Resource
private SecretKeyMapper secretKeyMapper;
@Override @Override
public List<Image> list(@Nonnull String clientId) { public List<Image> list(@Nonnull String clientId) {
DockerClient dockerClient = dockerClientFactory.getdockerClient(clientId); DockerClient dockerClient = dockerClientFactory.getdockerClient(clientId);
@ -144,7 +164,9 @@ public class ImageService implements IImageService {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Override @Override
public int localImageUpload(DockerImage dockerImage) { 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 @Override
@ -153,4 +175,59 @@ public class ImageService implements IImageService {
PageResult<DockerImageDo> page = dockerImageDao.selectPage(pageVO, queryWrapperX.likeIfPresent(DockerImageDo::getName, pageVO.getName()).orderByDesc(DockerImageDo::getCreateTime)); PageResult<DockerImageDo> page = dockerImageDao.selectPage(pageVO, queryWrapperX.likeIfPresent(DockerImageDo::getName, pageVO.getName()).orderByDesc(DockerImageDo::getCreateTime));
return page; 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);
}
} }