| @@ -118,12 +118,20 @@ public class ImageController { | |||
| return AjaxResult.success(this.imageService.removeById(id)); | |||
| } | |||
| @PostMapping("/net") | |||
| @ApiOperation("从本地上传构建镜像") | |||
| public AjaxResult createImageFromNet(@RequestParam("name") String imageName, | |||
| @RequestParam("tag") String imageTag, | |||
| @RequestParam("path") String path) throws Exception { | |||
| return AjaxResult.success(this.imageService.createImageFromNet(imageName,imageTag,path)); | |||
| } | |||
| @PostMapping("/local") | |||
| @ApiOperation("从本地上传构建镜像") | |||
| public AjaxResult createImageFromLocal(@RequestParam("name") String imageName, | |||
| @RequestParam("tag") String imageTag, | |||
| @RequestParam("description") String imageDescription){ | |||
| return AjaxResult.success(this.imageService.createImageFromLocal(imageName,imageTag,imageDescription)); | |||
| @RequestParam("path") String path) throws Exception { | |||
| return AjaxResult.success(this.imageService.createImageFromLocal(imageName,imageTag,path)); | |||
| } | |||
| @@ -8,4 +8,8 @@ import org.springframework.web.bind.annotation.RestController; | |||
| @RequestMapping("tensorBoard") | |||
| @Api("流水线管理") | |||
| public class tensorBoard { | |||
| //状态查询接口 | |||
| //启动tensorBoard接口 | |||
| } | |||
| @@ -71,11 +71,11 @@ public interface ImageService { | |||
| * 本地上传构建镜像 | |||
| * @param imageName | |||
| * @param imageTag | |||
| * @param imageDescription | |||
| * @param path | |||
| * @return | |||
| */ | |||
| String createImageFromLocal(String imageName, String imageTag, String imageDescription); | |||
| String createImageFromLocal(String imageName, String imageTag, String path) throws Exception; | |||
| String createImageFromNet(String imageName, String imageTag, String NetPath) throws Exception; | |||
| Map<String, String> uploadImageFiles(MultipartFile file) throws Exception; | |||
| } | |||
| @@ -8,9 +8,12 @@ import com.ruoyi.platform.service.ImageService; | |||
| import com.ruoyi.platform.service.ImageVersionService; | |||
| import com.ruoyi.platform.service.MinioService; | |||
| 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.V1Pod; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.springframework.beans.factory.annotation.Value; | |||
| import org.springframework.stereotype.Service; | |||
| import org.springframework.data.domain.Page; | |||
| import org.springframework.data.domain.PageImpl; | |||
| @@ -43,11 +46,22 @@ public class ImageServiceImpl implements ImageService { | |||
| @Resource | |||
| private ImageVersionDao imageVersionDao; | |||
| @Resource | |||
| private MinioService minioService; | |||
| @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; | |||
| @Value("${harbor.deploymentName}") | |||
| private String deploymentName; | |||
| @Value("${harbor.serviceNS}") | |||
| private String serviceNS; | |||
| /** | |||
| * 通过ID查询单条数据 | |||
| * | |||
| @@ -178,15 +192,68 @@ public class ImageServiceImpl implements ImageService { | |||
| } | |||
| @Override | |||
| public String createImageFromLocal(String imageName, String imageTag, String fileName) { | |||
| // TODO 检查环境是否存在,如果不存在,请开启容器 | |||
| // TODO 在容器的/data/admin 目录下执行命令 docker load -i fileName 得到返回的镜像名字name:tag | |||
| // TODO 在容器里执行 docker tag name:tag nexus3.kube-system.svc:8083/imageName:imageTag | |||
| public String createImageFromNet(String imageName, String imageTag, String netPath) throws Exception { | |||
| // 得到容器 | |||
| V1Pod pod = K8sClientUtil.getNSPodList(serviceNS, deploymentName); | |||
| if (pod == null) { | |||
| throw new Exception("镜像推送服务不存在"); | |||
| } | |||
| String loginCmd = "docker login -u " + harborUser +" -p "+harborpassword+" "+harborUrl; | |||
| // 执行命令 docker login -u admin -p Harbor12345 172.20.32.187 | |||
| String loginlog = K8sClientUtil.executeCommand(pod,loginCmd); | |||
| // 在这个容器的/data/admin 目录下执行命令 docker load -i fileName 得到返回的镜像名字name:tag | |||
| String username = SecurityUtils.getLoginUser().getUsername(); | |||
| // | |||
| String logs2 = K8sClientUtil.executeCommand(pod,"docker pull "+netPath); | |||
| // 在容器里执行 docker tag name:tag nexus3.kube-system.svc:8083/imageName:imageTag | |||
| if (StringUtils.isNoneBlank(logs2)){ | |||
| String substring = logs2.substring(logs2.indexOf(harborUrl), logs2.length()); | |||
| String cleanedString = substring.replaceAll("(\\r|\\n)", ""); | |||
| String cmd1 = "docker tag " + cleanedString+ " " + harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag; | |||
| String imageUrl = harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag; | |||
| String cmd2 = "docker push " + imageUrl; | |||
| String s = K8sClientUtil.executeCommand(pod, cmd1); | |||
| if (StringUtils.isNotEmpty(K8sClientUtil.executeCommand(pod, cmd2))){ | |||
| return imageUrl; | |||
| }else { | |||
| throw new Exception("拉取公网镜像失败,请检查网络或者镜像地址"); | |||
| } | |||
| }else { | |||
| throw new Exception("拉取公网镜像失败,请检查网络或者镜像地址"); | |||
| } | |||
| } | |||
| return null; | |||
| @Override | |||
| public String createImageFromLocal(String imageName, String imageTag, String path) throws Exception { | |||
| // 得到容器 | |||
| V1Pod pod = K8sClientUtil.getNSPodList(serviceNS, deploymentName); | |||
| if (pod == null) { | |||
| throw new Exception("镜像推送服务不存在"); | |||
| } | |||
| String loginCmd = "docker login -u " + harborUser +" -p "+harborpassword+" "+harborUrl; | |||
| // 执行命令 docker login -u admin -p Harbor12345 172.20.32.187 | |||
| String loginlog = K8sClientUtil.executeCommand(pod,loginCmd); | |||
| // 在这个容器的/data/admin 目录下执行命令 docker load -i fileName 得到返回的镜像名字name:tag | |||
| String username = SecurityUtils.getLoginUser().getUsername(); | |||
| // | |||
| String filePath = "/data/argo-workflow/" + bucketName + "/" +path; | |||
| String logs2 = K8sClientUtil.executeCommand(pod,"docker load -i "+filePath); | |||
| // 在容器里执行 docker tag name:tag nexus3.kube-system.svc:8083/imageName:imageTag | |||
| if (StringUtils.isNoneBlank(logs2)){ | |||
| String substring = logs2.substring(logs2.indexOf(harborUrl), logs2.length()); | |||
| String cleanedString = substring.replaceAll("(\\r|\\n)", ""); | |||
| String cmd1 = "docker tag " + cleanedString+ " " + harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag; | |||
| String imageUrl = harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag; | |||
| String cmd2 = "docker push " + imageUrl; | |||
| String s = K8sClientUtil.executeCommand(pod, cmd1); | |||
| if (StringUtils.isNotEmpty(K8sClientUtil.executeCommand(pod, cmd2))){ | |||
| return imageUrl; | |||
| }else { | |||
| throw new Exception("解析镜像压缩包失败,请检查镜像文件"); | |||
| } | |||
| }else { | |||
| throw new Exception("解析镜像压缩包失败,请检查镜像文件"); | |||
| } | |||
| } | |||
| @Override | |||
| @@ -1,5 +1,6 @@ | |||
| package com.ruoyi.platform.utils; | |||
| import io.kubernetes.client.Exec; | |||
| import io.kubernetes.client.custom.IntOrString; | |||
| import io.kubernetes.client.custom.Quantity; | |||
| import io.kubernetes.client.openapi.ApiClient; | |||
| @@ -13,6 +14,9 @@ import org.apache.commons.lang.StringUtils; | |||
| import org.springframework.beans.factory.annotation.Value; | |||
| import org.springframework.stereotype.Component; | |||
| import java.io.BufferedReader; | |||
| import java.io.IOException; | |||
| import java.io.InputStreamReader; | |||
| import java.util.HashMap; | |||
| import java.util.LinkedHashMap; | |||
| import java.util.Map; | |||
| @@ -48,7 +52,7 @@ public class K8sClientUtil { | |||
| try { | |||
| this.apiClient = new ClientBuilder(). | |||
| setBasePath("https://172.20.32.181:6443").setVerifyingSsl(false). | |||
| setAuthentication(new AccessTokenAuthentication("eyJhbGciOiJSUzI1NiIsImtpZCI6IjRWcFBPWl9YSFFxQ2tVanRuNHdRT1dnUlJNTnB2bG5TQlVSRjNKdExWNDQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkZWZhdWx0LXRva2VuLXNteGxuIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImRlZmF1bHQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIyZWY1ZjdkMC0zMTdkLTQxN2UtOWY4Ni1mYjA1OTFhYWVhZDQiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06ZGVmYXVsdCJ9.GMpReYK7YJ0nNy-F6VrUJQzjWQiSauAOeq0-DT8ik2Lx8f2eznYEm_3cHX4kIn_nYgfxo857urcHt4Ft6IgVtWzxLzVTCQVaNP_H2J8bnJn8W2tUKXzF_3d_GwO75H7kN8P3aoShULrOLpiIf3o3Az28_gwHkwCnd42npcKrCXfAKj8A2U7-KUFQXXA-etrWSw81C5t8ziL-2xaiDgwD3ewH-TNYsOpyWjGopNTxJn1F7GyJ7xDlmMJOaZhSnOrDggB7lqDEsE68YmZtqB7lcSaZHnKzvNhEdbKri4R7_urpjttz_k6qcfIi-l6GwPtJLatsPDg3OL3FFnzjvArJ-A")).build(); | |||
| setAuthentication(new AccessTokenAuthentication("eyJhbGciOiJSUzI1NiIsImtpZCI6IjRWcFBPWl9YSFFxQ2tVanRuNHdRT1dnUlJNTnB2bG5TQlVSRjNKdExWNDQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImFkbWluLXNlcnZpY2UtYWNjb3VudC10b2tlbi14ZDk5eiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJhZG1pbi1zZXJ2aWNlLWFjY291bnQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJmMGEzNmYyMS01MjQyLTQ4MTAtYWVmZS0xOTEwOTZlZjc5YmUiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDphZG1pbi1zZXJ2aWNlLWFjY291bnQifQ.fo-Wf0-5-IRC5fhRh65yfqCJqKfE9MrNFIXL2fd1CqVAHD7JBpWO2IsFiSmz9Bm7VfLmFAp2NB7DjW4ZLjC7ODiGhpSseBP8x4ceFuHL6pRGUsEBvHQBBBuQcGhNOcsxIDHnDqUdUzoLprj223lMZNTQowITuqYFU4GVbethyEuS6G5Wh9KHI3KYHFtG4_AeWBgI5Ppz8pDrhHzSFWTFbzxQ3RPGEwF0V-9wEtdrSYnfETi3rdRWif9W4a0RW8HwD9Gf7UCYcyFOs7e5_3-IvmctS85g87PYIfHXMhu_kOw-_Il4bkwPEK5uiBFDw0M1-s9YP-F9r5sXXvOJlsAr1g")).build(); | |||
| } catch (Exception e) { | |||
| log.error("构建K8s-Client异常", e); | |||
| throw new RuntimeException("构建K8s-Client异常"); | |||
| @@ -62,16 +66,16 @@ public class K8sClientUtil { | |||
| * | |||
| * @param kubeConfigPath kube连接配置文件 | |||
| */ | |||
| public K8sClientUtil(String kubeConfigPath) { | |||
| try { | |||
| this.apiClient = new ClientBuilder(). | |||
| setBasePath("https://172.20.32.181:6443").setVerifyingSsl(false). | |||
| setAuthentication(new AccessTokenAuthentication("eyJhbGciOiJSUzI1NiIsImtpZCI6IjRWcFBPWl9YSFFxQ2tVanRuNHdRT1dnUlJNTnB2bG5TQlVSRjNKdExWNDQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkZWZhdWx0LXRva2VuLXNteGxuIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImRlZmF1bHQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIyZWY1ZjdkMC0zMTdkLTQxN2UtOWY4Ni1mYjA1OTFhYWVhZDQiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06ZGVmYXVsdCJ9.GMpReYK7YJ0nNy-F6VrUJQzjWQiSauAOeq0-DT8ik2Lx8f2eznYEm_3cHX4kIn_nYgfxo857urcHt4Ft6IgVtWzxLzVTCQVaNP_H2J8bnJn8W2tUKXzF_3d_GwO75H7kN8P3aoShULrOLpiIf3o3Az28_gwHkwCnd42npcKrCXfAKj8A2U7-KUFQXXA-etrWSw81C5t8ziL-2xaiDgwD3ewH-TNYsOpyWjGopNTxJn1F7GyJ7xDlmMJOaZhSnOrDggB7lqDEsE68YmZtqB7lcSaZHnKzvNhEdbKri4R7_urpjttz_k6qcfIi-l6GwPtJLatsPDg3OL3FFnzjvArJ-A")).build(); | |||
| } catch (Exception e) { | |||
| log.error("构建K8s-Client异常", e); | |||
| throw new RuntimeException("构建K8s-Client异常"); | |||
| } | |||
| } | |||
| // public K8sClientUtil(String kubeConfigPath) { | |||
| // try { | |||
| // this.apiClient = new ClientBuilder(). | |||
| // setBasePath("https://172.20.32.181:6443").setVerifyingSsl(false). | |||
| // setAuthentication(new AccessTokenAuthentication("eyJhbGciOiJSUzI1NiIsImtpZCI6IjRWcFBPWl9YSFFxQ2tVanRuNHdRT1dnUlJNTnB2bG5TQlVSRjNKdExWNDQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkZWZhdWx0LXRva2VuLXNteGxuIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImRlZmF1bHQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIyZWY1ZjdkMC0zMTdkLTQxN2UtOWY4Ni1mYjA1OTFhYWVhZDQiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06ZGVmYXVsdCJ9.GMpReYK7YJ0nNy-F6VrUJQzjWQiSauAOeq0-DT8ik2Lx8f2eznYEm_3cHX4kIn_nYgfxo857urcHt4Ft6IgVtWzxLzVTCQVaNP_H2J8bnJn8W2tUKXzF_3d_GwO75H7kN8P3aoShULrOLpiIf3o3Az28_gwHkwCnd42npcKrCXfAKj8A2U7-KUFQXXA-etrWSw81C5t8ziL-2xaiDgwD3ewH-TNYsOpyWjGopNTxJn1F7GyJ7xDlmMJOaZhSnOrDggB7lqDEsE68YmZtqB7lcSaZHnKzvNhEdbKri4R7_urpjttz_k6qcfIi-l6GwPtJLatsPDg3OL3FFnzjvArJ-A")).build(); | |||
| // } catch (Exception e) { | |||
| // log.error("构建K8s-Client异常", e); | |||
| // throw new RuntimeException("构建K8s-Client异常"); | |||
| // } | |||
| // } | |||
| /** | |||
| * 获取所有的Pod | |||
| @@ -269,4 +273,52 @@ public class K8sClientUtil { | |||
| V1Service service = createService(namespace, podName + "-svc", port, selector); | |||
| return service.getSpec().getPorts().get(0).getNodePort(); | |||
| } | |||
| /** | |||
| * 根据获取namespace,deploymentName的Pod Name | |||
| * | |||
| * @return podList | |||
| */ | |||
| public static V1Pod getNSPodList(String namespace,String deploymentName) throws Exception { | |||
| // new a CoreV1Api | |||
| CoreV1Api api = new CoreV1Api(apiClient); | |||
| V1PodList v1PodList = null; | |||
| try { | |||
| v1PodList = api.listNamespacedPod(namespace, null, null, null, null, null, null, null, null, null, null); | |||
| } catch (ApiException e) { | |||
| log.error("获取 POD 异常:", e); | |||
| } | |||
| // invokes the CoreV1Api client | |||
| for (V1Pod item : v1PodList.getItems()) { | |||
| if (item.getMetadata().getGenerateName().startsWith(deploymentName)) { | |||
| // 找到匹配的Pod,获取其名称 | |||
| return item; | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| public static String executeCommand(V1Pod item, String command) { | |||
| try { | |||
| // 创建API实例 | |||
| // 创建Exec实例 | |||
| Exec exec = new Exec(apiClient); | |||
| String[] cmd = { "/bin/sh", "-c", command}; | |||
| Process proc = exec.exec(item, cmd, false); | |||
| // 读取输出 | |||
| BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream())); | |||
| StringBuilder builder = new StringBuilder(); | |||
| String line = null; | |||
| while ((line = reader.readLine()) != null) { | |||
| builder.append(line); | |||
| builder.append(System.getProperty("line.separator")); | |||
| } | |||
| return builder.toString(); | |||
| } catch (Exception e) { | |||
| log.error("执行命令异常", e); | |||
| throw new RuntimeException("执行命令异常"); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| apiVersion: rbac.authorization.k8s.io/v1 | |||
| kind: ClusterRoleBinding | |||
| metadata: | |||
| name: admin-service-account-binding | |||
| roleRef: | |||
| apiGroup: rbac.authorization.k8s.io | |||
| kind: ClusterRole | |||
| name: cluster-admin | |||
| subjects: | |||
| - kind: ServiceAccount | |||
| name: admin-service-account | |||
| namespace: default | |||