@@ -1,6 +1,8 @@ | |||
# ---> Java | |||
# Compiled class file | |||
*.class | |||
./.idea/ | |||
.idea/ | |||
# Log file | |||
*.log | |||
@@ -0,0 +1,218 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4"> | |||
<component name="FacetManager"> | |||
<facet type="web" name="Web"> | |||
<configuration> | |||
<webroots /> | |||
<sourceRoots> | |||
<root url="file://$MODULE_DIR$/src/main/java" /> | |||
<root url="file://$MODULE_DIR$/src/main/resources" /> | |||
</sourceRoots> | |||
</configuration> | |||
</facet> | |||
<facet type="Spring" name="Spring"> | |||
<configuration /> | |||
</facet> | |||
</component> | |||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8"> | |||
<output url="file://$MODULE_DIR$/target/classes" /> | |||
<output-test url="file://$MODULE_DIR$/target/test-classes" /> | |||
<content url="file://$MODULE_DIR$"> | |||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> | |||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> | |||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> | |||
<excludeFolder url="file://$MODULE_DIR$/target" /> | |||
</content> | |||
<orderEntry type="inheritedJdk" /> | |||
<orderEntry type="sourceFolder" forTests="false" /> | |||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-configuration-processor:2.5.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.5.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.5.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.3.12" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.5.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.5.6" level="project" /> | |||
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.6" level="project" /> | |||
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.14.1" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.14.1" level="project" /> | |||
<orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.32" level="project" /> | |||
<orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework:spring-core:5.3.12" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.3.12" level="project" /> | |||
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.28" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-starter-test:2.5.6" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test:2.5.6" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test-autoconfigure:2.5.6" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: com.jayway.jsonpath:json-path:2.5.0" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: net.minidev:json-smart:2.4.7" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: net.minidev:accessors-smart:2.4.7" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.ow2.asm:asm:9.1" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: jakarta.activation:jakarta.activation-api:1.2.2" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.assertj:assertj-core:3.19.0" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest:2.2" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter:5.7.2" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-api:5.7.2" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.apiguardian:apiguardian-api:1.1.0" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.opentest4j:opentest4j:1.2.0" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-commons:1.7.2" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-params:5.7.2" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-engine:5.7.2" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-engine:1.7.2" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-core:3.9.0" level="project" /> | |||
<orderEntry type="library" name="Maven: net.bytebuddy:byte-buddy:1.10.22" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: net.bytebuddy:byte-buddy-agent:1.10.22" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.objenesis:objenesis:3.2" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-junit-jupiter:3.9.0" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.skyscreamer:jsonassert:1.5.0" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: com.vaadin.external.google:android-json:0.0.20131108.vaadin1" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.springframework:spring-test:5.3.12" level="project" /> | |||
<orderEntry type="library" scope="TEST" name="Maven: org.xmlunit:xmlunit-core:2.8.3" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.5.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.3.12" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework:spring-beans:5.3.12" level="project" /> | |||
<orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.9.7" level="project" /> | |||
<orderEntry type="library" name="Maven: com.spring4all:swagger-spring-boot-starter:1.9.0.RELEASE" level="project" /> | |||
<orderEntry type="library" name="Maven: io.springfox:springfox-swagger-ui:2.9.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.springfox:springfox-spring-web:2.9.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.springfox:springfox-swagger2:2.9.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.swagger:swagger-annotations:1.5.20" level="project" /> | |||
<orderEntry type="library" name="Maven: io.swagger:swagger-models:1.5.20" level="project" /> | |||
<orderEntry type="library" name="Maven: io.springfox:springfox-spi:2.9.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.springfox:springfox-core:2.9.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.springfox:springfox-schema:2.9.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.springfox:springfox-swagger-common:2.9.2" level="project" /> | |||
<orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-core:1.2.0.RELEASE" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-metadata:1.2.0.RELEASE" level="project" /> | |||
<orderEntry type="library" name="Maven: org.mapstruct:mapstruct:1.2.0.Final" level="project" /> | |||
<orderEntry type="library" name="Maven: io.springfox:springfox-bean-validators:2.9.2" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.5.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.5.6" level="project" /> | |||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.12.5" level="project" /> | |||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.12.5" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-tomcat:2.5.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.54" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-websocket:9.0.54" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework:spring-web:5.3.12" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.3.12" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.3.12" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.5.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-redis:2.5.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-keyvalue:2.5.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-commons:2.5.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework:spring-tx:5.3.12" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework:spring-oxm:5.3.12" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.3.12" level="project" /> | |||
<orderEntry type="library" name="Maven: io.lettuce:lettuce-core:6.1.5.RELEASE" level="project" /> | |||
<orderEntry type="library" name="Maven: io.netty:netty-common:4.1.69.Final" level="project" /> | |||
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.69.Final" level="project" /> | |||
<orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.69.Final" level="project" /> | |||
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.69.Final" level="project" /> | |||
<orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.69.Final" level="project" /> | |||
<orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.69.Final" level="project" /> | |||
<orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.4.11" level="project" /> | |||
<orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.3" level="project" /> | |||
<orderEntry type="library" name="Maven: org.projectlombok:lombok:1.18.22" level="project" /> | |||
<orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.83" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.5" level="project" /> | |||
<orderEntry type="library" name="Maven: javax.validation:validation-api:2.0.1.Final" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.5.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-el:9.0.54" level="project" /> | |||
<orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.2.0.Final" level="project" /> | |||
<orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" /> | |||
<orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.2.Final" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.12.0" level="project" /> | |||
<orderEntry type="library" name="Maven: cn.hutool:hutool-all:5.7.11" level="project" /> | |||
<orderEntry type="library" name="Maven: tk.mybatis:mapper-spring-boot-starter:2.1.0" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.5.6" level="project" /> | |||
<orderEntry type="library" name="Maven: com.zaxxer:HikariCP:4.0.3" level="project" /> | |||
<orderEntry type="library" name="Maven: org.springframework:spring-jdbc:5.3.12" level="project" /> | |||
<orderEntry type="library" name="Maven: org.mybatis:mybatis:3.4.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.mybatis:mybatis-spring:1.3.2" level="project" /> | |||
<orderEntry type="library" name="Maven: tk.mybatis:mapper-core:1.1.0" level="project" /> | |||
<orderEntry type="library" name="Maven: javax.persistence:persistence-api:1.0" level="project" /> | |||
<orderEntry type="library" name="Maven: tk.mybatis:mapper-base:1.1.0" level="project" /> | |||
<orderEntry type="library" name="Maven: tk.mybatis:mapper-weekend:1.1.4.2" level="project" /> | |||
<orderEntry type="library" name="Maven: tk.mybatis:mapper-spring:1.1.0" level="project" /> | |||
<orderEntry type="library" name="Maven: tk.mybatis:mapper-extra:1.1.0" level="project" /> | |||
<orderEntry type="library" name="Maven: tk.mybatis:mapper-spring-boot-autoconfigure:2.1.0" level="project" /> | |||
<orderEntry type="library" scope="RUNTIME" name="Maven: mysql:mysql-connector-java:8.0.27" level="project" /> | |||
<orderEntry type="library" name="Maven: com.alibaba:druid-spring-boot-starter:1.1.10" level="project" /> | |||
<orderEntry type="library" name="Maven: com.alibaba:druid:1.1.10" level="project" /> | |||
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.32" level="project" /> | |||
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-starter:1.2.10" level="project" /> | |||
<orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.2" level="project" /> | |||
<orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-autoconfigure:1.3.2" level="project" /> | |||
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-autoconfigure:1.2.10" level="project" /> | |||
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper:5.1.8" level="project" /> | |||
<orderEntry type="library" name="Maven: com.github.jsqlparser:jsqlparser:1.2" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.shiro:shiro-core:1.9.0" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.shiro:shiro-lang:1.9.0" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.shiro:shiro-cache:1.9.0" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.shiro:shiro-crypto-hash:1.9.0" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.shiro:shiro-crypto-core:1.9.0" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.shiro:shiro-crypto-cipher:1.9.0" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.shiro:shiro-config-core:1.9.0" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.shiro:shiro-config-ogdl:1.9.0" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.shiro:shiro-event:1.9.0" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.shiro:shiro-spring:1.9.0" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.shiro:shiro-web:1.9.0" level="project" /> | |||
<orderEntry type="library" name="Maven: org.owasp.encoder:encoder:1.2.2" level="project" /> | |||
<orderEntry type="library" name="Maven: com.google.code.gson:gson:2.9.0" level="project" /> | |||
<orderEntry type="library" name="Maven: commons-io:commons-io:2.7" level="project" /> | |||
<orderEntry type="library" name="Maven: commons-codec:commons-codec:1.15" level="project" /> | |||
<orderEntry type="library" name="Maven: commons-net:commons-net:3.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.9" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-client:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-core:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-common:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.12.5" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-rbac:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-admissionregistration:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-apps:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-autoscaling:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-apiextensions:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-batch:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-certificates:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-coordination:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-discovery:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-events:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-extensions:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-flowcontrol:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-networking:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-metrics:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-policy:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-scheduling:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-storageclass:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:kubernetes-model-node:5.11.2" level="project" /> | |||
<orderEntry type="library" name="Maven: com.squareup.okhttp3:okhttp:3.14.9" level="project" /> | |||
<orderEntry type="library" name="Maven: com.squareup.okio:okio:1.17.2" level="project" /> | |||
<orderEntry type="library" name="Maven: com.squareup.okhttp3:logging-interceptor:3.14.9" level="project" /> | |||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.12.5" level="project" /> | |||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.5" level="project" /> | |||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.12.5" level="project" /> | |||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.12.5" level="project" /> | |||
<orderEntry type="library" name="Maven: io.fabric8:zjsonpatch:0.3.0" level="project" /> | |||
<orderEntry type="library" name="Maven: com.github.mifmif:generex:1.0.2" level="project" /> | |||
<orderEntry type="library" name="Maven: dk.brics.automaton:automaton:1.11-8" level="project" /> | |||
<orderEntry type="library" name="Maven: redis.clients:jedis:2.9.0" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.commons:commons-pool2:2.9.0" level="project" /> | |||
<orderEntry type="library" name="Maven: com.google.guava:guava:22.0" level="project" /> | |||
<orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:1.3.9" level="project" /> | |||
<orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.0.18" level="project" /> | |||
<orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.1" level="project" /> | |||
<orderEntry type="library" name="Maven: org.codehaus.mojo:animal-sniffer-annotations:1.14" level="project" /> | |||
<orderEntry type="library" name="Maven: com.googlecode.aviator:aviator:5.1.2" level="project" /> | |||
<orderEntry type="library" name="Maven: commons-beanutils:commons-beanutils:1.9.3" level="project" /> | |||
<orderEntry type="library" name="Maven: commons-collections:commons-collections:3.2.2" level="project" /> | |||
<orderEntry type="library" name="Maven: com.aliyun:aliyun-java-sdk-core:4.4.6" level="project" /> | |||
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.5.13" level="project" /> | |||
<orderEntry type="library" name="Maven: commons-logging:commons-logging:1.2" level="project" /> | |||
<orderEntry type="library" name="Maven: javax.xml.bind:jaxb-api:2.3.1" level="project" /> | |||
<orderEntry type="library" name="Maven: javax.activation:javax.activation-api:1.2.0" level="project" /> | |||
<orderEntry type="library" name="Maven: org.jacoco:org.jacoco.agent:runtime:0.8.3" level="project" /> | |||
<orderEntry type="library" name="Maven: org.ini4j:ini4j:0.5.4" level="project" /> | |||
<orderEntry type="library" name="Maven: org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0" level="project" /> | |||
</component> | |||
</module> |
@@ -0,0 +1,36 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<artifactId>parent</artifactId> | |||
<groupId>com.imitate</groupId> | |||
<version>0.0.1-SNAPSHOT</version> | |||
<relativePath>../parent/pom.xml</relativePath> | |||
</parent> | |||
<artifactId>common</artifactId> | |||
<version>0.0.1-SNAPSHOT</version> | |||
<name>common</name> | |||
<description>common</description> | |||
<packaging>jar</packaging> | |||
<dependencies> | |||
<!-- 配置文件联想提示 --> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-configuration-processor</artifactId> | |||
<optional>true</optional> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-autoconfigure</artifactId> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -0,0 +1,73 @@ | |||
package com.imitate.common.advisor; | |||
import com.imitate.common.enums.ErrorCodeEnum; | |||
import com.imitate.common.exception.BusinessException; | |||
import com.imitate.common.util.R; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.http.HttpStatus; | |||
import org.springframework.validation.BindException; | |||
import org.springframework.web.HttpRequestMethodNotSupportedException; | |||
import org.springframework.web.bind.MissingServletRequestParameterException; | |||
import org.springframework.web.bind.annotation.ExceptionHandler; | |||
import org.springframework.web.bind.annotation.ResponseStatus; | |||
import org.springframework.web.bind.annotation.RestControllerAdvice; | |||
import javax.validation.ConstraintViolationException; | |||
/** | |||
* @author yanchao | |||
*/ | |||
@RestControllerAdvice | |||
public class DefaultControllerAdvisor { | |||
private static final Logger logger = LoggerFactory.getLogger(DefaultControllerAdvisor.class); | |||
@ExceptionHandler(Exception.class) | |||
public R processException(Exception e) { | |||
logger.error(e.getMessage(), e); | |||
return R.error(ErrorCodeEnum.EXCEPTION.getValue(),ErrorCodeEnum.EXCEPTION.getDescription()); | |||
} | |||
@ExceptionHandler(BusinessException.class) | |||
public R processException(BusinessException e) { | |||
logger.error(e.getMessage(), e); | |||
return R.error(e.getErrCode(),e.getMessage()); | |||
} | |||
@ExceptionHandler(MissingServletRequestParameterException.class) | |||
public R processMissingServletRequestParameterException(MissingServletRequestParameterException e){ | |||
logger.error(e.getMessage(), e); | |||
return R.error(ErrorCodeEnum.MVC_BIND_EXCEPTION.getValue(),ErrorCodeEnum.MVC_BIND_EXCEPTION.getDescription()); | |||
} | |||
@ExceptionHandler(ConstraintViolationException.class) | |||
public R processConstraintViolationException(ConstraintViolationException e) { | |||
logger.error(e.getMessage(), e); | |||
return R.error(ErrorCodeEnum.INVALID_ARG_EXCEPTION.getValue(),ErrorCodeEnum.INVALID_ARG_EXCEPTION.getDescription()); | |||
} | |||
@ExceptionHandler(BindException.class) | |||
public R processBindException(BindException e) { | |||
logger.error(e.getMessage(), e); | |||
return R.error(ErrorCodeEnum.BIND_EXCEPTION.getValue(),ErrorCodeEnum.BIND_EXCEPTION.getDescription()); | |||
} | |||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class) | |||
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) | |||
public R processHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { | |||
logger.error(e.getMessage(), e); | |||
return R.error(ErrorCodeEnum.METHOD_NOT_ALLOWED_EXCEPTION.getValue(),ErrorCodeEnum.METHOD_NOT_ALLOWED_EXCEPTION.getDescription()); | |||
} | |||
} |
@@ -0,0 +1,19 @@ | |||
package com.imitate.common.annotation; | |||
import java.lang.annotation.*; | |||
/** | |||
* 公开性自定义注解 | |||
* @author yanchao | |||
*/ | |||
@Inherited | |||
@Target({ElementType.METHOD, ElementType.TYPE}) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Documented | |||
public @interface PublicUrl { | |||
/** | |||
* 是否校验签名合法性 | |||
* @return | |||
*/ | |||
boolean signValidate() default false; | |||
} |
@@ -0,0 +1,61 @@ | |||
package com.imitate.common.bean; | |||
import com.imitate.common.constant.ApiResultCsts; | |||
public class ApiResult<T> { | |||
private int code = ApiResultCsts.CODE_SUCCESS; | |||
private String msg; | |||
private T data; | |||
public int getCode() { | |||
return code; | |||
} | |||
public void setCode(int code) { | |||
this.code = code; | |||
} | |||
public String getMsg() { | |||
return msg; | |||
} | |||
public void setMsg(String msg) { | |||
this.msg = msg; | |||
} | |||
public T getData() { | |||
return data; | |||
} | |||
public void setData(T data) { | |||
this.data = data; | |||
} | |||
public ApiResult() { | |||
} | |||
public ApiResult(int code, String msg, T data) { | |||
this.code = code; | |||
this.msg = msg; | |||
this.data = data; | |||
} | |||
public static <T> ApiResult<T> successResult(T data) { | |||
return successResult("success", data); | |||
} | |||
public static <T> ApiResult<T> successResult(String msg, T data) { | |||
return new ApiResult<>(0, msg, data); | |||
} | |||
public static <T> ApiResult<T> failResult(int code, String msg, T data) { | |||
return new ApiResult<>(code, msg, data); | |||
} | |||
public static <T> ApiResult<T> failResult(int code, String msg) { | |||
return new ApiResult<>(code, msg, null); | |||
} | |||
} |
@@ -0,0 +1,44 @@ | |||
/** | |||
* 文件名 : BeanFactory.java | |||
* 版权 : <版权/公司名> | |||
* 描述 : <描述> | |||
* @author liliy | |||
* 版本 : <版本> | |||
* 修改时间: 2017年6月13日 | |||
* 修改内容: <修改内容> | |||
*/ | |||
package com.imitate.common.bean; | |||
import org.springframework.beans.BeansException; | |||
import org.springframework.context.ApplicationContext; | |||
import org.springframework.context.ApplicationContextAware; | |||
import org.springframework.stereotype.Component; | |||
/** | |||
* <一句话功能简述> <功能详细描述> | |||
* | |||
* @author liliy | |||
* @version [版本号,2017年6月13日] | |||
* @see [相关类/方法] | |||
* @since [产品/模块版本] | |||
*/ | |||
@Component | |||
public class BeanFactory implements ApplicationContextAware { | |||
private static ApplicationContext ctx = null; | |||
public static Object getObejct(String name) { | |||
return ctx.getBean(name); | |||
} | |||
public static <T> T getObejct(Class<T> requiredType) { | |||
return ctx.getBean(requiredType); | |||
} | |||
@Override | |||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { | |||
BeanFactory.ctx = applicationContext; | |||
} | |||
} |
@@ -0,0 +1,53 @@ | |||
package com.imitate.common.bean; | |||
import com.github.pagehelper.Page; | |||
import java.util.List; | |||
public class BridgePage<T> { | |||
private List<T> data; | |||
private Integer pageNum; | |||
private Integer pageSize; | |||
private Long total; | |||
public BridgePage(Page<T> page) { | |||
this.data = page.getResult(); | |||
this.pageNum = page.getPageNum(); | |||
this.pageSize = page.getPageSize(); | |||
this.total = page.getTotal(); | |||
} | |||
public List<T> getData() { | |||
return data; | |||
} | |||
public void setData(List<T> data) { | |||
this.data = data; | |||
} | |||
public Integer getPageNum() { | |||
return pageNum; | |||
} | |||
public void setPageNum(Integer pageNum) { | |||
this.pageNum = pageNum; | |||
} | |||
public Integer getPageSize() { | |||
return pageSize; | |||
} | |||
public void setPageSize(Integer pageSize) { | |||
this.pageSize = pageSize; | |||
} | |||
public Long getTotal() { | |||
return total; | |||
} | |||
public void setTotal(Long total) { | |||
this.total = total; | |||
} | |||
} |
@@ -0,0 +1,48 @@ | |||
package com.imitate.common.bean; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
/** | |||
* shell执行结果 | |||
* | |||
* @author 威少 | |||
*/ | |||
@Data | |||
@NoArgsConstructor | |||
@AllArgsConstructor | |||
public class ShellResult { | |||
/** | |||
* 退出码 | |||
*/ | |||
private Integer exitStatus; | |||
/** | |||
* 实际输出 | |||
*/ | |||
private String out; | |||
public enum ExitStatus { | |||
/** | |||
* 成功 | |||
*/ | |||
SUCCESS(0), | |||
/** | |||
* 超时 | |||
*/ | |||
TIMEOUT(124), | |||
/** | |||
* 默认失败 | |||
*/ | |||
FAIL(-1); | |||
private int code; | |||
ExitStatus(int code) { | |||
this.code = code; | |||
} | |||
public int getCode() { | |||
return code; | |||
} | |||
} | |||
} |
@@ -0,0 +1,141 @@ | |||
package com.imitate.common.config; | |||
import com.fasterxml.jackson.core.JsonGenerator; | |||
import com.fasterxml.jackson.core.JsonParser; | |||
import com.fasterxml.jackson.core.JsonProcessingException; | |||
import com.fasterxml.jackson.databind.*; | |||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; | |||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; | |||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; | |||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; | |||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; | |||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; | |||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.core.convert.converter.Converter; | |||
import java.io.IOException; | |||
import java.text.ParseException; | |||
import java.text.SimpleDateFormat; | |||
import java.time.LocalDate; | |||
import java.time.LocalDateTime; | |||
import java.time.LocalTime; | |||
import java.time.format.DateTimeFormatter; | |||
import java.util.Date; | |||
@Configuration | |||
public class DateConfig { | |||
/** 默认日期时间格式 */ | |||
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; | |||
/** 默认日期格式 */ | |||
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd"; | |||
/** 默认时间格式 */ | |||
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss"; | |||
/** | |||
* LocalDate转换器,用于转换RequestParam和PathVariable参数 | |||
*/ | |||
@Bean | |||
public Converter<String, LocalDate> localDateConverter() { | |||
return new Converter<String, LocalDate>() { | |||
@Override | |||
public LocalDate convert(String source) { | |||
return LocalDate.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)); | |||
} | |||
}; | |||
} | |||
/** | |||
* LocalDateTime转换器,用于转换RequestParam和PathVariable参数 | |||
*/ | |||
@Bean | |||
public Converter<String, LocalDateTime> localDateTimeConverter() { | |||
return new Converter<String, LocalDateTime>() { | |||
@Override | |||
public LocalDateTime convert(String source) { | |||
return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)); | |||
} | |||
}; | |||
} | |||
/** | |||
* LocalTime转换器,用于转换RequestParam和PathVariable参数 | |||
*/ | |||
@Bean | |||
public Converter<String, LocalTime> localTimeConverter() { | |||
return new Converter<String, LocalTime>() { | |||
@Override | |||
public LocalTime convert(String source) { | |||
return LocalTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)); | |||
} | |||
}; | |||
} | |||
/** | |||
* Date转换器,用于转换RequestParam和PathVariable参数 | |||
*/ | |||
@Bean | |||
public Converter<String, Date> dateConverter() { | |||
return new Converter<String, Date>() { | |||
@Override | |||
public Date convert(String source) { | |||
SimpleDateFormat format = new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT); | |||
try { | |||
return format.parse(source); | |||
} catch (ParseException e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
}; | |||
} | |||
/** | |||
* Json序列化和反序列化转换器,用于转换Post请求体中的json以及将我们的对象序列化为返回响应的json | |||
*/ | |||
@Bean("objectMapper") | |||
public ObjectMapper objectMapper(){ | |||
ObjectMapper objectMapper = new ObjectMapper(); | |||
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); | |||
objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); | |||
//LocalDateTime系列序列化和反序列化模块,继承自jsr310,我们在这里修改了日期格式 | |||
JavaTimeModule javaTimeModule = new JavaTimeModule(); | |||
javaTimeModule.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))); | |||
javaTimeModule.addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))); | |||
javaTimeModule.addSerializer(LocalTime.class,new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))); | |||
javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))); | |||
javaTimeModule.addDeserializer(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))); | |||
javaTimeModule.addDeserializer(LocalTime.class,new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))); | |||
//Date序列化和反序列化 | |||
javaTimeModule.addSerializer(Date.class, new JsonSerializer<Date>() { | |||
@Override | |||
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { | |||
SimpleDateFormat formatter = new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT); | |||
String formattedDate = formatter.format(date); | |||
jsonGenerator.writeString(formattedDate); | |||
} | |||
}); | |||
javaTimeModule.addDeserializer(Date.class, new JsonDeserializer<Date>() { | |||
@Override | |||
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { | |||
SimpleDateFormat format = new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT); | |||
String date = jsonParser.getText(); | |||
try { | |||
return format.parse(date); | |||
} catch (ParseException e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
}); | |||
objectMapper.registerModule(javaTimeModule); | |||
return objectMapper; | |||
} | |||
} |
@@ -0,0 +1,36 @@ | |||
package com.imitate.common.config; | |||
import com.imitate.common.util.RedisPool; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.boot.context.event.ApplicationReadyEvent; | |||
import org.springframework.context.ApplicationListener; | |||
import org.springframework.stereotype.Component; | |||
import redis.clients.jedis.Jedis; | |||
@Component | |||
public class InitListener implements ApplicationListener<ApplicationReadyEvent> { | |||
private static final Logger log = LoggerFactory.getLogger(InitListener.class); | |||
@Override | |||
public void onApplicationEvent(ApplicationReadyEvent event) { | |||
String active = System.getProperty("spring.profiles.active"); | |||
// 本地不初始化这些信息 | |||
Jedis jedis = null; | |||
try { | |||
if (!StringUtils.equals(active, "local")) { | |||
// 初始化redis | |||
jedis = RedisPool.getJedis(); | |||
} | |||
} catch (Exception e) { | |||
log.error("项目启动失败", e); | |||
if ("jedisPool初始化错误".equals(e.getMessage())) { | |||
Runtime.getRuntime().exit(-1); | |||
} | |||
} finally { | |||
RedisPool.returnResource(jedis); | |||
} | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
package com.imitate.common.config; | |||
import com.zaxxer.hikari.HikariDataSource; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.core.env.Environment; | |||
import org.springframework.jdbc.core.JdbcTemplate; | |||
import org.springframework.stereotype.Component; | |||
/** | |||
* @author yanchao | |||
*/ | |||
@Component | |||
public class JdbcConfig { | |||
@Autowired | |||
private Environment environment; | |||
private static final String HOST = "com.mysql.cj.jdbc.Driver"; | |||
private static final String URL = "jdbc:mysql://rm-bp1ht3504joktie83.mysql.rds.aliyuncs.com:3306/educoderweb?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false"; | |||
private static final String USERNAME = "readonly"; | |||
private static final String PASSWORD = "readonly_20210901"; | |||
@Bean | |||
public JdbcTemplate jdbcTemplate(){ | |||
HikariDataSource dataSource = new HikariDataSource(); | |||
dataSource.setDriverClassName(HOST); | |||
dataSource.setJdbcUrl(URL); | |||
dataSource.setUsername(USERNAME); | |||
dataSource.setPassword(PASSWORD); | |||
return new JdbcTemplate(dataSource); | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
package com.imitate.common.config; | |||
import org.springframework.context.annotation.Configuration; | |||
import tk.mybatis.spring.annotation.MapperScan; | |||
@Configuration | |||
public class MybatisConfig { | |||
} |
@@ -0,0 +1,21 @@ | |||
package com.imitate.common.config; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.core.annotation.Order; | |||
import org.springframework.data.redis.connection.RedisConnectionFactory; | |||
import org.springframework.data.redis.listener.RedisMessageListenerContainer; | |||
@Order(1) | |||
@Configuration | |||
public class RedisListenerConfig { | |||
@Bean | |||
RedisMessageListenerContainer listenerContainer(RedisConnectionFactory connectionFactory) { | |||
RedisMessageListenerContainer container = new RedisMessageListenerContainer(); | |||
container.setConnectionFactory(connectionFactory); | |||
return container; | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
/* | |||
* Copyright 2017-2018 Educoder Group. | |||
*/ | |||
package com.imitate.common.config; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.stereotype.Service; | |||
/** | |||
* 未经捕获的异常处理 | |||
* @author weishao | |||
*/ | |||
@Service | |||
public class UncaughtBridgeExceptionHandler implements Thread.UncaughtExceptionHandler { | |||
private static Logger logger = LoggerFactory.getLogger(UncaughtBridgeExceptionHandler.class); | |||
@Override | |||
public void uncaughtException(Thread t, Throwable e) { | |||
// 此处只处理善后异常,其它已经在善后处理中解决 | |||
} | |||
} |
@@ -0,0 +1,63 @@ | |||
package com.imitate.common.config; | |||
import com.fasterxml.jackson.databind.ObjectMapper; | |||
import com.fasterxml.jackson.databind.module.SimpleModule; | |||
import com.imitate.common.interceptor.SignInterceptor; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.core.annotation.Order; | |||
import org.springframework.http.converter.HttpMessageConverter; | |||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; | |||
import org.springframework.web.method.support.HandlerMethodArgumentResolver; | |||
import org.springframework.web.servlet.HandlerInterceptor; | |||
import org.springframework.web.servlet.config.annotation.CorsRegistry; | |||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | |||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; | |||
import java.util.List; | |||
/** | |||
* | |||
* @author yanchao | |||
*/ | |||
@Configuration | |||
@Order(1) | |||
@Slf4j | |||
public class WebMvcConfig extends WebMvcConfigurationSupport { | |||
@Override | |||
public void addInterceptors(InterceptorRegistry registry) { | |||
//签名拦截器 | |||
registry.addInterceptor(getHandlerInterceptor()); | |||
} | |||
@Override | |||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { | |||
log.debug("【配置argumentResolver】ok"); | |||
} | |||
@Override | |||
public void addCorsMappings(CorsRegistry registry) { | |||
registry.addMapping("/**") | |||
.allowedHeaders("Content-Type", "x-requested-with", "X-Custom-Header") | |||
.allowedOriginPatterns("*") | |||
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS") | |||
.allowCredentials(true) | |||
.maxAge(3600); | |||
} | |||
@Bean | |||
public HandlerInterceptor getHandlerInterceptor() { | |||
return new SignInterceptor(); | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
package com.imitate.common.constant; | |||
public interface ApiResultCsts { | |||
int CODE_SUCCESS = 0; | |||
int CODE_FAIL = -1; | |||
} |
@@ -0,0 +1,76 @@ | |||
package com.imitate.common.constant; | |||
public interface BuildResultCsts { | |||
/** | |||
* 完整结果正则。 | |||
* | |||
*/ | |||
// \{ 匹配 { , [\s]* 匹配0-多个空白字符, "compileResult"[\s]*:匹配"compileResult": | |||
// [\s\S]*匹配0-多个任意字符, [\s\S]*"out"[\s]*:匹配"out": , [^}]*匹配除}之外的所有字符 | |||
String FULL_RESULT_REG = "\\{[\\s]*\"compileResult\"[\\s]*:[\\s\\S]*\"out\"[\\s]*:[^}]*}"; | |||
String TIMEOUT_RES_USAGE = "\\{[\\s]*\"exitStatus\"[\\s]*:[\\s\\S]*\"evaCpuUsage\"[\\s]*:[\\s\\S]*\"nodeLoadAvg\"[\\s]*:[^}]*}"; | |||
String SYS_BUSY_TIP = "系统繁忙,请稍后重试"; | |||
String SYS_BUSY_BASE64 = "57O757uf57mB5b-Z77yM6K-356iN5ZCO6YeN6K-V"; | |||
String EVA_UNDEFINED_ERROR_TIP = "程序执行失败导致评测提前终止,请稍后重试或联系系统管理员。"; | |||
String EVA_UNDEFINED_ERROR_BASE64 = "56iL5bqP5omn6KGM5aSx6LSl5a-86Ie06K-E5rWL5o-Q5YmN57uI5q2i77yM6K-356iN5ZCO6YeN6K-V5oiW6IGU57O757O757uf566h55CG5ZGY44CC"; | |||
String EVA_UNPUBLISHED_SCRIPT_ERROR_TIP = "评测脚本设置异常,建议您在实训的配置页面重新选择或修改评测脚本。"; | |||
String EVA_UNPUBLISHED_SCRIPT_ERROR_BASE64 = "6K-E5rWL6ISa5pys6K6-572u5byC5bi477yM5bu66K6u5oKo5Zyo5a6e6K6t55qE6YWN572u6aG16Z2i6YeN5paw6YCJ5oup5oiW5L-u5pS56K-E5rWL6ISa5pys44CC"; | |||
String EVA_PUBLISHED_SCRIPT_ERROR_TIP = "评测脚本设置异常,请联系老师在实训的配置页面重新选择或修改评测脚本。"; | |||
String EVA_PUBLISHED_SCRIPT_ERROR_BASE64 = "6K-E5rWL6ISa5pys6K6-572u5byC5bi477yM6K-36IGU57O76ICB5biI5Zyo5a6e6K6t55qE6YWN572u6aG16Z2i6YeN5paw6YCJ5oup5oiW5L-u5pS56K-E5rWL6ISa5pys44CC"; | |||
// String EVA_RETRY_ERROR_TIP = "本实训有严格的资源限制,当前账号对资源占有级别较低,请10秒后重试。"; | |||
// String EVA_RETRY_ERROR_BASE64 = "5pys5a6e6K6t5pyJ5Lil5qC855qE6LWE5rqQ6ZmQ5Yi277yM5b2T5YmN6LSm5Y-35a-56LWE5rqQ5Y2g5pyJ57qn5Yir6L6D5L2O77yM6K-3MTDnp5LlkI7ph43or5XjgII"; | |||
String EVA_RETRY_ERROR_TIP = "本次评测网络延迟较高,资源无法正常加载,请10s后重试。"; | |||
String EVA_RETRY_ERROR_BASE64 = "5pys5qyh6K-E5rWL572R57uc5bu26L-f6L6D6auY77yM6LWE5rqQ5peg5rOV5q2j5bi45Yqg6L2977yM6K-3MTBz5ZCO6YeN6K-V44CC"; | |||
String EVA_WINDOWS_ERROR_TIP = "实验环境存在问题或已被回收,请保存数据再重置实训重新评测。"; | |||
String EVA_VIRTUAL_NOT_READY_TIP = "环境尚未初始化完成,请稍后重试。"; | |||
String POD_HAVE_BEEN_CLEANED_TIP = "当前资源因未持续使用正在回收中,稍后重试即可。"; | |||
String POD_HAVE_BEEN_CLEANED_BASE64 = "5b2T5YmN6LWE5rqQ5Zug5pyq5oyB57ut5L2_55So5q2j5Zyo5Zue5pS25Lit77yM56iN5ZCO6YeN6K-V5Y2z5Y-v44CC"; | |||
String REDO_EVALUATING_FAIL_TIP = "当前账号两次评测间歇时间较短,请稍后重试!"; | |||
String REDO_EVALUATING_FAIL_BASE64 = "5b2T5YmN6LSm5Y-35Lik5qyh6K-E5rWL6Ze05q2H5pe26Ze06L6D55-t77yM6K-356iN5ZCO6YeN6K-V77yB"; | |||
String EVA_OVER_MEMORY_LIMIT_TIP = "当前运行程序超过系统最大空间限制,请检查程序是否存在死循环或内存泄漏等问题!"; | |||
String EVA_OVER_MEMORY_LIMIT_BASE64 = "5b2T5YmN6L+Q6KGM56iL5bqP6LaF6L+H57O757uf5pyA5aSn5YaF5a2Y6ZmQ5Yi277yM6K+35qOA5p+l56iL5bqP5piv5ZCm5a2Y5Zyo5q275b6q546v5oiW5YaF5a2Y5rOE5ryP562J6Zeu6aKY77yB"; | |||
String EVA_OVER_MEMORY_LIMIT_BASE64_REENCODE = "5b2T5YmN6L-Q6KGM56iL5bqP6LaF6L-H57O757uf5pyA5aSn5YaF5a2Y6ZmQ5Yi277yM56iL5bqP5Y-v6IO95a2Y5Zyo5q275b6q546v5oiW6ICF5YaF5a2Y5rOE5ryP562J6Zeu6aKY77yBDQo"; | |||
String PULL_IMAGE_FAIL = "当前实验环境正在更新中,请稍后重试或联系系统管理员!"; | |||
String CLONE_CODE_FAIL = "当前网络较差,代码下载超时,请稍后重试!"; | |||
String CLONE_CODE_FAIL_BASE64_REENCODE = "5b2T5YmN572R57uc6L6D5beu77yM5Luj56CB5LiL6L296LaF5pe277yM6K-356iN5ZCO6YeN6K-V77yB"; | |||
String RES_SCALE_TIP = "当前实验使用的用户较多,系统正在智能化为您调度更优质的资源,预计一分钟内完成,请稍后重试!"; | |||
String RES_SCALE_BASE64 = "5b2T5YmN5a6e6aqM5L2_55So55qE55So5oi36L6D5aSa77yM57O757uf5q2j5Zyo5pm66IO95YyW5Li65oKo6LCD5bqm5pu05LyY6LSo55qE6LWE5rqQ77yM6aKE6K6h5LiA5YiG6ZKf5YaF5a6M5oiQ77yM6K-356iN5ZCO6YeN6K-V77yB"; | |||
String OUTPUT_TOO_LONG_ERROR = "cannot allocate"; | |||
String INPUT_TOO_LONG_ERROR = "argument list too long"; | |||
String OUTPUT_TOO_LONG_TIP = "评测输出结果过长,请检查代码逻辑"; | |||
String INPUT_TOO_LONG_TIP = "评测测试用例过长,请采用文件测试用例形式"; | |||
String COMPILE_SUCCESS_TIP = "compile successfully"; | |||
String COMPILE_SUCCESS_BASE64 = "Y29tcGlsZSBzdWNjZXNzZnVsbHk"; | |||
String DOWNLOAD_STATUS = "downloadStatus"; | |||
String DOWNLOAD_STATUS_SUCCESS = "1"; | |||
String DOWNLOAD_STATUS_FAIL = "0"; | |||
String CREATE_POD_STATUS = "createPodStatus"; | |||
String CREATE_POD_STATUS_SUCCESS = "1"; | |||
String CREATE_POD_STATUS_FAIL = "0"; | |||
String COMPILE_SUCCESS_YES = "1"; | |||
String COMPILE_SUCCESS_NO = "0"; | |||
String TEXT_MSG_SERVICE_START = "服务启动中..."; | |||
String TEXT_MSG_SERVICE_SUCCESS = "服务启动完成"; | |||
String TEXT_MSG_REDO_EVA = "重复评测中..."; | |||
} |
@@ -0,0 +1,217 @@ | |||
package com.imitate.common.constant; | |||
public interface TpCsts { | |||
String TPI_ID = "tpiID"; | |||
/** | |||
* 类型 | |||
*/ | |||
String TYPE_EVALUATE = "evaluate"; | |||
String TYPE_EVASSH = "evassh"; | |||
String TYPE_WEBSSH = "webssh"; | |||
String TYPE_VNC = "vnc"; | |||
String TYPE_JUPYTER = "jupyter"; | |||
String TYPE_VSCODE = "vscode"; | |||
String TYPE_WEB = "web"; | |||
int POD_TYPE_EVALUATE = 0; | |||
int POD_TYPE_WEBSSH = 1; | |||
int POD_TYPE_EVASSH = 2; | |||
int POD_TYPE_VNC = 3; | |||
int POD_TYPE_JUPYTER = 4; | |||
int POD_TYPE_VSCODE = 5; | |||
int POD_TYPE_WEB = 6; | |||
/** | |||
* 全部可评测实训clone到tpiWorkspace, 都命名为此 | |||
*/ | |||
String TP_UNIFY_REPO_NAME = "myshixun"; | |||
String TP_RASPBERRY_REPO_SCRIPT = "raspberry"; | |||
String TP_USERFILES_NAME = "userfiles"; | |||
/** | |||
* pod 分配对node的要求 | |||
*/ | |||
String NODE_REQUIRE = "nodeRequire"; | |||
String NODE_TYPE = "type"; | |||
String NODE_TYPE_OTHERS = "others"; | |||
String NODE_TYPE_GPU = "Python-GPU"; | |||
/** | |||
* shenlong节点 | |||
*/ | |||
String SHENLONG_NODE_LABEL_KEY = "shenlong"; | |||
String SHENLONG_NODE_LABEL_VALUE = "true"; | |||
/** | |||
* GPU节点 | |||
*/ | |||
String GPU_NODE_LABEL_KEY = "GPU"; | |||
String GPU_NODE_LABEL_VALUE = "true"; | |||
/** | |||
* oj pod 和 node | |||
*/ | |||
String OJ_LABEL_KEY = "oj"; | |||
String OJ_LABEL_VALUE = "true"; | |||
String OJ_CAPACITY_NODE_LABEL_KEY = "capacity"; | |||
/** | |||
* OJ 平台类型 | |||
*/ | |||
String PLATFORM = "platform"; | |||
/** | |||
* tpi评测需要用到的平台资源,在pod中的挂载路径 | |||
*/ | |||
String TPI_PLATFORM_MOUNT_PATH = "/data/platform/eva"; | |||
String VNC_POD_DEV_SHM_PATH = "/dev/shm"; | |||
/** | |||
* tpi保护空间的挂载路径,存放评测执行脚本等 | |||
*/ | |||
String TPI_PROTECT_SPACE_MOUNT_PATH = "/data/protectspace"; | |||
/** | |||
* tpi工作空间的挂载路径 | |||
*/ | |||
String TPI_WORKSPACE_MOUNT_PATH = "/data/workspace"; | |||
/** | |||
* 评测分步输出消息匹配格式 | |||
*/ | |||
String EVA_STEP_OUT_MSG_PREFIX = "{\"step\":"; | |||
String EVA_STEP_OUT_MSG_SUFFIX = "}"; | |||
/** | |||
* 超时编码:不确定 | |||
*/ | |||
int TIMEOUT_CODE_UNSURE = 1; | |||
/** | |||
* 超时编码:死循环 | |||
*/ | |||
int TIMEOUT_CODE_DEAD_LOOP = 2; | |||
/** | |||
* 超时编码:阻塞。(1)读取输入阻塞,(2)网络编程端口阻塞,(3)多线程阻塞。 | |||
*/ | |||
int TIMEOUT_CODE_BLOCKING = 3; | |||
/** | |||
* 超时编码:节点繁忙 | |||
*/ | |||
int TIMEOUT_CODE_NODE_BUSY = 4; | |||
/** | |||
* 主题:pod 调度 | |||
*/ | |||
String TOPIC_POD_SCHEDULE = "pod_schedule"; | |||
/** | |||
* 主题:评测 | |||
*/ | |||
String TOPIC_EVALUATING = "evaluating"; | |||
String POD_SCHEDULE_CLUSTER_REDIS_KEY = "pod_schedule_cluster_"; | |||
long POD_SCHEDULE_CLUSTER_REDIS_EXPIRE_TIME = 5000; | |||
String STATEFUL_POD_CLUSTER_RECORD_REDIS_KEY = "stateful_pod_schedule_"; | |||
long STATEFUL_POD_CLUSTER_RECORD_EXPIRE_TIME = 2 * 24 * 60 * 60 * 1000; // 2天 | |||
/** | |||
* 评测开始存入redis,评测结束则删除。存在key,表明评测正在进行,value为true表示为重复评测。 | |||
*/ | |||
String REDO_EVALUATING_REDIS_KEY = "redo_evaluating_"; | |||
String REDO_EVALUATING_REDIS_VALUE_TRUE = "true"; | |||
String REDO_EVALUATING_REDIS_VALUE_FALSE = "false"; | |||
String RESET_LOCAL_REDIS_KEY = "reset_local_"; | |||
String CREATE_IMAGE_MAP = "create_image"; | |||
String CREATE_IMAGE_NAME_REDIS_KEY = "create_image_name_"; | |||
Long CREATE_IMAGE_NAME_REDIS_EXPIRE_TIME = 30 * 24 * 60 * 60 * 1000L; // 30天 | |||
String CREATE_IMAGE_TPI_ID_REDIS_KEY = "create_image_tpi_id_"; | |||
long CREATE_IMAGE_TPI_ID_REDIS_EXPIRE_TIME = 60 * 1000L; // 1分钟 | |||
long NODE_IMAGE_STATUS_EXPIRE_TIME = 30 * 60 * 1000L; // 30分钟 | |||
String K8S_NODE_STATUS_READY = "Ready"; | |||
String K8S_NODE_STATUS_NOT_READY = "NotReady"; | |||
String K8S_NODE_STATUS_LABEL_VALUE = "status"; | |||
String K8S_ELASTIC_NODE_LABEL_KEY = "tx"; | |||
String K8S_ELASTIC_NODE_LABEL_VALUE = "normal"; | |||
String K8S_NEW_CAPACITY_NODE_LABEL_KEY = "newCapacity"; | |||
String K8S_NEW_CAPACITY_NODE_LABEL_VALUE = "true"; | |||
String K8S_NEW_CAPACITY_NODE_REDIS_KEY = "k8s_new_capacity_node_key"; | |||
String K8S_NODE_PRE_REDUCE_LABEL_KEY = "pre_reduce"; | |||
String K8S_NODE_PRE_REDUCE_LABEL_VALUE = "true"; | |||
String K8S_PRE_REDUCE_NODE_REDIS_KEY = "k8s_pre_reduce_node_key"; | |||
String CLOUD_HOST_TYPE_WINDOWS = "windows"; | |||
String PROXMOX_HOST_TYPE_VIRTUAL = "virtual"; | |||
String CLOUD_HOST_TYPE_LINUX = "linux"; | |||
Integer NODE_CPU_USAGE_UNKNOWN = -1; | |||
String JUPYTER_TYPE_LAB = "lab"; | |||
String JUPYTER_TYPE_NOTEBOOK = "notebook"; | |||
String IGNORED = "ignored"; | |||
/** | |||
* 用户实时运行输出位置 | |||
*/ | |||
String USER_OUTPUT_FILE = "user.out"; | |||
/** | |||
* 用户case输出分隔符 | |||
*/ | |||
String CASE_OUTPUT_SEPARATOR = "\\x1b\\x09\\x1d"; | |||
/** | |||
* 本地集群 | |||
*/ | |||
String LOCAL_CLUSTER = "local"; | |||
/** | |||
* 默认工作空间 | |||
*/ | |||
String DEFAULT_NAMESPACE = "default"; | |||
/** | |||
* 挂载点名称 | |||
*/ | |||
String WORKSPACE = "workspace"; | |||
String TEST_CASE_DIR = "test-case-dir"; | |||
String TEST_CASE_ACTUAL_OUT_DIR = "test-case-actual-out-dir"; | |||
String PROTECT_DIR = "protect"; | |||
/** | |||
* 仅运行结果前缀 | |||
*/ | |||
String RUN_ONLY_RESULT_KEY_PREFIX = "runOnlyResult"; | |||
} |
@@ -0,0 +1,53 @@ | |||
package com.imitate.common.enums; | |||
/** | |||
* 通用状态枚举 0为否定含义,1为肯定含义 | |||
* @author 威少 | |||
*/ | |||
public enum CommonStateEnum { | |||
/** | |||
* 否定含义 | |||
*/ | |||
FALSE(0, false), | |||
/** | |||
* 肯定含义 | |||
*/ | |||
TRUE(1, true); | |||
int value; | |||
boolean booleanValue; | |||
CommonStateEnum(int value, boolean booleanValue) { | |||
this.value = value; | |||
this.booleanValue = booleanValue; | |||
} | |||
public int getValue() { | |||
return value; | |||
} | |||
public boolean getBooleanValue() { | |||
return booleanValue; | |||
} | |||
/** | |||
* 从bool值转换 | |||
* @param value bool值 | |||
* @return 肯定为1,否定为0 | |||
*/ | |||
public static CommonStateEnum fromBoolean(Boolean value) { | |||
return value == null ? FALSE : value ? TRUE : FALSE; | |||
} | |||
/** | |||
* 从整型值转换 | |||
* @param value 整型值 | |||
* @return 肯定为1,否定为0 | |||
*/ | |||
public static CommonStateEnum fromInteger(Integer value) { | |||
return value == null || value == FALSE.getValue() ? FALSE : TRUE; | |||
} | |||
} |
@@ -0,0 +1,60 @@ | |||
package com.imitate.common.enums; | |||
import java.util.Arrays; | |||
/** | |||
* 全局错误枚举 | |||
* @author yanchao | |||
*/ | |||
public enum ErrorCodeEnum { | |||
SUCCESS("000000","成功"), | |||
INVALID_ARG_EXCEPTION("000001","参数验证异常"), | |||
BIND_EXCEPTION("000007","参数绑定异常"), | |||
MVC_BIND_EXCEPTION("000014","请求参数绑定异常"), | |||
METHOD_NOT_ALLOWED_EXCEPTION("000008","请求方式异常"), | |||
EXCEPTION("999999","系统异常"), | |||
DATAFLOW_EXCEPTION("000010","默认数据异常"), | |||
BUSINESS_EXCEPTION("000011","默认业务异常"), | |||
LOGIN_EXPIRE_TIME("000013","登录已过期,请重新登录"), | |||
USER_LOGIN_DISABLE("000014","账号已经锁定,请联系管理员"), | |||
NO_AUTH("000012","未认证登录状态"), | |||
AFTERMATH_EXP("000013","评测线程出错,善后处理发生异常"), | |||
GIT_FAIL("000014","获取git凭证失败: "), | |||
GIT_CREDENTIAL_FAIL("000015","设置git凭证失败:"), | |||
CLONE_FAIL("100001","克隆失败"), | |||
EVALUATION_SHELL_FAIL("100002","生成评测脚本失败"), | |||
WRITE_FILE_CODE_FAIL("100003","写代码文件失败"), | |||
UPDATE_VERSION_REPOSITORY_FAIL("100004","更新版本库失败"), | |||
SYNC_CLUSTER_VERSION_REPOSITORY_FAIL("100005","远程集群版本库同步失败"), | |||
PUSH_FAIL("100006","push版本库失败"), | |||
JUPYTER_ADD_OR_COMMIT_FAIL("100007","Jupyter添加提交失败"), | |||
VERSION_REPOSITORY_NOT_EXIST("10000","主机名不存在"); | |||
String value; | |||
String description; | |||
ErrorCodeEnum(String value, String description) { | |||
this.value = value; | |||
this.description = description; | |||
} | |||
public String getValue() { | |||
return value; | |||
} | |||
public String getDescription() { | |||
return description; | |||
} | |||
public static ErrorCodeEnum getByDescription(String description) { | |||
return Arrays.stream(values()).filter(errorCodeEnum -> errorCodeEnum.getDescription().equals(description)).findFirst().orElse(null); | |||
} | |||
} | |||
@@ -0,0 +1,36 @@ | |||
package com.imitate.common.exception; | |||
import com.imitate.common.enums.ErrorCodeEnum; | |||
/** | |||
* 业务异常 | |||
* @author yanchao | |||
*/ | |||
public class BusinessException extends RuntimeException{ | |||
/** | |||
* 错误码 | |||
*/ | |||
private String errCode = ErrorCodeEnum.DATAFLOW_EXCEPTION.getValue(); | |||
public BusinessException(ErrorCodeEnum errorCodeEnum){ | |||
super(errorCodeEnum.getDescription()); | |||
setErrCode(errorCodeEnum.getValue()); | |||
} | |||
public BusinessException(String code,String msg){ | |||
super(msg); | |||
setErrCode(code); | |||
} | |||
public void setErrCode(String errCode) { | |||
this.errCode = errCode; | |||
} | |||
public String getErrCode() { | |||
return errCode; | |||
} | |||
} | |||
@@ -0,0 +1,117 @@ | |||
package com.imitate.common.interceptor; | |||
import com.imitate.common.util.HttpContextUtil; | |||
import org.apache.commons.io.IOUtils; | |||
import org.springframework.core.annotation.Order; | |||
import org.springframework.stereotype.Component; | |||
import javax.servlet.*; | |||
import javax.servlet.annotation.WebFilter; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletRequestWrapper; | |||
import java.io.*; | |||
import java.nio.charset.StandardCharsets; | |||
/*** | |||
* HttpServletRequest 过滤器 | |||
* 解决: request.getInputStream()只能读取一次的问题 | |||
* 目标: 流可重复读 | |||
* @author yanchao | |||
*/ | |||
@Component | |||
@WebFilter(filterName = "HttpServletRequestFilter", urlPatterns = "/") | |||
@Order(10000) | |||
public class HttpServletRequestFilter implements Filter { | |||
@Override | |||
public void init(FilterConfig filterConfig) throws ServletException { | |||
} | |||
@Override | |||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { | |||
ServletRequest requestWrapper = null; | |||
if(servletRequest instanceof HttpServletRequest) { | |||
requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest); | |||
} | |||
//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中 | |||
// 在chain.doFiler方法中传递新的request对象 | |||
if(null == requestWrapper) { | |||
filterChain.doFilter(servletRequest, servletResponse); | |||
} else { | |||
filterChain.doFilter(requestWrapper, servletResponse); | |||
} | |||
} | |||
@Override | |||
public void destroy() { | |||
} | |||
/*** | |||
* HttpServletRequest 包装器 | |||
* 解决: request.getInputStream()只能读取一次的问题 | |||
* 目标: 流可重复读 | |||
*/ | |||
public static class RequestWrapper extends HttpServletRequestWrapper { | |||
//参数字节数组 | |||
private byte[] requestBody; | |||
//Http请求对象 | |||
private HttpServletRequest request; | |||
public RequestWrapper(HttpServletRequest request) throws IOException { | |||
super(request); | |||
this.request = request; | |||
} | |||
/** | |||
* @return | |||
* @throws IOException | |||
*/ | |||
@Override | |||
public ServletInputStream getInputStream() throws IOException { | |||
/** | |||
* 每次调用此方法时将数据流中的数据读取出来,然后再回填到InputStream之中 | |||
* 解决通过@RequestBody和@RequestParam(POST方式)读取一次后控制器拿不到参数问题 | |||
*/ | |||
if (null == this.requestBody) { | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
IOUtils.copy(request.getInputStream(), baos); | |||
this.requestBody = baos.toByteArray(); | |||
} | |||
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody); | |||
return new ServletInputStream() { | |||
@Override | |||
public boolean isFinished() { | |||
return false; | |||
} | |||
@Override | |||
public boolean isReady() { | |||
return false; | |||
} | |||
@Override | |||
public void setReadListener(ReadListener listener) { | |||
} | |||
@Override | |||
public int read() { | |||
return bais.read(); | |||
} | |||
}; | |||
} | |||
public byte[] getRequestBody() { | |||
return requestBody; | |||
} | |||
@Override | |||
public BufferedReader getReader() throws IOException { | |||
return new BufferedReader(new InputStreamReader(this.getInputStream())); | |||
} | |||
} | |||
} |
@@ -0,0 +1,142 @@ | |||
package com.imitate.common.interceptor; | |||
import cn.hutool.core.util.ObjectUtil; | |||
import com.alibaba.fastjson.JSON; | |||
import com.alibaba.fastjson.JSONObject; | |||
import com.imitate.common.annotation.PublicUrl; | |||
import com.imitate.common.util.SignUtil; | |||
import org.springframework.web.method.HandlerMethod; | |||
import org.springframework.web.servlet.HandlerInterceptor; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import java.io.BufferedReader; | |||
import java.io.IOException; | |||
import java.io.InputStreamReader; | |||
import java.io.PrintWriter; | |||
import java.util.Map; | |||
/** | |||
* 签名认证拦截器 | |||
* @author 悟空 | |||
*/ | |||
public class SignInterceptor implements HandlerInterceptor { | |||
@Override | |||
public boolean preHandle(HttpServletRequest request, | |||
HttpServletResponse response, Object handler) throws Exception { | |||
if(handler instanceof HandlerMethod){ | |||
HandlerMethod handlerMethod = (HandlerMethod) handler; | |||
ValidateResponse validateResponse = new ValidateResponse(true); | |||
PublicUrl publicUrl = handlerMethod.getMethodAnnotation(PublicUrl.class); | |||
if (null != publicUrl) { | |||
if (publicUrl.signValidate()) { | |||
BufferedReader streamReader = new BufferedReader( new InputStreamReader(request.getInputStream(), "UTF-8")); | |||
StringBuilder responseStrBuilder = new StringBuilder(); | |||
String inputStr; | |||
while ((inputStr = streamReader.readLine()) != null) { | |||
responseStrBuilder.append(inputStr); | |||
} | |||
Map<String,String> params = JSON.parseObject(responseStrBuilder.toString(), Map.class); | |||
if(params != null){ | |||
validateResponse = paramSignValidate(params, response); | |||
}else{ | |||
validateResponse = paramSignValidate(request, response); | |||
} | |||
} | |||
} | |||
/*if (!validateResponse.isValidate()) { | |||
JSONObject result = new JSONObject(); | |||
result.put("code","-1"); | |||
result.put("msg","签名认证失败"); | |||
returnJson(response,result.toJSONString()); | |||
return false; | |||
}*/ | |||
} | |||
return true; | |||
} | |||
/** | |||
* 签名校验 | |||
* | |||
* @param request | |||
* @param response | |||
* @return | |||
*/ | |||
private ValidateResponse paramSignValidate(HttpServletRequest request, HttpServletResponse response) { | |||
String sign = request.getParameter("sign"); | |||
Map<String, String[]> map = request.getParameterMap(); | |||
// 将参数按照一定规则获取到sign和前端传过来的sign进行比较,规则自定义需要和前端一致 | |||
String sign1 = SignUtil.signMap(map); | |||
if (ObjectUtil.isEmpty(sign) || !sign.equals(sign1)) { | |||
return new ValidateResponse(false); | |||
} | |||
return new ValidateResponse(true); | |||
} | |||
/** | |||
* 签名校验 | |||
* | |||
* @param | |||
* @param response | |||
* @return | |||
*/ | |||
private ValidateResponse paramSignValidate(Map<String,String> params, HttpServletResponse response) { | |||
String sign = params.get("sign"); | |||
// 将参数按照一定规则获取到sign和前端传过来的sign进行比较,规则自定义需要和前端一致 | |||
String sign1 = SignUtil.signMap(null, params); | |||
if (ObjectUtil.isEmpty(sign) || !sign.equals(sign1)) { | |||
return new ValidateResponse(false); | |||
} | |||
return new ValidateResponse(true); | |||
} | |||
/** | |||
* 校验返回对象 | |||
*/ | |||
private static class ValidateResponse { | |||
private boolean validate; | |||
public ValidateResponse(boolean validate) { | |||
this.validate = validate; | |||
} | |||
public boolean isValidate() { | |||
return validate; | |||
} | |||
} | |||
/** | |||
* 认证失败返回json数据 | |||
* @param response | |||
* @param json | |||
* @throws Exception | |||
*/ | |||
@SuppressWarnings("unused") | |||
private void returnJson(HttpServletResponse response, String json) throws Exception{ | |||
PrintWriter writer = null; | |||
response.setCharacterEncoding("UTF-8"); | |||
response.setContentType("application/json; charset=utf-8"); | |||
try { | |||
writer = response.getWriter(); | |||
writer.print(json); | |||
} catch (IOException e) { | |||
} finally { | |||
if (writer != null) { | |||
writer.close(); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,73 @@ | |||
package com.imitate.common.k8s.bean; | |||
public class BridgeContainer { | |||
private boolean mainContainer; | |||
private String name; | |||
private String image; | |||
private Double cpuRequest; | |||
private Double cpuLimit; | |||
private Double memoryRequest; | |||
private Double memoryLimit; | |||
public boolean isMainContainer() { | |||
return mainContainer; | |||
} | |||
public void setMainContainer(boolean mainContainer) { | |||
this.mainContainer = mainContainer; | |||
} | |||
public String getName() { | |||
return name; | |||
} | |||
public void setName(String name) { | |||
this.name = name; | |||
} | |||
public String getImage() { | |||
return image; | |||
} | |||
public void setImage(String image) { | |||
this.image = image; | |||
} | |||
public Double getCpuRequest() { | |||
return cpuRequest; | |||
} | |||
public void setCpuRequest(Double cpuRequest) { | |||
this.cpuRequest = cpuRequest; | |||
} | |||
public Double getCpuLimit() { | |||
return cpuLimit; | |||
} | |||
public void setCpuLimit(Double cpuLimit) { | |||
this.cpuLimit = cpuLimit; | |||
} | |||
public Double getMemoryRequest() { | |||
return memoryRequest; | |||
} | |||
public void setMemoryRequest(Double memoryRequest) { | |||
this.memoryRequest = memoryRequest; | |||
} | |||
public Double getMemoryLimit() { | |||
return memoryLimit; | |||
} | |||
public void setMemoryLimit(Double memoryLimit) { | |||
this.memoryLimit = memoryLimit; | |||
} | |||
} |
@@ -0,0 +1,47 @@ | |||
package com.imitate.common.k8s.bean; | |||
import java.time.LocalDateTime; | |||
public class BridgeNode { | |||
private String name; | |||
private String ip; | |||
private LocalDateTime createTime; | |||
private Integer podNum; | |||
public String getName() { | |||
return name; | |||
} | |||
public void setName(String name) { | |||
this.name = name; | |||
} | |||
public String getIp() { | |||
return ip; | |||
} | |||
public void setIp(String ip) { | |||
this.ip = ip; | |||
} | |||
public LocalDateTime getCreateTime() { | |||
return createTime; | |||
} | |||
public void setCreateTime(LocalDateTime createTime) { | |||
this.createTime = createTime; | |||
} | |||
public Integer getPodNum() { | |||
return podNum; | |||
} | |||
public void setPodNum(Integer podNum) { | |||
this.podNum = podNum; | |||
} | |||
} |
@@ -0,0 +1,231 @@ | |||
package com.imitate.common.k8s.bean; | |||
import java.util.List; | |||
/** | |||
* 评测结果 | |||
* | |||
* @author liliy | |||
*/ | |||
public class BuildResult { | |||
private String buildID; | |||
private String tpiID; | |||
private String status; | |||
private Integer timeoutCode; | |||
private String outPut; | |||
private List<ExecResultCase> msg; | |||
private String resubmit; | |||
private String compileSuccess; | |||
private String executeSuccess; | |||
private String createPodStatus; | |||
private String downloadStatus; | |||
private String sec_key; | |||
private String showServer; | |||
private String extras; | |||
public boolean isSysBusy() { | |||
return "57O757uf57mB5b-Z77yM6K-356iN5ZCO6YeN6K-V".equals(outPut); | |||
} | |||
public boolean isTimeOut() { | |||
String timeOutMsg = "5Luj56CB6K-E5rWL6LaF5pe277yB"; | |||
for (ExecResultCase c : msg) { | |||
if (c.getOutput().startsWith(timeOutMsg)) { | |||
return Boolean.TRUE; | |||
} | |||
} | |||
return Boolean.FALSE; | |||
} | |||
/** | |||
* 步骤。输出分步输出时的步骤 | |||
*/ | |||
private String step; | |||
/** | |||
* 输出存文本 | |||
*/ | |||
private String textMsg; | |||
public String getSec_key() { | |||
return sec_key; | |||
} | |||
public void setSec_key(String sec_key) { | |||
this.sec_key = sec_key; | |||
} | |||
public String getBuildID() { | |||
return buildID; | |||
} | |||
public void setBuildID(String buildID) { | |||
this.buildID = buildID; | |||
} | |||
public String getTpiID() { | |||
return tpiID; | |||
} | |||
public void setTpiID(String tpiID) { | |||
this.tpiID = tpiID; | |||
} | |||
public String getStatus() { | |||
return status; | |||
} | |||
public void setStatus(String status) { | |||
this.status = status; | |||
} | |||
public String getOutPut() { | |||
return outPut; | |||
} | |||
public void setOutPut(String outPut) { | |||
this.outPut = outPut; | |||
} | |||
public List<ExecResultCase> getMsg() { | |||
return msg; | |||
} | |||
public void setMsg(List<ExecResultCase> msg) { | |||
this.msg = msg; | |||
} | |||
public String getResubmit() { | |||
return resubmit; | |||
} | |||
public void setResubmit(String resubmit) { | |||
this.resubmit = resubmit; | |||
} | |||
public BuildResult(String buildID, String tpiID, String status, String outPut, List<ExecResultCase> msg, String resubmit, | |||
String sec_key, String showServer, String extras) { | |||
this.buildID = buildID; | |||
this.tpiID = tpiID; | |||
this.status = status; | |||
this.outPut = outPut; | |||
this.msg = msg; | |||
this.resubmit = resubmit; | |||
this.sec_key = sec_key; | |||
this.showServer = showServer; | |||
this.extras = extras; | |||
} | |||
public String getCompileSuccess() { | |||
return compileSuccess; | |||
} | |||
public void setCompileSuccess(String compileSuccess) { | |||
this.compileSuccess = compileSuccess; | |||
} | |||
public String getStep() { | |||
return step; | |||
} | |||
public void setStep(String step) { | |||
this.step = step; | |||
} | |||
public String getTextMsg() { | |||
return textMsg; | |||
} | |||
public void setTextMsg(String textMsg) { | |||
this.textMsg = textMsg; | |||
} | |||
public String getCreatePodStatus() { | |||
return createPodStatus; | |||
} | |||
public void setCreatePodStatus(String createPodStatus) { | |||
this.createPodStatus = createPodStatus; | |||
} | |||
public String getDownloadStatus() { | |||
return downloadStatus; | |||
} | |||
public void setDownloadStatus(String downloadStatus) { | |||
this.downloadStatus = downloadStatus; | |||
} | |||
public String getShowServer() { | |||
return showServer; | |||
} | |||
public void setShowServer(String showServer) { | |||
this.showServer = showServer; | |||
} | |||
public Integer getTimeoutCode() { | |||
return timeoutCode; | |||
} | |||
public void setTimeoutCode(Integer timeoutCode) { | |||
this.timeoutCode = timeoutCode; | |||
} | |||
public String getExtras() { | |||
return extras; | |||
} | |||
public void setExtras(String extras) { | |||
this.extras = extras; | |||
} | |||
public String getExecuteSuccess() { | |||
return executeSuccess; | |||
} | |||
public void setExecuteSuccess(String executeSuccess) { | |||
this.executeSuccess = executeSuccess; | |||
} | |||
public enum Status { | |||
/** | |||
* 评测通过 | |||
*/ | |||
PASS("0"), | |||
/** | |||
* 评测不通过 | |||
*/ | |||
FAIL("-1"), | |||
/** | |||
* oj 评测超时 | |||
*/ | |||
RUN_TIMEOUT("2"), | |||
/** | |||
* 创建pod 超时 | |||
*/ | |||
CREATE_POD_FAIL("3"), | |||
/** | |||
* 编译错误 | |||
*/ | |||
COMPILE_FAIL("4"), | |||
/** | |||
* 执行错误,执行用户程序,进程返回-1。如数组越界 | |||
*/ | |||
EXECUTE_ERROR("5"); | |||
private String value; | |||
public String getValue() { | |||
return value; | |||
} | |||
public void setValue(String value) { | |||
this.value = value; | |||
} | |||
Status(String value) { | |||
this.value = value; | |||
} | |||
} | |||
} |
@@ -0,0 +1,384 @@ | |||
package com.imitate.common.k8s.bean; | |||
import com.google.common.util.concurrent.AtomicDouble; | |||
import com.imitate.common.k8s.constant.BridgeNodeCsts; | |||
import com.imitate.common.k8s.util.K8sUtils; | |||
import com.imitate.common.sys.pojo.ClusterConfig; | |||
import io.fabric8.kubernetes.api.model.Node; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
public class ClusterInfo { | |||
/** | |||
* 集群配置 | |||
*/ | |||
private ClusterConfig clusterConfig; | |||
/** | |||
* 是否自动扩容 | |||
*/ | |||
private Boolean autoscale; | |||
/** | |||
* 比重 | |||
*/ | |||
private double weight; | |||
/** | |||
* 集群节点 Map<nodeName, Node> | |||
*/ | |||
private Map<String, Node> nodeMap; | |||
/** | |||
* 集群pod总数 | |||
*/ | |||
private int podCount; | |||
/** | |||
* 集群可分配cpu 总量 | |||
*/ | |||
private double allocatableCpu; | |||
/** | |||
* 集群可分配memory 总量,以M为单位 | |||
*/ | |||
private double allocatableMemory; | |||
/** | |||
* 固定节点数量 | |||
*/ | |||
private int foreverNodeNum; | |||
/** | |||
* 扩容节点数量 | |||
*/ | |||
private int scaleNodeNum; | |||
/** | |||
* 集群已分配的request cpu 总量 | |||
*/ | |||
private double requestCpuSum; | |||
/** | |||
* 集群已分配的request memory 总量 | |||
*/ | |||
private double requestMemorySum; | |||
/** | |||
* 集群已被预订的request cpu 总量。pod已经被分配到此集群,但还没有创建。 | |||
*/ | |||
private AtomicDouble reserveRequestCpuSum = new AtomicDouble(0); | |||
/** | |||
* 集群已被预订的request memory 总量。pod已经被分配到此集群,但还没有创建。 | |||
*/ | |||
private AtomicDouble reserveRequestMemorySum = new AtomicDouble(0); | |||
/** | |||
* 集群剩余可分配的request cpu 总量 | |||
*/ | |||
private double surplusCpuSum; | |||
/** | |||
* 集群剩余可分配的request memory 总量 | |||
*/ | |||
private double surplusMemorySum; | |||
/** | |||
* 集群固定节点可分配cpu 总量 | |||
*/ | |||
private double foreverAllocatableCpuSum; | |||
/** | |||
* 集群固定节点可分配memory 总量 | |||
*/ | |||
private double foreverAllocatableMemorySum; | |||
/** | |||
* 折算固定节点 request cpu比例。把扩容节点上的request cpu折算到固定节点 | |||
*/ | |||
private double conversionForeverNodeRequestCpuRatio; | |||
/** | |||
* 折算固定节点 request memory比例。把扩容节点上的request memory折算到固定节点 | |||
*/ | |||
private double conversionForeverNodeRequestMemoryRatio; | |||
private double requestCpuRatio; | |||
private double requestMemoryRatio; | |||
public void cal(String cluster, List<Node> nodes, List<NodeResStat> nodeResStats) { | |||
Map<String, Node> nodeMap = new HashMap<>(); | |||
for (Node node : nodes) { | |||
nodeMap.put(node.getMetadata().getName(), node); | |||
} | |||
double allocatableCpuCount = 0; | |||
double allocatableMemoryCount = 0; | |||
double foreverNodeAllocatableCpuCount = 0; | |||
double foreverNodeAllocatableMemoryCount = 0; | |||
int foreverNodeNum = 0; | |||
int scaleNodeNum = 0; | |||
for(Node node : nodes) { | |||
String typeVal = K8sUtils.getLabel(node, BridgeNodeCsts.NODE_LABEL_TYPE); | |||
boolean isEvaNode = BridgeNodeCsts.NODE_LABEL_TYPE_OTHERS.equals(typeVal) | |||
|| BridgeNodeCsts.NODE_LABEL_TYPE_UNDER_PRESSURE.equals(typeVal); | |||
if(!isEvaNode || K8sUtils.isOjNode(node)) { // 普通评测节点 | |||
continue; | |||
} | |||
double nodeAllocatableCpu = K8sUtils.getNodeAllocatableCpu(node); | |||
allocatableCpuCount += nodeAllocatableCpu; | |||
double allocatableMemory = K8sUtils.getNodeAllocatableMemory(node); | |||
allocatableMemoryCount += allocatableMemory; | |||
String txVal = K8sUtils.getLabel(node, "tx"); | |||
if("normal".equals(txVal)) { // 扩容节点 | |||
scaleNodeNum ++; | |||
} else { | |||
foreverNodeAllocatableCpuCount += nodeAllocatableCpu; | |||
foreverNodeAllocatableMemoryCount += allocatableMemory; | |||
foreverNodeNum ++; | |||
} | |||
} | |||
double requestCpuSum = 0; | |||
double requestMemorySum = 0; | |||
int podCount = 0; | |||
for (NodeResStat nodeResStat : nodeResStats) { | |||
if (cluster.equals(nodeResStat.getCluster())) { | |||
podCount += nodeResStat.getPodNum(); | |||
requestCpuSum += nodeResStat.getCpuRequest(); | |||
requestMemorySum += nodeResStat.getMemoryRequest(); | |||
} | |||
} | |||
double conversionForeverNodeRequestCpuRatio = requestCpuSum / foreverNodeAllocatableCpuCount * 100; | |||
double conversionForeverNodeRequestMemoryRatio = requestMemorySum / foreverNodeAllocatableMemoryCount * 100; | |||
double surplusCpuSum = allocatableCpuCount - requestCpuSum; | |||
double surplusMemorySum = allocatableMemoryCount - requestMemorySum; | |||
double requestCpuRatio = allocatableCpuCount == 0 ? 100 : (requestCpuSum / allocatableCpuCount * 100); | |||
double requestMemoryRatio = allocatableMemoryCount == 0 ? 100 | |||
: (requestMemorySum / allocatableMemoryCount * 100); | |||
setNodeMap(nodeMap); | |||
setPodCount(podCount); | |||
setAllocatableCpu(allocatableCpuCount); | |||
setAllocatableMemory(allocatableMemoryCount); | |||
setForeverNodeNum(foreverNodeNum); | |||
setScaleNodeNum(scaleNodeNum); | |||
setRequestCpuSum(requestCpuSum); | |||
setRequestMemorySum(requestMemorySum); | |||
setSurplusCpuSum(surplusCpuSum); | |||
setSurplusMemorySum(surplusMemorySum); | |||
setForeverAllocatableCpuSum(foreverNodeAllocatableCpuCount); | |||
setForeverAllocatableMemorySum(foreverNodeAllocatableMemoryCount); | |||
setConversionForeverNodeRequestCpuRatio(conversionForeverNodeRequestCpuRatio); | |||
setConversionForeverNodeRequestMemoryRatio(conversionForeverNodeRequestMemoryRatio); | |||
setRequestCpuRatio(requestCpuRatio); | |||
setRequestMemoryRatio(requestMemoryRatio); | |||
} | |||
public ClusterConfig getClusterConfig() { | |||
return clusterConfig; | |||
} | |||
public void setClusterConfig(ClusterConfig clusterConfig) { | |||
this.clusterConfig = clusterConfig; | |||
} | |||
public Boolean getAutoscale() { | |||
return autoscale; | |||
} | |||
public void setAutoscale(Boolean autoscale) { | |||
this.autoscale = autoscale; | |||
} | |||
public double getWeight() { | |||
return weight; | |||
} | |||
public void setWeight(double weight) { | |||
this.weight = weight; | |||
} | |||
public Map<String, Node> getNodeMap() { | |||
return nodeMap; | |||
} | |||
public void setNodeMap(Map<String, Node> nodeMap) { | |||
this.nodeMap = nodeMap; | |||
} | |||
public double getAllocatableCpu() { | |||
return allocatableCpu; | |||
} | |||
public void setAllocatableCpu(double allocatableCpu) { | |||
this.allocatableCpu = allocatableCpu; | |||
} | |||
public double getAllocatableMemory() { | |||
return allocatableMemory; | |||
} | |||
public void setAllocatableMemory(double allocatableMemory) { | |||
this.allocatableMemory = allocatableMemory; | |||
} | |||
public int getForeverNodeNum() { | |||
return foreverNodeNum; | |||
} | |||
public void setForeverNodeNum(int foreverNodeNum) { | |||
this.foreverNodeNum = foreverNodeNum; | |||
} | |||
public int getScaleNodeNum() { | |||
return scaleNodeNum; | |||
} | |||
public void setScaleNodeNum(int scaleNodeNum) { | |||
this.scaleNodeNum = scaleNodeNum; | |||
} | |||
public double getRequestCpuSum() { | |||
return requestCpuSum; | |||
} | |||
public void setRequestCpuSum(double requestCpuSum) { | |||
this.requestCpuSum = requestCpuSum; | |||
} | |||
public double getRequestMemorySum() { | |||
return requestMemorySum; | |||
} | |||
public void setRequestMemorySum(double requestMemorySum) { | |||
this.requestMemorySum = requestMemorySum; | |||
} | |||
public double getReserveRequestCpuSum() { | |||
return reserveRequestCpuSum.doubleValue(); | |||
} | |||
public void addReserveRequestCpu(double reserveRequestCpu) { | |||
this.reserveRequestCpuSum.addAndGet(reserveRequestCpu); | |||
} | |||
public double getReserveRequestMemorySum() { | |||
return reserveRequestMemorySum.doubleValue(); | |||
} | |||
public void addReserveRequestMemory(double reserveRequestMemory) { | |||
this.reserveRequestMemorySum.addAndGet(reserveRequestMemory); | |||
} | |||
public double getSurplusCpuSum() { | |||
return surplusCpuSum; | |||
} | |||
public void setSurplusCpuSum(double surplusCpuSum) { | |||
this.surplusCpuSum = surplusCpuSum; | |||
} | |||
public double getSurplusMemorySum() { | |||
return surplusMemorySum; | |||
} | |||
public void setSurplusMemorySum(double surplusMemorySum) { | |||
this.surplusMemorySum = surplusMemorySum; | |||
} | |||
public double getConversionForeverNodeRequestCpuRatio() { | |||
return conversionForeverNodeRequestCpuRatio; | |||
} | |||
public void setConversionForeverNodeRequestCpuRatio(double conversionForeverNodeRequestCpuRatio) { | |||
this.conversionForeverNodeRequestCpuRatio = conversionForeverNodeRequestCpuRatio; | |||
} | |||
public double getConversionForeverNodeRequestMemoryRatio() { | |||
return conversionForeverNodeRequestMemoryRatio; | |||
} | |||
public void setConversionForeverNodeRequestMemoryRatio(double conversionForeverNodeRequestMemoryRatio) { | |||
this.conversionForeverNodeRequestMemoryRatio = conversionForeverNodeRequestMemoryRatio; | |||
} | |||
public double getForeverAllocatableCpuSum() { | |||
return foreverAllocatableCpuSum; | |||
} | |||
public void setForeverAllocatableCpuSum(double foreverAllocatableCpuSum) { | |||
this.foreverAllocatableCpuSum = foreverAllocatableCpuSum; | |||
} | |||
public double getForeverAllocatableMemorySum() { | |||
return foreverAllocatableMemorySum; | |||
} | |||
public void setForeverAllocatableMemorySum(double foreverAllocatableMemorySum) { | |||
this.foreverAllocatableMemorySum = foreverAllocatableMemorySum; | |||
} | |||
public double getRequestCpuRatio() { | |||
return requestCpuRatio; | |||
} | |||
public void setRequestCpuRatio(double requestCpuRatio) { | |||
this.requestCpuRatio = requestCpuRatio; | |||
} | |||
public double getRequestMemoryRatio() { | |||
return requestMemoryRatio; | |||
} | |||
public void setRequestMemoryRatio(double requestMemoryRatio) { | |||
this.requestMemoryRatio = requestMemoryRatio; | |||
} | |||
public int getPodCount() { | |||
return podCount; | |||
} | |||
public void setPodCount(int podCount) { | |||
this.podCount = podCount; | |||
} | |||
@Override | |||
public String toString() { | |||
return "ClusterInfo{" + | |||
"clusterConfig=" + clusterConfig + | |||
", autoscale=" + autoscale + | |||
", weight=" + weight + | |||
", nodeMap=" + nodeMap.size() + | |||
", allocatableCpu=" + allocatableCpu + | |||
", allocatableMemory=" + allocatableMemory + | |||
", foreverNodeNum=" + foreverNodeNum + | |||
", scaleNodeNum=" + scaleNodeNum + | |||
", requestCpuSum=" + requestCpuSum + | |||
", requestMemorySum=" + requestMemorySum + | |||
", reserveRequestCpuSum=" + reserveRequestCpuSum + | |||
", reserveRequestMemorySum=" + reserveRequestMemorySum + | |||
", surplusCpuSum=" + surplusCpuSum + | |||
", surplusMemorySum=" + surplusMemorySum + | |||
", foreverAllocatableCpuSum=" + foreverAllocatableCpuSum + | |||
", foreverAllocatableMemorySum=" + foreverAllocatableMemorySum + | |||
", conversionForeverNodeRequestCpuRatio=" + conversionForeverNodeRequestCpuRatio + | |||
", conversionForeverNodeRequestMemoryRatio=" + conversionForeverNodeRequestMemoryRatio + | |||
'}'; | |||
} | |||
} |
@@ -0,0 +1,143 @@ | |||
package com.imitate.common.k8s.bean; | |||
import com.alibaba.fastjson.JSONObject; | |||
import com.google.common.base.Splitter; | |||
import lombok.Builder; | |||
import lombok.Data; | |||
import lombok.ToString; | |||
import org.apache.commons.lang3.StringUtils; | |||
import java.util.List; | |||
/** | |||
* 创建容器的参数 | |||
* | |||
* @author 威少 | |||
*/ | |||
@Data | |||
@ToString | |||
@Builder | |||
public class ContainerCreateParams { | |||
/** | |||
* 超级权限 | |||
*/ | |||
private boolean privileged = false; | |||
/** | |||
* 镜像 | |||
*/ | |||
private String image; | |||
/** | |||
* 镜像名 | |||
*/ | |||
private String imageName; | |||
/** | |||
* 容器拉取策略 | |||
*/ | |||
private String imagePullPolicy = "IfNotPresent"; | |||
/** | |||
* 内存限制 | |||
*/ | |||
private String memoryLimit; | |||
/** | |||
* CPU限制 | |||
*/ | |||
private String cpuLimit; | |||
/** | |||
* 内存request | |||
*/ | |||
private String memoryRequest; | |||
/** | |||
* CPU request | |||
*/ | |||
private String cpuRequest; | |||
/** | |||
* 磁盘限制 | |||
*/ | |||
private String diskLimit; | |||
/** | |||
* 容器类型 | |||
*/ | |||
private Type type; | |||
/** | |||
* 启动时间限制 | |||
*/ | |||
private Integer startTimeLimit; | |||
/** | |||
* 容器挂载点信息 | |||
*/ | |||
private List<ContainerMountParams> containerMountParams; | |||
/** | |||
* 启动命令 | |||
*/ | |||
private List<String> command; | |||
/** | |||
* prestop hook 执行的命令 | |||
*/ | |||
private List<String> prestopCommand; | |||
/** | |||
* poststart hook 执行的指令 | |||
*/ | |||
private List<String> postStartCommand; | |||
/** | |||
* 从json转换,格式形如 | |||
* {"image":"python3-ssh:v1.0","cpuLimit":2.0,"cpuRequest":0.1,"memoryLimit":"2048M", | |||
* "memoryRequest":"10M","resourceLimit":"10000K","startTime":15,"type":"main"} | |||
*/ | |||
public static ContainerCreateParams fromJSON(JSONObject containers) { | |||
ContainerCreateParams containerCreateParams = ContainerCreateParams.builder() | |||
.image(containers.getString("image")) | |||
.cpuLimit(containers.getString("cpuLimit")) | |||
.memoryLimit(containers.getString("memoryLimit")) | |||
.memoryRequest(containers.getString("memoryRequest")) | |||
.cpuRequest(containers.getString("cpuRequest")) | |||
.diskLimit(containers.getString("resourceLimit")) | |||
.startTimeLimit(containers.getInteger("startTime")) | |||
.build(); | |||
if (StringUtils.isNotEmpty(containers.getString("type"))) { | |||
containerCreateParams.setType(Type.valueOf(containers.getString("type"))); | |||
} | |||
if (StringUtils.isNotEmpty(containers.getString("command"))) { | |||
containerCreateParams.setCommand(Splitter.on(" ").splitToList(containers.getString("command"))); | |||
} | |||
return containerCreateParams; | |||
} | |||
/** | |||
* 容器类型 | |||
*/ | |||
public enum Type { | |||
/** | |||
* 主类别 | |||
*/ | |||
MAIN("main"), | |||
/** | |||
* 子类别 | |||
*/ | |||
SUB("sub"); | |||
private String value; | |||
Type(String value) { | |||
this.value = value; | |||
} | |||
public String getValue() { | |||
return value; | |||
} | |||
} | |||
} |
@@ -0,0 +1,28 @@ | |||
package com.imitate.common.k8s.bean; | |||
import lombok.Builder; | |||
import lombok.Data; | |||
import lombok.ToString; | |||
/** | |||
* 容器挂载参数 | |||
* | |||
* @author 威少 | |||
*/ | |||
@Data | |||
@ToString | |||
@Builder | |||
public class ContainerMountParams { | |||
/** | |||
* 数据卷名称 | |||
*/ | |||
private String name; | |||
/** | |||
* 挂载目标位置 | |||
*/ | |||
private String path; | |||
/** | |||
* 是否只读 | |||
*/ | |||
private Boolean readonly; | |||
} |
@@ -0,0 +1,66 @@ | |||
package com.imitate.common.k8s.bean; | |||
import io.fabric8.kubernetes.api.model.Pod; | |||
public class CreatePodResult { | |||
private Boolean success = Boolean.FALSE; | |||
private Integer status = CREATE_POD_STATUS_DEF; | |||
public static final int CREATE_POD_STATUS_DEF = 0; | |||
public static final int CREATE_POD_STATUS_FAIL_PULL_IMAGE = 1; | |||
public static final int CREATE_POD_STATUS_FAIL_DELETE_NOW = 2; | |||
private Pod pod; | |||
private String nodeName; | |||
/** | |||
* 创建开始时间 | |||
*/ | |||
private Long startTime; | |||
public CreatePodResult() { | |||
} | |||
public CreatePodResult(Long startTime) { | |||
this.startTime = startTime; | |||
} | |||
public Pod getPod() { | |||
return pod; | |||
} | |||
public void setPod(Pod pod) { | |||
this.pod = pod; | |||
} | |||
public long getMillisecondCost() { | |||
return System.currentTimeMillis() - startTime; | |||
} | |||
public Boolean getSuccess() { | |||
return success; | |||
} | |||
public void setSuccess(Boolean success) { | |||
this.success = success; | |||
} | |||
public Integer getStatus() { | |||
return status; | |||
} | |||
public void setStatus(Integer status) { | |||
this.status = status; | |||
} | |||
public String getNodeName() { | |||
return nodeName; | |||
} | |||
public void setNodeName(String nodeName) { | |||
this.nodeName = nodeName; | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
package com.imitate.common.k8s.bean; | |||
import lombok.Builder; | |||
import lombok.Data; | |||
import lombok.ToString; | |||
import java.util.Map; | |||
/** | |||
* 创建Deployment的参数 | |||
* | |||
* @author 威少 | |||
*/ | |||
@Data | |||
@ToString | |||
@Builder | |||
public class DeploymentCreateParams { | |||
/** | |||
* Deployment的名字 | |||
*/ | |||
private String name; | |||
/** | |||
* 标签 | |||
*/ | |||
private Map<String, String> labels; | |||
/** | |||
* pod副本数量 | |||
*/ | |||
private Integer replicas; | |||
/** | |||
* Pod Create Params | |||
*/ | |||
private PodCreateParams podCreateParams; | |||
} |
@@ -0,0 +1,122 @@ | |||
package com.imitate.common.k8s.bean; | |||
/** | |||
* Created by weishao on 2017/4/12. | |||
*/ | |||
public class ExecResultCase { | |||
private String caseId; | |||
private String input; | |||
private String output; | |||
private String expectedOutput; | |||
private String passed; | |||
private String type = "0"; // 默认为文本类型 | |||
private Long testSetTime; | |||
private Long testSetMem; | |||
public String getCaseId() { | |||
return caseId; | |||
} | |||
public String getInput() { | |||
return input; | |||
} | |||
public String getOutput() { | |||
return output; | |||
} | |||
public String getExpectedOutput() { | |||
return expectedOutput; | |||
} | |||
public String getPassed() { | |||
return passed; | |||
} | |||
public void setCaseId(String caseId) { | |||
this.caseId = caseId; | |||
} | |||
public void setInput(String input) { | |||
this.input = input; | |||
} | |||
public void setOutput(String output) { | |||
this.output = output; | |||
} | |||
public void setExpectedOutput(String expectedOutput) { | |||
this.expectedOutput = expectedOutput; | |||
} | |||
public void setPassed(String passed) { | |||
this.passed = passed; | |||
} | |||
public Long getTestSetTime() { | |||
return testSetTime; | |||
} | |||
public void setTestSetTime(Long testSetTime) { | |||
this.testSetTime = testSetTime; | |||
} | |||
public Long getTestSetMem() { | |||
return testSetMem; | |||
} | |||
public void setTestSetMem(Long testSetMem) { | |||
this.testSetMem = testSetMem; | |||
} | |||
public String getType() { | |||
return type; | |||
} | |||
public void setType(String type) { | |||
this.type = type; | |||
} | |||
public ExecResultCase() { | |||
} | |||
public ExecResultCase(String caseId, String output, String passed) { | |||
this.caseId = caseId; | |||
this.output = output; | |||
this.passed = passed; | |||
} | |||
public ExecResultCase(String caseId, String output, String passed, String type) { | |||
this.caseId = caseId; | |||
this.output = output; | |||
this.passed = passed; | |||
this.type = type; | |||
} | |||
public enum Status { | |||
/** | |||
* 测试用例通过 | |||
*/ | |||
PASS("1"), | |||
/** | |||
* 测试用例不通过 | |||
*/ | |||
FAIL("0"); | |||
private String value; | |||
public String getValue() { | |||
return value; | |||
} | |||
public void setValue(String value) { | |||
this.value = value; | |||
} | |||
Status(String value) { | |||
this.value = value; | |||
} | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
package com.imitate.common.k8s.bean; | |||
import lombok.Builder; | |||
import lombok.Data; | |||
import lombok.ToString; | |||
/** | |||
* 创建HPA的参数 | |||
* | |||
* @author 威少 | |||
*/ | |||
@Data | |||
@ToString | |||
@Builder | |||
public class HPACreateParams { | |||
/** | |||
* hpa的名字 | |||
*/ | |||
private String name; | |||
/** | |||
* deployment的名字 | |||
*/ | |||
private String deploymentName; | |||
/** | |||
* 最小Pod数 | |||
*/ | |||
private Integer minReplicas; | |||
/** | |||
* 最大Pod数 | |||
*/ | |||
private Integer maxReplicas; | |||
/** | |||
* 期望的CPU利用率 | |||
*/ | |||
private Integer desireCpuPercent; | |||
/** | |||
* 期望的内存利用率 | |||
*/ | |||
private Integer desireMemoryPercent; | |||
} |
@@ -0,0 +1,77 @@ | |||
package com.imitate.common.k8s.bean; | |||
/** | |||
* 节点评测错误信息 | |||
*/ | |||
public class NodeEvaErrorInfo { | |||
private String nodeName; | |||
private Integer downloadErrorNum; | |||
private Integer createPodErrorNum; | |||
private Integer runErrorNum; | |||
private Integer evaCount; | |||
public boolean isErrorNode(Integer errorNum, Double errorRatio) { | |||
return (createPodErrorNum > errorNum && ((double) createPodErrorNum / evaCount) > errorRatio) | |||
|| (runErrorNum > errorNum && ((double) runErrorNum / evaCount) > errorRatio); | |||
} | |||
public boolean isRunErrorNode(Integer errorNum, Double errorRatio) { | |||
return runErrorNum > errorNum && ((double) runErrorNum / evaCount) > errorRatio; | |||
} | |||
public String getNodeName() { | |||
return nodeName; | |||
} | |||
public void setNodeName(String nodeName) { | |||
this.nodeName = nodeName; | |||
} | |||
public Integer getDownloadErrorNum() { | |||
return downloadErrorNum; | |||
} | |||
public void setDownloadErrorNum(Integer downloadErrorNum) { | |||
this.downloadErrorNum = downloadErrorNum; | |||
} | |||
public Integer getCreatePodErrorNum() { | |||
return createPodErrorNum; | |||
} | |||
public void setCreatePodErrorNum(Integer createPodErrorNum) { | |||
this.createPodErrorNum = createPodErrorNum; | |||
} | |||
public Integer getRunErrorNum() { | |||
return runErrorNum; | |||
} | |||
public void setRunErrorNum(Integer runErrorNum) { | |||
this.runErrorNum = runErrorNum; | |||
} | |||
public Integer getEvaCount() { | |||
return evaCount; | |||
} | |||
public void setEvaCount(Integer evaCount) { | |||
this.evaCount = evaCount; | |||
} | |||
@Override | |||
public String toString() { | |||
return "NodeEvaErrorInfo{" + | |||
"nodeName='" + nodeName + '\'' + | |||
", downloadErrorNum=" + downloadErrorNum + | |||
", createPodErrorNum=" + createPodErrorNum + | |||
", runErrorNum=" + runErrorNum + | |||
", evaCount=" + evaCount + | |||
'}'; | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
package com.imitate.common.k8s.bean; | |||
import java.util.Map; | |||
public class NodeQueryParam { | |||
private String cluster; | |||
private String name; | |||
private Map<String, String> labels; | |||
public String getCluster() { | |||
return cluster; | |||
} | |||
public void setCluster(String cluster) { | |||
this.cluster = cluster; | |||
} | |||
public String getName() { | |||
return name; | |||
} | |||
public void setName(String name) { | |||
this.name = name; | |||
} | |||
public Map<String, String> getLabels() { | |||
return labels; | |||
} | |||
public void setLabels(Map<String, String> labels) { | |||
this.labels = labels; | |||
} | |||
} |
@@ -0,0 +1,58 @@ | |||
package com.imitate.common.k8s.bean; | |||
import com.google.common.util.concurrent.AtomicDouble; | |||
public class NodeRes { | |||
private String nodeName; | |||
private Double requestCpu; | |||
private Double allocatableCpu; | |||
private Double requestCpuRate; | |||
/** | |||
* 调度时,使得pod尽量不调度到此节点的cpu量 | |||
*/ | |||
private AtomicDouble unAffinityCpu = new AtomicDouble(0.0); | |||
public String getNodeName() { | |||
return nodeName; | |||
} | |||
public void setNodeName(String nodeName) { | |||
this.nodeName = nodeName; | |||
} | |||
public Double getRequestCpu() { | |||
return requestCpu; | |||
} | |||
public void setRequestCpu(Double requestCpu) { | |||
this.requestCpu = requestCpu; | |||
} | |||
public Double getAllocatableCpu() { | |||
return allocatableCpu; | |||
} | |||
public void setAllocatableCpu(Double allocatableCpu) { | |||
this.allocatableCpu = allocatableCpu; | |||
} | |||
public Double getRequestCpuRate() { | |||
return requestCpuRate; | |||
} | |||
public void setRequestCpuRate(Double requestCpuRate) { | |||
this.requestCpuRate = requestCpuRate; | |||
} | |||
public double getUnAffinityCpu() { | |||
return unAffinityCpu.doubleValue(); | |||
} | |||
public void addUnAffinityCpu(double unAffinityCpu) { | |||
this.unAffinityCpu.addAndGet(unAffinityCpu); | |||
} | |||
} |
@@ -0,0 +1,76 @@ | |||
package com.imitate.common.k8s.bean; | |||
/** | |||
* 节点资源统计,数据来源于 run_pod 表。 | |||
* | |||
* @author mumu | |||
* | |||
*/ | |||
public class NodeResStat { | |||
private String cluster; | |||
private String nodeIp; | |||
private Integer podNum; | |||
private Double cpuRequest; | |||
private Double cpuLimit; | |||
private Double memoryRequest; | |||
private Double momoryLimit; | |||
public String getCluster() { | |||
return cluster; | |||
} | |||
public void setCluster(String cluster) { | |||
this.cluster = cluster; | |||
} | |||
public String getNodeIp() { | |||
return nodeIp; | |||
} | |||
public void setNodeIp(String nodeIp) { | |||
this.nodeIp = nodeIp; | |||
} | |||
public Double getCpuRequest() { | |||
return cpuRequest; | |||
} | |||
public void setCpuRequest(Double cpuRequest) { | |||
this.cpuRequest = cpuRequest; | |||
} | |||
public Double getCpuLimit() { | |||
return cpuLimit; | |||
} | |||
public void setCpuLimit(Double cpuLimit) { | |||
this.cpuLimit = cpuLimit; | |||
} | |||
public Double getMemoryRequest() { | |||
return memoryRequest; | |||
} | |||
public void setMemoryRequest(Double memoryRequest) { | |||
this.memoryRequest = memoryRequest; | |||
} | |||
public Double getMomoryLimit() { | |||
return momoryLimit; | |||
} | |||
public void setMomoryLimit(Double momoryLimit) { | |||
this.momoryLimit = momoryLimit; | |||
} | |||
public Integer getPodNum() { | |||
return podNum; | |||
} | |||
public void setPodNum(Integer podNum) { | |||
this.podNum = podNum; | |||
} | |||
} |
@@ -0,0 +1,165 @@ | |||
package com.imitate.common.k8s.bean; | |||
import java.util.List; | |||
/** | |||
* oj评测结果 | |||
*/ | |||
public class OjEvaResult { | |||
private String tpiID; | |||
private String status; | |||
private String execMode; | |||
public final static String OJ_EVA_EXEC_MODE_DEBUG = "debug"; | |||
public final static String OJ_EVA_EXEC_MODE_ALL = "submit"; | |||
/** | |||
* 代码,返回上层,避免查数据库 | |||
*/ | |||
private String codeFileContent; | |||
private String executeMem; | |||
private String executeTime; | |||
private Integer failCaseNum; | |||
private String outPut; | |||
private ExecResultCase testCase; | |||
private List<ExecResultCase> execResultCases; | |||
private TimeCost timeCost; | |||
private String sec_key; | |||
private String extras; | |||
// 新增编译和执行返回 0代表编译/运行失败 1 代表编译/运行成功 | |||
private String compileSuccess; | |||
private String executeSuccess; | |||
public OjEvaResult(String tpiID, String status, String outPut, ExecResultCase testCase, String sec_key, String extras) { | |||
this.tpiID = tpiID; | |||
this.status = status; | |||
this.outPut = outPut; | |||
this.testCase = testCase; | |||
this.sec_key = sec_key; | |||
this.extras = extras; | |||
} | |||
public String getSec_key() { | |||
return sec_key; | |||
} | |||
public void setSec_key(String sec_key) { | |||
this.sec_key = sec_key; | |||
} | |||
public String getTpiID() { | |||
return tpiID; | |||
} | |||
public void setTpiID(String tpiID) { | |||
this.tpiID = tpiID; | |||
} | |||
public String getStatus() { | |||
return status; | |||
} | |||
public void setStatus(String status) { | |||
this.status = status; | |||
} | |||
public String getOutPut() { | |||
return outPut; | |||
} | |||
public void setOutPut(String outPut) { | |||
this.outPut = outPut; | |||
} | |||
public ExecResultCase getTestCase() { | |||
return testCase; | |||
} | |||
public void setTestCase(ExecResultCase testCase) { | |||
this.testCase = testCase; | |||
} | |||
public String getExecMode() { | |||
return execMode; | |||
} | |||
public void setExecMode(String execMode) { | |||
this.execMode = execMode; | |||
} | |||
public Integer getFailCaseNum() { | |||
return failCaseNum; | |||
} | |||
public void setFailCaseNum(Integer failCaseNum) { | |||
this.failCaseNum = failCaseNum; | |||
} | |||
public TimeCost getTimeCost() { | |||
return timeCost; | |||
} | |||
public void setTimeCost(TimeCost timeCost) { | |||
this.timeCost = timeCost; | |||
} | |||
public String getExecuteMem() { | |||
return executeMem; | |||
} | |||
public void setExecuteMem(String executeMem) { | |||
this.executeMem = executeMem; | |||
} | |||
public String getExecuteTime() { | |||
return executeTime; | |||
} | |||
public void setExecuteTime(String executeTime) { | |||
this.executeTime = executeTime; | |||
} | |||
public String getCodeFileContent() { | |||
return codeFileContent; | |||
} | |||
public void setCodeFileContent(String codeFileContent) { | |||
this.codeFileContent = codeFileContent; | |||
} | |||
public List<ExecResultCase> getCases() { | |||
return execResultCases; | |||
} | |||
public void setCases(List<ExecResultCase> execResultCases) { | |||
this.execResultCases = execResultCases; | |||
} | |||
public String getExtras() { | |||
return extras; | |||
} | |||
public void setExtras(String extras) { | |||
this.extras = extras; | |||
} | |||
public String getCompileSuccess() { | |||
return compileSuccess; | |||
} | |||
public void setCompileSuccess(String compileSuccess) { | |||
this.compileSuccess = compileSuccess; | |||
} | |||
public String getExecuteSuccess() { | |||
return executeSuccess; | |||
} | |||
public void setExecuteSuccess(String executeSuccess) { | |||
this.executeSuccess = executeSuccess; | |||
} | |||
} |
@@ -0,0 +1,41 @@ | |||
package com.imitate.common.k8s.bean; | |||
import io.fabric8.kubernetes.api.model.Container; | |||
import io.fabric8.kubernetes.api.model.Volume; | |||
import lombok.Data; | |||
import lombok.ToString; | |||
import java.util.List; | |||
import java.util.Map; | |||
/** | |||
* 创建Pod的参数 | |||
* | |||
* @author 威少 | |||
*/ | |||
@Data | |||
@ToString | |||
public class PodCreateParams { | |||
/** | |||
* 标签 | |||
*/ | |||
private Map<String, String> labels; | |||
/** | |||
* 名称 | |||
*/ | |||
private String name; | |||
/** | |||
* 容器 | |||
*/ | |||
private List<Container> containerList; | |||
/** | |||
* 节点选择器 | |||
*/ | |||
private Map<String, String> nodeSelector; | |||
/** | |||
* mount | |||
*/ | |||
private Volume[] volumes; | |||
} |
@@ -0,0 +1,123 @@ | |||
package com.imitate.common.k8s.bean; | |||
import com.imitate.common.sys.constant.SysConfigCsts; | |||
import java.util.List; | |||
public class PodCreateStrategy { | |||
private Integer timeLimit = MAX_CREATE_POD_TIME_LIMIT; | |||
public static final int MAX_CREATE_POD_TIME_LIMIT = 15; | |||
/** | |||
* 普通pod能否分配到神龙节点 | |||
*/ | |||
private Boolean normalPodCanShenlong; | |||
/** | |||
* 普通pod能否分配到GPU节点 | |||
*/ | |||
private Boolean normalPodCanGpu; | |||
/** | |||
* 普通pod能否分配到oj节点 | |||
*/ | |||
private Boolean normalPodCanOj; | |||
private List<String> noSchedulableNodes; | |||
private List<String> noSchedulableShenlongNodes; | |||
private List<String> noSchedulableGpuNodes; | |||
/** | |||
* pod不可调度的权重 | |||
*/ | |||
private Integer podNoSchedulableWeight; | |||
private Integer autoscaleNodeWeight = SysConfigCsts.AUTOSCALE_NODE_WEIGHT_DEF; | |||
public Integer getTimeLimit() { | |||
return timeLimit; | |||
} | |||
public PodCreateStrategy setTimeLimit(Integer timeLimit) { | |||
this.timeLimit = timeLimit; | |||
return this; | |||
} | |||
public Boolean getNormalPodCanShenlong() { | |||
return normalPodCanShenlong; | |||
} | |||
public PodCreateStrategy setNormalPodCanShenlong(Boolean normalPodCanShenlong) { | |||
this.normalPodCanShenlong = normalPodCanShenlong; | |||
return this; | |||
} | |||
public Boolean getNormalPodCanGpu() { | |||
return normalPodCanGpu; | |||
} | |||
public PodCreateStrategy setNormalPodCanGpu(Boolean normalPodCanGpu) { | |||
this.normalPodCanGpu = normalPodCanGpu; | |||
return this; | |||
} | |||
public Boolean getNormalPodCanOj() { | |||
return normalPodCanOj; | |||
} | |||
public PodCreateStrategy setNormalPodCanOj(Boolean normalPodCanOj) { | |||
this.normalPodCanOj = normalPodCanOj; | |||
return this; | |||
} | |||
public List<String> getNoSchedulableNodes() { | |||
return noSchedulableNodes; | |||
} | |||
public PodCreateStrategy setNoSchedulableNodes(List<String> noSchedulableNodes) { | |||
this.noSchedulableNodes = noSchedulableNodes; | |||
return this; | |||
} | |||
public List<String> getNoSchedulableShenlongNodes() { | |||
return noSchedulableShenlongNodes; | |||
} | |||
public PodCreateStrategy setNoSchedulableShenlongNodes(List<String> noSchedulableShenlongNodes) { | |||
this.noSchedulableShenlongNodes = noSchedulableShenlongNodes; | |||
return this; | |||
} | |||
public List<String> getNoSchedulableGpuNodes() { | |||
return noSchedulableGpuNodes; | |||
} | |||
public PodCreateStrategy setNoSchedulableGpuNodes(List<String> noSchedulableGpuNodes) { | |||
this.noSchedulableGpuNodes = noSchedulableGpuNodes; | |||
return this; | |||
} | |||
public Integer getPodNoSchedulableWeight() { | |||
return podNoSchedulableWeight; | |||
} | |||
public PodCreateStrategy setPodNoSchedulableWeight(Integer podNoSchedulableWeight) { | |||
this.podNoSchedulableWeight = podNoSchedulableWeight; | |||
return this; | |||
} | |||
public Integer getAutoscaleNodeWeight() { | |||
return autoscaleNodeWeight; | |||
} | |||
public PodCreateStrategy setAutoscaleNodeWeight(Integer autoscaleNodeWeight) { | |||
this.autoscaleNodeWeight = autoscaleNodeWeight; | |||
return this; | |||
} | |||
} |
@@ -0,0 +1,281 @@ | |||
package com.imitate.common.k8s.bean; | |||
import java.time.LocalDateTime; | |||
public class PodQueryParam { | |||
private Long id; | |||
private String cluster; | |||
private String name; | |||
private String tpiID; | |||
private String uid; | |||
private LocalDateTime requestTime; | |||
private LocalDateTime minRequestTime; | |||
private LocalDateTime maxRequestTime; | |||
private String secKey; | |||
private String nodeName; | |||
private String nodeIp; | |||
private String downloadStatus; | |||
private String createPodStatus; | |||
private String compileStatus; | |||
private String runStatus; | |||
private String status; | |||
private String sortField; | |||
private String sortDirection; | |||
private Integer pageNum = 1; | |||
private Integer pageSize = 10; | |||
private String statDate; | |||
private Double pull; | |||
private Double createPod; | |||
private String imageName; | |||
private Double cpuLimit; | |||
private Integer memoryLimit; | |||
private Double cpuRequest; | |||
private Integer memoryRequest; | |||
private Double errorRatio; | |||
public Long getId() { | |||
return id; | |||
} | |||
public void setId(Long id) { | |||
this.id = id; | |||
} | |||
public String getCluster() { | |||
return cluster; | |||
} | |||
public void setCluster(String cluster) { | |||
this.cluster = cluster; | |||
} | |||
public String getName() { | |||
return name; | |||
} | |||
public void setName(String name) { | |||
this.name = name; | |||
} | |||
public String getTpiID() { | |||
return tpiID; | |||
} | |||
public void setTpiID(String tpiID) { | |||
this.tpiID = tpiID; | |||
} | |||
public String getUid() { | |||
return uid; | |||
} | |||
public void setUid(String uid) { | |||
this.uid = uid; | |||
} | |||
public LocalDateTime getRequestTime() { | |||
return requestTime; | |||
} | |||
public void setRequestTime(LocalDateTime requestTime) { | |||
this.requestTime = requestTime; | |||
} | |||
public LocalDateTime getMinRequestTime() { | |||
return minRequestTime; | |||
} | |||
public void setMinRequestTime(LocalDateTime minRequestTime) { | |||
this.minRequestTime = minRequestTime; | |||
} | |||
public LocalDateTime getMaxRequestTime() { | |||
return maxRequestTime; | |||
} | |||
public void setMaxRequestTime(LocalDateTime maxRequestTime) { | |||
this.maxRequestTime = maxRequestTime; | |||
} | |||
public String getSecKey() { | |||
return secKey; | |||
} | |||
public void setSecKey(String secKey) { | |||
this.secKey = secKey; | |||
} | |||
public String getNodeName() { | |||
return nodeName; | |||
} | |||
public void setNodeName(String nodeName) { | |||
this.nodeName = nodeName; | |||
} | |||
public String getNodeIp() { | |||
return nodeIp; | |||
} | |||
public void setNodeIp(String nodeIp) { | |||
this.nodeIp = nodeIp; | |||
} | |||
public String getDownloadStatus() { | |||
return downloadStatus; | |||
} | |||
public void setDownloadStatus(String downloadStatus) { | |||
this.downloadStatus = downloadStatus; | |||
} | |||
public String getCreatePodStatus() { | |||
return createPodStatus; | |||
} | |||
public void setCreatePodStatus(String createPodStatus) { | |||
this.createPodStatus = createPodStatus; | |||
} | |||
public String getCompileStatus() { | |||
return compileStatus; | |||
} | |||
public void setCompileStatus(String compileStatus) { | |||
this.compileStatus = compileStatus; | |||
} | |||
public String getRunStatus() { | |||
return runStatus; | |||
} | |||
public void setRunStatus(String runStatus) { | |||
this.runStatus = runStatus; | |||
} | |||
public String getStatus() { | |||
return status; | |||
} | |||
public void setStatus(String status) { | |||
this.status = status; | |||
} | |||
public String getSortField() { | |||
return sortField; | |||
} | |||
public void setSortField(String sortField) { | |||
this.sortField = sortField; | |||
} | |||
public String getSortDirection() { | |||
return sortDirection; | |||
} | |||
public void setSortDirection(String sortDirection) { | |||
this.sortDirection = sortDirection; | |||
} | |||
public Integer getPageNum() { | |||
return pageNum; | |||
} | |||
public void setPageNum(Integer pageNum) { | |||
this.pageNum = pageNum; | |||
} | |||
public Integer getPageSize() { | |||
return pageSize; | |||
} | |||
public void setPageSize(Integer pageSize) { | |||
this.pageSize = pageSize; | |||
} | |||
public String getStatDate() { | |||
return statDate; | |||
} | |||
public void setStatDate(String statDate) { | |||
this.statDate = statDate; | |||
} | |||
public Double getPull() { | |||
return pull; | |||
} | |||
public void setPull(Double pull) { | |||
this.pull = pull; | |||
} | |||
public Double getCreatePod() { | |||
return createPod; | |||
} | |||
public void setCreatePod(Double createPod) { | |||
this.createPod = createPod; | |||
} | |||
public String getImageName() { | |||
return imageName; | |||
} | |||
public void setImageName(String imageName) { | |||
this.imageName = imageName; | |||
} | |||
public Double getCpuLimit() { | |||
return cpuLimit; | |||
} | |||
public void setCpuLimit(Double cpuLimit) { | |||
this.cpuLimit = cpuLimit; | |||
} | |||
public Integer getMemoryLimit() { | |||
return memoryLimit; | |||
} | |||
public void setMemoryLimit(Integer memoryLimit) { | |||
this.memoryLimit = memoryLimit; | |||
} | |||
public Double getCpuRequest() { | |||
return cpuRequest; | |||
} | |||
public void setCpuRequest(Double cpuRequest) { | |||
this.cpuRequest = cpuRequest; | |||
} | |||
public Integer getMemoryRequest() { | |||
return memoryRequest; | |||
} | |||
public void setMemoryRequest(Integer memoryRequest) { | |||
this.memoryRequest = memoryRequest; | |||
} | |||
public Double getErrorRatio() { | |||
return errorRatio; | |||
} | |||
public void setErrorRatio(Double errorRatio) { | |||
this.errorRatio = errorRatio; | |||
} | |||
} |
@@ -0,0 +1,127 @@ | |||
package com.imitate.common.k8s.bean; | |||
import java.time.LocalDateTime; | |||
import java.util.List; | |||
public class RunPodQueryParam { | |||
private String cluster; | |||
private String name; | |||
private List<String> names; | |||
private LocalDateTime expireTime; | |||
private Long priority; | |||
private LocalDateTime createTime; | |||
private String createBy; | |||
private String sshPort; | |||
private String svcPort; | |||
private Integer capacityThreshold; | |||
private String nodeName; | |||
private String nodeIp; | |||
public String getName() { | |||
return name; | |||
} | |||
public void setName(String name) { | |||
this.name = name; | |||
} | |||
public LocalDateTime getExpireTime() { | |||
return expireTime; | |||
} | |||
public void setExpireTime(LocalDateTime expireTime) { | |||
this.expireTime = expireTime; | |||
} | |||
public LocalDateTime getCreateTime() { | |||
return createTime; | |||
} | |||
public void setCreateTime(LocalDateTime createTime) { | |||
this.createTime = createTime; | |||
} | |||
public Long getPriority() { | |||
return priority; | |||
} | |||
public void setPriority(Long priority) { | |||
this.priority = priority; | |||
} | |||
public String getCreateBy() { | |||
return createBy; | |||
} | |||
public void setCreateBy(String createBy) { | |||
this.createBy = createBy; | |||
} | |||
public Integer getCapacityThreshold() { | |||
return capacityThreshold; | |||
} | |||
public void setCapacityThreshold(Integer capacityThreshold) { | |||
this.capacityThreshold = capacityThreshold; | |||
} | |||
public List<String> getNames() { | |||
return names; | |||
} | |||
public void setNames(List<String> names) { | |||
this.names = names; | |||
} | |||
public String getSshPort() { | |||
return sshPort; | |||
} | |||
public void setSshPort(String sshPort) { | |||
this.sshPort = sshPort; | |||
} | |||
public String getSvcPort() { | |||
return svcPort; | |||
} | |||
public void setSvcPort(String svcPort) { | |||
this.svcPort = svcPort; | |||
} | |||
public String getNodeIp() { | |||
return nodeIp; | |||
} | |||
public void setNodeIp(String nodeIp) { | |||
this.nodeIp = nodeIp; | |||
} | |||
public String getCluster() { | |||
return cluster; | |||
} | |||
public void setCluster(String cluster) { | |||
this.cluster = cluster; | |||
} | |||
public String getNodeName() { | |||
return nodeName; | |||
} | |||
public void setNodeName(String nodeName) { | |||
this.nodeName = nodeName; | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
package com.imitate.common.k8s.bean; | |||
import lombok.Data; | |||
import lombok.ToString; | |||
import java.util.Map; | |||
/** | |||
* kubernetes service创建参数 | |||
* | |||
* @author 威少 | |||
*/ | |||
@Data | |||
@ToString | |||
public class ServiceCreateParams { | |||
/** | |||
* 标签 | |||
*/ | |||
private Map<String, String> labels; | |||
/** | |||
* 名称 | |||
*/ | |||
private String name; | |||
/** | |||
* 命名空间 | |||
*/ | |||
private String namespace; | |||
/** | |||
* 选择器 | |||
*/ | |||
private Map<String, String> selector; | |||
/** | |||
* service对外暴露端口 | |||
*/ | |||
private Integer nodePort; | |||
/** | |||
* 容器内部端口 | |||
*/ | |||
private Integer targetPort; | |||
} |
@@ -0,0 +1,81 @@ | |||
package com.imitate.common.k8s.bean; | |||
import java.time.LocalDateTime; | |||
/** | |||
* 评测各阶段时间记录 | |||
*/ | |||
public class TimeCost { | |||
private LocalDateTime evaluateStartTime; | |||
private LocalDateTime evaluateEndTime; | |||
private String pull; | |||
private String createPod; | |||
private String execute; | |||
private String evaluateAllTime; | |||
public LocalDateTime getEvaluateStartTime() { | |||
return evaluateStartTime; | |||
} | |||
public void setEvaluateStartTime(LocalDateTime evaluateStartTime) { | |||
this.evaluateStartTime = evaluateStartTime; | |||
} | |||
public LocalDateTime getEvaluateEndTime() { | |||
return evaluateEndTime; | |||
} | |||
public void setEvaluateEndTime(LocalDateTime evaluateEndTime) { | |||
this.evaluateEndTime = evaluateEndTime; | |||
} | |||
public String getPull() { | |||
return pull; | |||
} | |||
public void setPull(String pull) { | |||
this.pull = pull; | |||
} | |||
public String getCreatePod() { | |||
return createPod; | |||
} | |||
public void setCreatePod(String createPod) { | |||
this.createPod = createPod; | |||
} | |||
public String getExecute() { | |||
return execute; | |||
} | |||
public void setExecute(String execute) { | |||
this.execute = execute; | |||
} | |||
public String getEvaluateAllTime() { | |||
return evaluateAllTime; | |||
} | |||
public void setEvaluateAllTime(String evaluateAllTime) { | |||
this.evaluateAllTime = evaluateAllTime; | |||
} | |||
@Override | |||
public String toString() { | |||
return "TimeCost{" + | |||
"evaluateStartTime=" + evaluateStartTime + | |||
", evaluateEndTime=" + evaluateEndTime + | |||
", pull='" + pull + '\'' + | |||
", createPod='" + createPod + '\'' + | |||
", execute='" + execute + '\'' + | |||
", evaluateAllTime='" + evaluateAllTime + '\'' + | |||
'}'; | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
package com.imitate.common.k8s.constant; | |||
public interface BridgeNodeCsts { | |||
String NODE_LABEL_TYPE = "type"; | |||
String NODE_LABEL_TYPE_OTHERS = "others"; | |||
String NODE_LABEL_TYPE_UNDER_PRESSURE = "under-pressure"; | |||
} |
@@ -0,0 +1,8 @@ | |||
package com.imitate.common.k8s.constant; | |||
public interface BridgePodCsts { | |||
String STATUS_BEGIN = "0"; | |||
String STATUS_END = "1"; | |||
} |
@@ -0,0 +1,12 @@ | |||
package com.imitate.common.k8s.constant; | |||
public interface PlatformConfigCsts { | |||
String PLATFORM_JAVA = "java"; | |||
String PLATFORM_C = "c"; | |||
String PLATFORM_CPP = "cpp"; | |||
String PLATFORM_PYTHON = "python"; | |||
} |
@@ -0,0 +1,46 @@ | |||
package com.imitate.common.k8s.mapper; | |||
import com.imitate.common.k8s.pojo.BridgePod; | |||
import com.imitate.common.k8s.bean.NodeEvaErrorInfo; | |||
import com.imitate.common.k8s.bean.PodQueryParam; | |||
import com.imitate.common.k8s.pojo.EvaDayStat; | |||
import com.imitate.common.util.BaseMapper; | |||
import org.apache.ibatis.annotations.Mapper; | |||
import org.springframework.stereotype.Repository; | |||
import java.time.LocalDateTime; | |||
import java.util.List; | |||
@Repository | |||
@Mapper | |||
public interface BridgePodMapper extends BaseMapper<BridgePod> { | |||
@Override | |||
int insert(BridgePod record); | |||
@Override | |||
int insertSelective(BridgePod record); | |||
@Override | |||
int updateByPrimaryKeySelective(BridgePod record); | |||
@Override | |||
int updateByPrimaryKey(BridgePod record); | |||
List<BridgePod> selectBridgePod(PodQueryParam param); | |||
void updateUpdateDeleteTime(BridgePod param); | |||
void deleteByRequestTime(LocalDateTime deleteTime); | |||
/** | |||
* | |||
* @param requestTime 整数,形式为yyyyMMdd | |||
* @return | |||
*/ | |||
EvaDayStat countEvaDayStat(Integer requestTime); | |||
List<NodeEvaErrorInfo> selectNodeEvaErrorInfos(LocalDateTime requestTime); | |||
} |
@@ -0,0 +1,14 @@ | |||
package com.imitate.common.k8s.mapper; | |||
import com.imitate.common.k8s.pojo.ErrorPodInfo; | |||
import com.imitate.common.util.BaseMapper; | |||
import org.apache.ibatis.annotations.Mapper; | |||
import org.springframework.stereotype.Repository; | |||
@Repository | |||
@Mapper | |||
public interface ErrorPodInfoMapper extends BaseMapper<ErrorPodInfo> { | |||
@Override | |||
int insert(ErrorPodInfo errorPodInfo); | |||
} |
@@ -0,0 +1,21 @@ | |||
package com.imitate.common.k8s.mapper; | |||
import com.imitate.common.k8s.pojo.EvaDayStat; | |||
import com.imitate.common.util.BaseMapper; | |||
import org.apache.ibatis.annotations.Mapper; | |||
import org.springframework.stereotype.Repository; | |||
@Mapper | |||
@Repository | |||
public interface EvaDayStatMapper extends BaseMapper<EvaDayStat> { | |||
@Override | |||
int insert(EvaDayStat record); | |||
@Override | |||
int insertSelective(EvaDayStat record); | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.imitate.common.k8s.mapper; | |||
import com.imitate.common.k8s.pojo.OjEvaDayStat; | |||
import com.imitate.common.util.BaseMapper; | |||
import org.apache.ibatis.annotations.Mapper; | |||
import org.springframework.stereotype.Repository; | |||
@Repository | |||
@Mapper | |||
public interface OjEvaDayStatMapper extends BaseMapper<OjEvaDayStat> { | |||
@Override | |||
int insertSelective(OjEvaDayStat record); | |||
} |
@@ -0,0 +1,22 @@ | |||
package com.imitate.common.k8s.mapper; | |||
import com.imitate.common.k8s.pojo.PlatformConfig; | |||
import com.imitate.common.util.BaseMapper; | |||
import org.apache.ibatis.annotations.Mapper; | |||
import org.springframework.stereotype.Repository; | |||
import java.util.List; | |||
@Repository | |||
@Mapper | |||
public interface PlatformConfigMapper extends BaseMapper<PlatformConfig> { | |||
@Override | |||
int insertSelective(PlatformConfig record); | |||
@Override | |||
int updateByPrimaryKeySelective(PlatformConfig record); | |||
List<PlatformConfig> selectAllConfig(); | |||
} |
@@ -0,0 +1,55 @@ | |||
package com.imitate.common.k8s.mapper; | |||
import com.imitate.common.k8s.bean.NodeResStat; | |||
import com.imitate.common.k8s.pojo.RunPod; | |||
import com.imitate.common.k8s.bean.RunPodQueryParam; | |||
import com.imitate.common.util.BaseMapper; | |||
import org.apache.ibatis.annotations.Mapper; | |||
import org.apache.ibatis.annotations.Param; | |||
import org.springframework.stereotype.Repository; | |||
import java.time.LocalDateTime; | |||
import java.util.List; | |||
@Repository | |||
@Mapper | |||
public interface RunPodMapper extends BaseMapper<RunPod> { | |||
int deleteByName(String name); | |||
int deleteByNameExpired(@Param("name") String name, @Param("expireTime") LocalDateTime expireTime); | |||
@Override | |||
int insert(RunPod record); | |||
@Override | |||
int insertSelective(RunPod record); | |||
@Override | |||
int updateByPrimaryKeySelective(RunPod record); | |||
void updateByNameSelective(RunPod runPod); | |||
int delayRunPodExpireTime(RunPod runPod); | |||
@Override | |||
int updateByPrimaryKey(RunPod record); | |||
RunPod selectRunPodForUpdate(String podName); | |||
List<RunPod> selectRunPods(RunPodQueryParam param); | |||
int updatePortByName(RunPodQueryParam param); | |||
RunPod selectByName(String podName); | |||
RunPod selectSshRunPodByTpiID(String tpiID); | |||
List<NodeResStat> statNodeRes(RunPodQueryParam param); | |||
List<String> podNumStatGroupByNodeIp(String cluster); | |||
void updateExpireTimeByNodeIp(RunPodQueryParam runPodQueryParam); | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.imitate.common.k8s.mapper; | |||
import com.imitate.common.k8s.pojo.SecurityContextConfig; | |||
import com.imitate.common.util.BaseMapper; | |||
import org.apache.ibatis.annotations.Mapper; | |||
import org.springframework.stereotype.Repository; | |||
import java.util.List; | |||
@Repository | |||
@Mapper | |||
public interface SecurityContextConfigMapper extends BaseMapper<SecurityContextConfig> { | |||
List<SecurityContextConfig> selectAll(); | |||
} |
@@ -0,0 +1,31 @@ | |||
package com.imitate.common.k8s.mapper; | |||
import com.imitate.common.k8s.pojo.WindowsInfo; | |||
import com.imitate.common.util.BaseMapper; | |||
import org.apache.ibatis.annotations.Mapper; | |||
import org.springframework.stereotype.Repository; | |||
import java.util.List; | |||
@Repository | |||
@Mapper | |||
public interface WindowsInfoMapper extends BaseMapper<WindowsInfo> { | |||
int deleteByUniqId(String uniqId); | |||
@Override | |||
int insertSelective(WindowsInfo record); | |||
WindowsInfo selectByUniqId(String uniqId); | |||
List<WindowsInfo> selectByTpiId(String tpiId); | |||
List<WindowsInfo> selectByUserId(String userID); | |||
int updateByUniqIdSelective(WindowsInfo windowsInfo); | |||
List<String> selectUniqIdByAutoReleaseTime(WindowsInfo windowsInfo); | |||
List<WindowsInfo> selectNotForwardEntryHost(); | |||
} |
@@ -0,0 +1,173 @@ | |||
package com.imitate.common.k8s.mgr; | |||
import com.imitate.common.k8s.bean.BridgeContainer; | |||
import com.imitate.common.k8s.bean.ClusterInfo; | |||
import com.imitate.common.k8s.bean.NodeQueryParam; | |||
import com.imitate.common.k8s.bean.NodeResStat; | |||
import com.imitate.common.k8s.service.K8sService; | |||
import com.imitate.common.k8s.service.RunPodService; | |||
import com.imitate.common.k8s.util.K8sUtils; | |||
import com.imitate.common.sys.pojo.ClusterConfig; | |||
import com.imitate.common.sys.service.ClusterConfigService; | |||
import com.imitate.common.sys.service.SysConfigService; | |||
import io.fabric8.kubernetes.api.model.Node; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Component; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
/** | |||
* 集群管理 | |||
*/ | |||
@Component | |||
public class ClusterManager { | |||
private Logger logger = LoggerFactory.getLogger(getClass()); | |||
// Map<cluster, ClusterInfo> | |||
private volatile Map<String, ClusterInfo> clusterMap = new HashMap<>(); | |||
@Autowired | |||
private K8sService k8sService; | |||
@Autowired | |||
private ClusterConfigService clusterConfigService; | |||
@Autowired | |||
private RunPodService runPodService; | |||
@Autowired | |||
private SysConfigService sysConfigService; | |||
// ClusterInfo 相关 begin ... | |||
// 刷新集群节点 | |||
public void refreshClusters() { | |||
List<ClusterConfig> ccList = clusterConfigService.getAvailableClusterConfigs(); | |||
List<NodeResStat> nodeResStats = runPodService.statNodeRes(); | |||
Map<String, ClusterInfo> tmpMap = new HashMap<>(); | |||
for (ClusterConfig cc : ccList) { | |||
try { | |||
ClusterInfo cInfo = refreshCluster(cc, nodeResStats); | |||
tmpMap.put(cc.getName(), cInfo); | |||
} catch (Exception e) { | |||
logger.error("刷新单个集群节点信息失败, cluster: " + cc.getName(), e); | |||
} | |||
} | |||
clusterMap = tmpMap; | |||
} | |||
private ClusterInfo refreshCluster(ClusterConfig cc, List<NodeResStat> nodeResStats) { | |||
String cluster = cc.getName(); | |||
NodeQueryParam param = new NodeQueryParam(); | |||
param.setCluster(cluster); | |||
List<Node> nodesTemp = k8sService.getNodes(param); | |||
List<Node> nodes = new ArrayList<>(); | |||
for (Node node : nodesTemp) { | |||
if (K8sUtils.isReadyStatus(node)) { | |||
nodes.add(node); | |||
} | |||
} | |||
ClusterInfo cInfo = new ClusterInfo(); | |||
cInfo.setClusterConfig(cc); | |||
cInfo.setWeight(cc.getWeight()); | |||
cInfo.setAutoscale(cc.getAutoScale()); | |||
cInfo.cal(cluster, nodes, nodeResStats); | |||
logger.debug("refreshCluster cInfo: {}", cInfo); | |||
return cInfo; | |||
} | |||
public Boolean canSupportedImageName(String cluster, String imageName) { | |||
ClusterInfo cInfo = clusterMap.get(cluster); | |||
if (cInfo != null) { | |||
if (cInfo.getClusterConfig().supportImage(imageName) | |||
&& !cInfo.getClusterConfig().notSupportImage(imageName)) { | |||
return Boolean.TRUE; | |||
} | |||
} | |||
return Boolean.FALSE; | |||
} | |||
/** | |||
* 获取支持镜像的集群 | |||
* | |||
* @param imageName | |||
* @return | |||
*/ | |||
public List<ClusterInfo> getSupportedClusterInfo(String imageName) { | |||
List<ClusterInfo> list = new ArrayList<>(); | |||
for (Map.Entry<String, ClusterInfo> entry : clusterMap.entrySet()) { | |||
ClusterInfo cInfo = entry.getValue(); | |||
// 检查白名单 | |||
if (!cInfo.getClusterConfig().supportImage(imageName)) { | |||
continue; | |||
} | |||
// 过滤黑名单 | |||
if (cInfo.getClusterConfig().notSupportImage(imageName)) { | |||
continue; | |||
} | |||
list.add(cInfo); | |||
} | |||
return list; | |||
} | |||
public Map<String, ClusterInfo> getClusterInfo() { | |||
return clusterMap; | |||
} | |||
public void clusterReserveRequestRes(String cluster, BridgeContainer bc) { | |||
ClusterInfo clusterInfo = clusterMap.get(cluster); | |||
clusterInfo.addReserveRequestCpu(bc.getCpuRequest()); | |||
clusterInfo.addReserveRequestMemory(bc.getMemoryRequest()); | |||
} | |||
// ClusterInfo 相关 end ... | |||
// Node 相关 begin ... | |||
public boolean isLocalNode(String cluster, String nodeName) { | |||
Node node = getNode(cluster, nodeName); | |||
Map<String, String> labelMap = node.getMetadata().getLabels(); | |||
return !labelMap.containsKey("remote"); | |||
} | |||
public boolean isLocalCluster(String cluster) { | |||
return clusterConfigService.getClusterConfig(cluster).getLocal(); | |||
} | |||
public Node getNode(String cluster, String nodeName) { | |||
ClusterInfo cInfo = clusterMap.get(cluster); | |||
if (cInfo != null) { | |||
Node node = cInfo.getNodeMap().get(nodeName); | |||
if (node != null) { | |||
return node; | |||
} | |||
} | |||
// 直接查询 | |||
return k8sService.getNode(cluster, nodeName); | |||
} | |||
// Node 相关 end ... | |||
public int getScaleNodeWeight(String cluster) { | |||
return sysConfigService.getScaleNodeWeight(); | |||
} | |||
public Map<String, Node> getClusterNodeMap(String cluster) { | |||
Map<String, Node> nodeMap = new HashMap<>(); | |||
ClusterInfo clusterInfo = clusterMap.get(cluster); | |||
if (clusterInfo != null) { | |||
nodeMap = clusterInfo.getNodeMap(); | |||
} | |||
return nodeMap; | |||
} | |||
} |
@@ -0,0 +1,146 @@ | |||
package com.imitate.common.k8s.mgr; | |||
import com.alibaba.fastjson.JSONArray; | |||
import com.alibaba.fastjson.JSONObject; | |||
import com.imitate.common.constant.TpCsts; | |||
import com.imitate.common.k8s.mgr.node.NodeMgrIf; | |||
import com.imitate.common.k8s.mgr.node.OjNodeMgr; | |||
import com.imitate.common.k8s.bean.CreatePodResult; | |||
import com.imitate.common.k8s.bean.PodCreateStrategy; | |||
import com.imitate.common.sys.service.SysConfigService; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.beans.factory.annotation.Qualifier; | |||
import org.springframework.stereotype.Component; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.concurrent.atomic.AtomicBoolean; | |||
/** | |||
* node管理 | |||
*/ | |||
@Component | |||
public class NodeManager { | |||
@Autowired | |||
private SysConfigService sysConfigService; | |||
@Qualifier("normalNodeMgr") | |||
@Autowired | |||
private NodeMgrIf normalNodeMgr; | |||
@Qualifier("shenlongNodeMgr") | |||
@Autowired | |||
private NodeMgrIf shenlongNodeMgr; | |||
@Qualifier("gpuNodeMgr") | |||
@Autowired | |||
private NodeMgrIf gpuNodeMgr; | |||
@Qualifier("ojNodeMgr") | |||
@Autowired | |||
private NodeMgrIf ojNodeMgr; | |||
private AtomicBoolean checking = new AtomicBoolean(false); | |||
public void check() { | |||
boolean r = checking.compareAndSet(false, true); | |||
if (!r) {// 启动时初始化线程和定时任务,可能并发执行。只允许一个执行 | |||
return; | |||
} | |||
try { | |||
normalNodeMgr.check(); | |||
shenlongNodeMgr.check(); | |||
gpuNodeMgr.check(); | |||
ojNodeMgr.check(); | |||
} finally { | |||
checking.set(false); | |||
} | |||
} | |||
/** | |||
* 是否公共pod | |||
* | |||
* @param podName | |||
* @param buildParams | |||
* @return | |||
*/ | |||
public boolean isCommonPod(String podName, JSONObject buildParams) { | |||
String containers = buildParams.getString("containers"); | |||
JSONArray jsonArray = JSONArray.parseArray(containers); | |||
if (jsonArray.size() > 1) { | |||
return false; | |||
} | |||
String imageName = jsonArray.getJSONObject(0).getString("image").split(":")[0]; | |||
List<String> commonImageNames = sysConfigService.getCommonImageNames(); | |||
return commonImageNames.contains(imageName) && podName.startsWith(TpCsts.TYPE_EVALUATE); | |||
} | |||
// 神龙------------start------------- | |||
public boolean canScheduleNormalPodToShenlong(String cluster, int timeLimit) { | |||
return this.shenlongNodeMgr.canScheduleNormalPod(cluster, timeLimit); | |||
} | |||
public void processShenlongPodStrategy(String cluster, PodCreateStrategy podCreateStrategy, String containers) { | |||
this.shenlongNodeMgr.processPodCreateStrategy(cluster, podCreateStrategy, containers); | |||
} | |||
// 神龙------------end------------- | |||
// GPU------------start------------- | |||
public boolean canScheduleNormalPodToGpu(String cluster, int timeLimit) { | |||
return this.gpuNodeMgr.canScheduleNormalPod(cluster, timeLimit); | |||
} | |||
public void processGpuPodStrategy(String cluster, PodCreateStrategy podCreateStrategy, String containers) { | |||
this.gpuNodeMgr.processPodCreateStrategy(cluster, podCreateStrategy, containers); | |||
} | |||
// GPU------------end------------- | |||
// oj------------start------------- | |||
public boolean canScheduleNormalPodToOj(String cluster, int timeLimit) { | |||
return this.ojNodeMgr.canScheduleNormalPod(cluster, timeLimit); | |||
} | |||
public void ojEvaStat(String cluster, String nodeName) { | |||
((OjNodeMgr) this.ojNodeMgr).ojEvaStat(cluster, nodeName); | |||
} | |||
public void ojEvaTimeoutStat(String cluster, String nodeName) { | |||
((OjNodeMgr) this.ojNodeMgr).ojEvaTimeoutStat(cluster, nodeName); | |||
} | |||
// oj------------end------------- | |||
public List<String> getNoSchedulableNodes(String cluster) { | |||
List<String> list = new ArrayList<>(); | |||
list.addAll(this.shenlongNodeMgr.getNoSchedulableNodes(cluster)); | |||
list.addAll(this.gpuNodeMgr.getNoSchedulableNodes(cluster)); | |||
list.addAll(this.ojNodeMgr.getNoSchedulableNodes(cluster)); | |||
list.addAll(this.normalNodeMgr.getNoSchedulableNodes(cluster)); | |||
return list; | |||
} | |||
public void processCreatePodResult(String cluster, CreatePodResult result) { | |||
boolean processed = this.shenlongNodeMgr.processCreatePodResult(cluster, result); | |||
if (processed) { | |||
return; | |||
} | |||
processed = this.gpuNodeMgr.processCreatePodResult(cluster, result); | |||
if (processed) { | |||
return; | |||
} | |||
processed = this.ojNodeMgr.processCreatePodResult(cluster, result); | |||
if (processed) { | |||
return; | |||
} | |||
this.normalNodeMgr.processCreatePodResult(cluster, result); | |||
} | |||
} |
@@ -0,0 +1,89 @@ | |||
package com.imitate.common.k8s.mgr.node; | |||
import com.imitate.common.k8s.mgr.ClusterManager; | |||
import com.imitate.common.k8s.mgr.node.cluster.ClusterGpuNodeMgr; | |||
import com.imitate.common.k8s.mgr.node.cluster.ClusterNodeMgrIf; | |||
import com.imitate.common.k8s.bean.ClusterInfo; | |||
import com.imitate.common.k8s.bean.CreatePodResult; | |||
import com.imitate.common.k8s.bean.PodCreateStrategy; | |||
import com.imitate.common.sys.settings.AppConfig; | |||
import com.imitate.common.bean.BeanFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Component; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
/** | |||
* GPU节点管理 | |||
*/ | |||
@Component | |||
public class GpuNodeMgr implements NodeMgrIf { | |||
// Map<cluster, ClusterNodeMgrIf> | |||
private final Map<String, ClusterNodeMgrIf> mgrMap = new HashMap<>(); | |||
@Autowired | |||
private ClusterManager clusterManager; | |||
@Autowired | |||
private AppConfig appConfig; | |||
@Override | |||
public void check() { | |||
// 没有Mgr的集群,初始化Mgr | |||
Map<String, ClusterInfo> clusterMap = clusterManager.getClusterInfo(); | |||
for (ClusterInfo cInfo : clusterMap.values()) { | |||
String cluster = cInfo.getClusterConfig().getName(); | |||
if (mgrMap.get(cluster) == null) { | |||
if (cInfo.getClusterConfig().notSupportImage(appConfig.getGpuImage())) { | |||
continue; | |||
} | |||
ClusterGpuNodeMgr mgr = BeanFactory.getObejct(ClusterGpuNodeMgr.class); | |||
mgr.setCluster(cluster); | |||
mgr.start(); | |||
mgrMap.put(cluster, mgr); | |||
} | |||
} | |||
// 已经删除的集群,删除掉Mgr | |||
for (String cluster : mgrMap.keySet()) { | |||
if (!clusterMap.containsKey(cluster)) { | |||
ClusterNodeMgrIf mgr = mgrMap.remove(cluster); | |||
mgr.stop(); | |||
} | |||
} | |||
} | |||
@Override | |||
public boolean canScheduleNormalPod(String cluster, int timeLimit) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
return mgr != null && mgr.canScheduleNormalPod(timeLimit); | |||
} | |||
@Override | |||
public List<String> getNoSchedulableNodes(String cluster) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
return mgr == null ? Collections.emptyList() : mgr.getNoSchedulableNodes(); | |||
} | |||
@Override | |||
public void processPodCreateStrategy(String cluster, PodCreateStrategy podCreateStrategy, String containers) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
if (mgr != null) { | |||
mgr.processPodCreateStrategy(podCreateStrategy, containers); | |||
} | |||
} | |||
@Override | |||
public boolean processCreatePodResult(String cluster, CreatePodResult result) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
return mgr != null && mgr.processCreatePodResult(result); | |||
} | |||
} |
@@ -0,0 +1,48 @@ | |||
package com.imitate.common.k8s.mgr.node; | |||
import com.imitate.common.k8s.bean.CreatePodResult; | |||
import com.imitate.common.k8s.bean.PodCreateStrategy; | |||
import java.util.List; | |||
/** | |||
* node管理接口 | |||
*/ | |||
public interface NodeMgrIf { | |||
/** | |||
* 检测集群管理器等 | |||
*/ | |||
void check(); | |||
/** | |||
* 节点能否调度普通pod | |||
* | |||
* @return | |||
*/ | |||
boolean canScheduleNormalPod(String cluster, int timeLimit); | |||
/** | |||
* 获取不可调度的节点列表 | |||
* | |||
* @return | |||
*/ | |||
List<String> getNoSchedulableNodes(String cluster); | |||
/** | |||
* 处理pod创建策略 | |||
* | |||
* @param podCreateStrategy | |||
* @param containers | |||
*/ | |||
void processPodCreateStrategy(String cluster, PodCreateStrategy podCreateStrategy, String containers); | |||
/** | |||
* 处理创建pod结果 | |||
* | |||
* @param result | |||
* @return 已经处理,返回true;不能处理,返回false。 | |||
*/ | |||
boolean processCreatePodResult(String cluster, CreatePodResult result); | |||
} |
@@ -0,0 +1,85 @@ | |||
package com.imitate.common.k8s.mgr.node; | |||
import com.imitate.common.k8s.mgr.ClusterManager; | |||
import com.imitate.common.k8s.mgr.node.cluster.ClusterNodeMgrIf; | |||
import com.imitate.common.k8s.mgr.node.cluster.ClusterNormalNodeMgr; | |||
import com.imitate.common.k8s.bean.ClusterInfo; | |||
import com.imitate.common.k8s.bean.CreatePodResult; | |||
import com.imitate.common.k8s.bean.PodCreateStrategy; | |||
import com.imitate.common.bean.BeanFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Component; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
/** | |||
* 普通 node 管理 | |||
*/ | |||
@Component | |||
public class NormalNodeMgr implements NodeMgrIf { | |||
// Map<cluster, ClusterNodeMgrIf> | |||
private final Map<String, ClusterNodeMgrIf> mgrMap = new HashMap<>(); | |||
@Autowired | |||
private ClusterManager clusterManager; | |||
@Override | |||
public void check() { | |||
// 没有Mgr的集群,初始化Mgr | |||
Map<String, ClusterInfo> clusterMap = clusterManager.getClusterInfo(); | |||
for (String cluster : clusterMap.keySet()) { | |||
if (mgrMap.get(cluster) == null) { | |||
ClusterNormalNodeMgr mgr = BeanFactory.getObejct(ClusterNormalNodeMgr.class); | |||
mgr.setCluster(cluster); | |||
mgr.start(); | |||
mgrMap.put(cluster, mgr); | |||
} | |||
} | |||
// 已经删除的集群,删除掉Mgr | |||
for (String cluster : mgrMap.keySet()) { | |||
if (!clusterMap.containsKey(cluster)) { | |||
ClusterNodeMgrIf mgr = mgrMap.remove(cluster); | |||
mgr.stop(); | |||
} | |||
} | |||
} | |||
@Override | |||
public boolean canScheduleNormalPod(String cluster, int timeLimit) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
return mgr != null && mgr.canScheduleNormalPod(timeLimit); | |||
} | |||
/** | |||
* 暂时只有超时的情况,不可调度 | |||
* | |||
* @return | |||
*/ | |||
@Override | |||
public List<String> getNoSchedulableNodes(String cluster) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
return mgr == null ? Collections.emptyList() : mgr.getNoSchedulableNodes(); | |||
} | |||
@Override | |||
public void processPodCreateStrategy(String cluster, PodCreateStrategy podCreateStrategy, String containers) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
if (mgr != null) { | |||
mgr.processPodCreateStrategy(podCreateStrategy, containers); | |||
} | |||
} | |||
@Override | |||
public boolean processCreatePodResult(String cluster, CreatePodResult result) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
return mgr != null && mgr.processCreatePodResult(result); | |||
} | |||
} |
@@ -0,0 +1,107 @@ | |||
package com.imitate.common.k8s.mgr.node; | |||
import com.imitate.common.k8s.mgr.ClusterManager; | |||
import com.imitate.common.k8s.mgr.node.cluster.ClusterNodeMgrIf; | |||
import com.imitate.common.k8s.mgr.node.cluster.ClusterOjNodeMgr; | |||
import com.imitate.common.k8s.bean.ClusterInfo; | |||
import com.imitate.common.k8s.bean.CreatePodResult; | |||
import com.imitate.common.k8s.bean.PodCreateStrategy; | |||
import com.imitate.common.bean.BeanFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Component; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
/** | |||
* oj 节点管理 | |||
*/ | |||
@Component | |||
public class OjNodeMgr implements NodeMgrIf { | |||
// Map<cluster, ClusterNodeMgrIf> | |||
private final Map<String, ClusterNodeMgrIf> mgrMap = new HashMap<>(); | |||
@Autowired | |||
private ClusterManager clusterManager; | |||
@Override | |||
public void check() { | |||
// 没有Mgr的集群,初始化Mgr | |||
Map<String, ClusterInfo> clusterMap = clusterManager.getClusterInfo(); | |||
for (ClusterInfo cInfo : clusterMap.values()) { | |||
String cluster = cInfo.getClusterConfig().getName(); | |||
if (mgrMap.get(cluster) == null) { | |||
if (!cInfo.getClusterConfig().getLocal()) { | |||
continue; | |||
} | |||
ClusterOjNodeMgr mgr = BeanFactory.getObejct(ClusterOjNodeMgr.class); | |||
mgr.setCluster(cluster); | |||
mgr.start(); | |||
mgrMap.put(cluster, mgr); | |||
} | |||
} | |||
// 已经删除的集群,删除掉Mgr | |||
for (String cluster : mgrMap.keySet()) { | |||
if (!clusterMap.containsKey(cluster)) { | |||
ClusterNodeMgrIf mgr = mgrMap.remove(cluster); | |||
mgr.stop(); | |||
} | |||
} | |||
} | |||
public ClusterOjNodeMgr getClusterOjNodeMgr(String cluster) { | |||
return (ClusterOjNodeMgr) mgrMap.get(cluster); | |||
} | |||
public void clearOjEvaStat() { | |||
for (ClusterNodeMgrIf mgr : mgrMap.values()) { | |||
((ClusterOjNodeMgr) mgr).clearOjEvaStat(); | |||
} | |||
} | |||
public void ojEvaStat(String cluster, String nodeName) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
if (mgr != null) { | |||
((ClusterOjNodeMgr) mgr).ojEvaStat(nodeName); | |||
} | |||
} | |||
public void ojEvaTimeoutStat(String cluster, String nodeName) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
if (mgr != null) { | |||
((ClusterOjNodeMgr) mgr).ojEvaTimeoutStat(nodeName); | |||
} | |||
} | |||
@Override | |||
public boolean canScheduleNormalPod(String cluster, int timeLimit) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
return mgr != null && mgr.canScheduleNormalPod(timeLimit); | |||
} | |||
@Override | |||
public List<String> getNoSchedulableNodes(String cluster) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
return mgr == null ? Collections.emptyList() : mgr.getNoSchedulableNodes(); | |||
} | |||
@Override | |||
public void processPodCreateStrategy(String cluster, PodCreateStrategy podCreateStrategy, String containers) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
if (mgr != null) { | |||
mgr.processPodCreateStrategy(podCreateStrategy, containers); | |||
} | |||
} | |||
@Override | |||
public boolean processCreatePodResult(String cluster, CreatePodResult result) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
return mgr != null && mgr.processCreatePodResult(result); | |||
} | |||
} |
@@ -0,0 +1,89 @@ | |||
package com.imitate.common.k8s.mgr.node; | |||
import com.imitate.common.k8s.mgr.ClusterManager; | |||
import com.imitate.common.k8s.mgr.node.cluster.ClusterNodeMgrIf; | |||
import com.imitate.common.k8s.mgr.node.cluster.ClusterShenlongNodeMgr; | |||
import com.imitate.common.k8s.bean.ClusterInfo; | |||
import com.imitate.common.k8s.bean.CreatePodResult; | |||
import com.imitate.common.k8s.bean.PodCreateStrategy; | |||
import com.imitate.common.sys.settings.AppConfig; | |||
import com.imitate.common.bean.BeanFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Component; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
/** | |||
* 神龙节点管理 | |||
*/ | |||
@Component | |||
public class ShenlongNodeMgr implements NodeMgrIf { | |||
// Map<cluster, ClusterNodeMgrIf> | |||
private final Map<String, ClusterNodeMgrIf> mgrMap = new HashMap<>(); | |||
@Autowired | |||
private ClusterManager clusterManager; | |||
@Autowired | |||
private AppConfig appConfig; | |||
@Override | |||
public void check() { | |||
// 没有Mgr的集群,初始化Mgr | |||
Map<String, ClusterInfo> clusterMap = clusterManager.getClusterInfo(); | |||
for (ClusterInfo cInfo : clusterMap.values()) { | |||
String cluster = cInfo.getClusterConfig().getName(); | |||
if (mgrMap.get(cluster) == null) { | |||
if (cInfo.getClusterConfig().notSupportImage(appConfig.getShenlongImageCharacter())) { | |||
continue; | |||
} | |||
ClusterShenlongNodeMgr mgr = BeanFactory.getObejct(ClusterShenlongNodeMgr.class); | |||
mgr.setCluster(cluster); | |||
mgr.start(); | |||
mgrMap.put(cluster, mgr); | |||
} | |||
} | |||
// 已经删除的集群,删除掉Mgr | |||
for (String cluster : mgrMap.keySet()) { | |||
if (!clusterMap.containsKey(cluster)) { | |||
ClusterNodeMgrIf mgr = mgrMap.remove(cluster); | |||
mgr.stop(); | |||
} | |||
} | |||
} | |||
@Override | |||
public boolean canScheduleNormalPod(String cluster, int timeLimit) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
return mgr != null && mgr.canScheduleNormalPod(timeLimit); | |||
} | |||
@Override | |||
public List<String> getNoSchedulableNodes(String cluster) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
return mgr == null ? Collections.emptyList() : mgr.getNoSchedulableNodes(); | |||
} | |||
@Override | |||
public void processPodCreateStrategy(String cluster, PodCreateStrategy podCreateStrategy, String containers) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
if (mgr != null) { | |||
mgr.processPodCreateStrategy(podCreateStrategy, containers); | |||
} | |||
} | |||
@Override | |||
public boolean processCreatePodResult(String cluster, CreatePodResult result) { | |||
ClusterNodeMgrIf mgr = mgrMap.get(cluster); | |||
return mgr != null && mgr.processCreatePodResult(result); | |||
} | |||
} |
@@ -0,0 +1,311 @@ | |||
package com.imitate.common.k8s.mgr.node.cluster; | |||
import com.imitate.common.constant.TpCsts; | |||
import com.imitate.common.k8s.constant.BridgeNodeCsts; | |||
import com.imitate.common.k8s.bean.CreatePodResult; | |||
import com.imitate.common.k8s.bean.NodeRes; | |||
import com.imitate.common.k8s.bean.PodCreateStrategy; | |||
import com.imitate.common.k8s.service.K8sService; | |||
import com.imitate.common.k8s.util.ContainerUtil; | |||
import com.imitate.common.k8s.util.K8sUtils; | |||
import com.imitate.common.sys.competitor.LockCompetitor; | |||
import com.imitate.common.sys.constant.SysConfigCsts; | |||
import com.imitate.common.sys.service.SysConfigService; | |||
import com.imitate.common.sys.settings.AppConfig; | |||
import com.imitate.common.util.ThreadUtils; | |||
import io.fabric8.kubernetes.api.model.Node; | |||
import io.fabric8.kubernetes.api.model.Pod; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.beans.factory.annotation.Qualifier; | |||
import org.springframework.context.annotation.Scope; | |||
import org.springframework.stereotype.Component; | |||
import java.util.*; | |||
import java.util.concurrent.ConcurrentHashMap; | |||
/** | |||
* 集群GPU节点管理 | |||
*/ | |||
@Scope("prototype") // 每个集群一个实例 | |||
@Component | |||
public class ClusterGpuNodeMgr implements ClusterNodeMgrIf { | |||
private final Logger logger = LoggerFactory.getLogger(getClass()); | |||
// Map<nodeName, 恢复正常时间>, 过载节点记录在此,到了恢复时间,即可重新分配pod | |||
private final Map<String, Long> overloadMap = new ConcurrentHashMap<>(); | |||
// 所有 pod request cpu之和阈值,达到阈值,不再分配普通pod到gpu节点 | |||
private volatile double requestCpuThreshold = 0; | |||
// pod request cpu总量 | |||
private volatile double requestCpuTotal = 0; | |||
// 节点资源,Map<nodeIp, NodeRes> | |||
private volatile Map<String, NodeRes> nodeResMap = new HashMap<>(); | |||
private String cluster; | |||
private volatile boolean terminated = false; | |||
@Qualifier("podScheduleLockCompetitor") | |||
@Autowired | |||
private LockCompetitor lockCompetitor; | |||
@Autowired | |||
private K8sService k8sService; | |||
@Autowired | |||
private SysConfigService sysConfigService; | |||
@Autowired | |||
private AppConfig appConfig; | |||
@Override | |||
public void start() { | |||
new Thread(() -> { | |||
this.checkNodePressure(); | |||
}).start(); | |||
new Thread(() -> { | |||
this.checkNodeRecovery(); | |||
}).start(); | |||
} | |||
// 检测节点是否恢复 | |||
private void checkNodeRecovery() { | |||
while (!terminated) { | |||
try { | |||
if (lockCompetitor.winLock()) { | |||
checkNodeRecoveryInner(); | |||
} | |||
} catch (Throwable t) { | |||
logger.error("GPU节点超时到期后,尝试创建pod,以检测节点是否恢复正常出错, cluster:{}", cluster, t); | |||
} finally { | |||
ThreadUtils.sleep(60 * 1000); | |||
} | |||
} | |||
} | |||
private void checkNodeRecoveryInner() { | |||
long now = System.currentTimeMillis(); | |||
Map<String, Long> tempMap = overloadMap; | |||
Set<String> keySet = tempMap.keySet(); | |||
for (String nodeName : keySet) { | |||
if (!nodeResMap.containsKey(nodeName)) { | |||
continue; | |||
} | |||
if (tempMap.get(nodeName) > now) { | |||
continue; | |||
} | |||
int timeLimit = (int) sysConfigService.getCreatePodCostMaxTime() / 1000 / 2; | |||
String podName = "checkpod-" + appConfig.getBridgeInstanceName(); | |||
try { | |||
Boolean success = k8sService.createCheckPod(cluster, podName, nodeName, timeLimit, | |||
SysConfigCsts.CHECK_POD_CONTAINER_IMAGE); | |||
if (success) { | |||
k8sService.updateNodeLabel(cluster, nodeName, "type", "others"); | |||
overloadMap.remove(nodeName); | |||
logger.info("GPU节点创建检测pod成功,超时状态结束, 节点:{}, cluster:{}", nodeName, cluster); | |||
} else { | |||
overloadMap.put(nodeName, | |||
System.currentTimeMillis() + sysConfigService.getGpuNodeOverloadExpireTime()); | |||
logger.info("GPU节点创建检测pod失败,超时状态继续, 节点:{}, cluster:{}", nodeName, cluster); | |||
} | |||
} catch (Exception e) { | |||
logger.error("检查GPU节点创建pod出错,image: {}, 节点:{}, cluster:{}", SysConfigCsts.CHECK_POD_CONTAINER_IMAGE, | |||
nodeName, cluster, e); | |||
} finally { | |||
k8sService.deletePod(cluster, podName); | |||
} | |||
} | |||
} | |||
private void checkNodePressure() { | |||
while (!terminated) { | |||
try { | |||
double totalCpu = 0; // 所有节点cpu总量 | |||
double requestCpuTotalTmp = 0; // 所有GPU pod request cpu总量 | |||
Map<String, NodeRes> nodeResMapTmp = new HashMap<>(); | |||
List<Node> nodes = k8sService.getNodeListWithLabel(cluster, TpCsts.GPU_NODE_LABEL_KEY, | |||
TpCsts.GPU_NODE_LABEL_VALUE); | |||
for (Node node : nodes) { | |||
NodeRes nodeRes = new NodeRes(); | |||
String nodeName = node.getMetadata().getName(); | |||
nodeRes.setNodeName(nodeName); | |||
String val = K8sUtils.getLabel(node, "type"); | |||
if (BridgeNodeCsts.NODE_LABEL_TYPE_UNDER_PRESSURE.equals(val)) { | |||
if (overloadMap.get(nodeName) == null) { | |||
overloadMap.put(nodeName, | |||
System.currentTimeMillis() + sysConfigService.getGpuNodeOverloadExpireTime()); | |||
} | |||
} else { | |||
overloadMap.remove(nodeName); | |||
} | |||
// 节点已分配 cpu | |||
double nodeRequestCpu = 0; | |||
List<Pod> podList = k8sService.getPodListWithNodeName(cluster, nodeName); | |||
for (Pod pod : podList) { | |||
if (pod.getMetadata() != null && pod.getMetadata().getLabels() != null) { | |||
String nodeRequire = pod.getMetadata().getLabels().get(TpCsts.NODE_REQUIRE); | |||
if (appConfig.getGpuImage().equals(nodeRequire)) {// 神龙pod | |||
double requestCpu = K8sUtils.getRequestCpu(pod); | |||
nodeRequestCpu += requestCpu; | |||
} | |||
} | |||
} | |||
nodeRes.setRequestCpu(nodeRequestCpu); | |||
// 节点可分配cpu | |||
double nodeCpu = K8sUtils.getNodeAllocatableCpu(node); | |||
nodeRes.setAllocatableCpu(nodeCpu); | |||
// 此节点GPU pod request cpu总量占可用cpu的比例 | |||
double requestRate = nodeRequestCpu / nodeCpu; | |||
nodeRes.setRequestCpuRate(requestRate); | |||
nodeResMapTmp.put(nodeName, nodeRes); | |||
totalCpu += nodeCpu; // 增加cpu总量 | |||
requestCpuTotalTmp += nodeRequestCpu; // 增加GPU pod request cpu总量 | |||
} | |||
nodeResMap = nodeResMapTmp; | |||
requestCpuTotal = requestCpuTotalTmp; | |||
requestCpuThreshold = totalCpu * sysConfigService.getGpuOverloadPercent() / 100; | |||
removeNoLongerExistNode(); // 删除不再存在的节点 | |||
} catch (Throwable t) { | |||
logger.error("检查GPU节点压力出错, cluster:{}", cluster, t); | |||
} finally { | |||
ThreadUtils.sleep(5000); | |||
} | |||
} | |||
} | |||
/** | |||
* 一些节点被回收,不再存在 | |||
*/ | |||
private void removeNoLongerExistNode() { | |||
Map<String, Long> tempMap = overloadMap; | |||
Set<String> keySet = tempMap.keySet(); | |||
for (String key : keySet) { | |||
if (!nodeResMap.containsKey(key)) { | |||
overloadMap.remove(key); | |||
} | |||
} | |||
} | |||
@Override | |||
public void setCluster(String cluster) { | |||
this.cluster = cluster; | |||
} | |||
@Override | |||
public boolean canScheduleNormalPod(int timeLimit) { | |||
boolean openReuse = sysConfigService.getGpuNodeOpenReuse(); | |||
if (!openReuse) { | |||
return false; | |||
} | |||
logger.debug("canScheduleNormalPod requestCpuTotal: {}, requestCpuThreshold: {}", requestCpuTotal, requestCpuThreshold); | |||
return requestCpuTotal < requestCpuThreshold; | |||
} | |||
/** | |||
* 暂时只有超时的情况,不可调度。 此处针对把普通pod调度到神龙节点。 | |||
* | |||
* @return | |||
*/ | |||
@Override | |||
public List<String> getNoSchedulableNodes() { | |||
return new ArrayList<>(overloadMap.keySet()); | |||
} | |||
/** | |||
* 处理pod调度策略 | |||
* | |||
* @param podCreateStrategy | |||
*/ | |||
@Override | |||
public void processPodCreateStrategy(PodCreateStrategy podCreateStrategy, String containers) { | |||
boolean gpu = containers.contains(appConfig.getGpuImage());// 可在上一层处理 | |||
if (!gpu) { | |||
return; | |||
} | |||
List<NodeRes> list = new ArrayList<>(nodeResMap.values()); | |||
if (list.size() <= 1) { | |||
return; | |||
} | |||
Collections.sort(list, new Comparator<NodeRes>() { | |||
@Override | |||
public int compare(NodeRes o1, NodeRes o2) { | |||
return o1.getRequestCpuRate().compareTo(o2.getRequestCpuRate()); | |||
} | |||
}); | |||
// bridge规模扩大时,必要改为集中分配 | |||
double requestCpu = ContainerUtil.getRequestCpu(containers); | |||
boolean existNoSchedulableNode = true; // 存在不可调度的节点 | |||
List<String> noList = new ArrayList<>(); | |||
for (int i = list.size() - 1; i > 0; i--) { | |||
NodeRes higher = list.get(i); | |||
NodeRes least = list.get(0); | |||
double unAffinityCpu = requestCpu + higher.getUnAffinityCpu(); | |||
// k8s是整体调度的,可能把很多GPU pod调度到资源较小的节点,普通pod调度到资源较大的节点。这时候更加需要把GPU pod往资源较大节点分配。 | |||
// 例如把3个GPU pod分配到8c节点,32c节点一个GPU pod都没有。 | |||
double allocatableCpu = least.getAllocatableCpu(); | |||
// 当前是2台bridge节点,每台可以调节50%的量, 再乘0.8是因为pod本来倾向于分配到较小节点 | |||
if (allocatableCpu * (higher.getRequestCpuRate() - least.getRequestCpuRate()) * 0.5 * 0.8 > unAffinityCpu) { | |||
noList.add(higher.getNodeName()); | |||
higher.addUnAffinityCpu(requestCpu); | |||
} else { | |||
existNoSchedulableNode = false; | |||
} | |||
// 3种情况检测结束:(1)没有更多不可调度的节点。(2)不可调度的节点数量超过9。(3)已经有超过一半的节点不可调度。 | |||
if (!existNoSchedulableNode || noList.size() > 9 || noList.size() >= list.size() / 2) { | |||
break; | |||
} | |||
} | |||
// 此处是针对GPU pod | |||
podCreateStrategy.setNoSchedulableGpuNodes(noList); | |||
} | |||
@Override | |||
public boolean processCreatePodResult(CreatePodResult result) { | |||
String nodeName = K8sUtils.getNodeName(result.getPod()); | |||
if (StringUtils.isBlank(nodeName)) { | |||
return false;// 调度失败,连节点都没有 | |||
} | |||
if (!nodeResMap.containsKey(nodeName)) { // 不是GPU节点 | |||
return false; | |||
} | |||
if (Boolean.FALSE.equals(result.getSuccess()) | |||
&& result.getMillisecondCost() >= sysConfigService.getCreatePodCostMaxTime()) { | |||
overloadMap.put(nodeName, System.currentTimeMillis() + sysConfigService.getGpuNodeOverloadExpireTime()); | |||
k8sService.updateNodeLabel(cluster, nodeName, BridgeNodeCsts.NODE_LABEL_TYPE, | |||
BridgeNodeCsts.NODE_LABEL_TYPE_UNDER_PRESSURE); | |||
logger.info("创建pod 超时 {}s,GPU节点超时状态开始,更改type label,pod:{}, node:{}, cluster:{}", | |||
result.getMillisecondCost() / 1000, K8sUtils.getPodName(result.getPod()), nodeName, cluster); | |||
} | |||
return true; | |||
} | |||
@Override | |||
public void stop() { | |||
terminated = true; | |||
} | |||
} |
@@ -0,0 +1,61 @@ | |||
package com.imitate.common.k8s.mgr.node.cluster; | |||
import com.imitate.common.k8s.bean.CreatePodResult; | |||
import com.imitate.common.k8s.bean.PodCreateStrategy; | |||
import java.util.List; | |||
/** | |||
* 集群节点管理接口。 | |||
*/ | |||
public interface ClusterNodeMgrIf { | |||
/** | |||
* 启动管理器 | |||
*/ | |||
void start(); | |||
/** | |||
* 集群 | |||
* | |||
* @param cluster | |||
* @return | |||
*/ | |||
void setCluster(String cluster); | |||
/** | |||
* 节点能否调度普通pod | |||
* | |||
* @return | |||
*/ | |||
boolean canScheduleNormalPod(int timeLimit); | |||
/** | |||
* 获取不可调度的节点列表 | |||
* | |||
* @return | |||
*/ | |||
List<String> getNoSchedulableNodes(); | |||
/** | |||
* 处理pod创建策略 | |||
* | |||
* @param podCreateStrategy | |||
* @param containers | |||
*/ | |||
void processPodCreateStrategy(PodCreateStrategy podCreateStrategy, String containers); | |||
/** | |||
* 处理创建pod结果 | |||
* | |||
* @param result | |||
* @return 已经处理,返回true;不能处理,返回false。 | |||
*/ | |||
boolean processCreatePodResult(CreatePodResult result); | |||
/** | |||
* 停止管理器 | |||
*/ | |||
void stop(); | |||
} | |||
@@ -0,0 +1,114 @@ | |||
package com.imitate.common.k8s.mgr.node.cluster; | |||
import com.imitate.common.k8s.bean.CreatePodResult; | |||
import com.imitate.common.k8s.bean.PodCreateStrategy; | |||
import com.imitate.common.k8s.util.K8sUtils; | |||
import com.imitate.common.sys.service.SysConfigService; | |||
import com.imitate.common.util.ThreadUtils; | |||
import io.fabric8.kubernetes.api.model.Pod; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.context.annotation.Scope; | |||
import org.springframework.stereotype.Component; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import java.util.concurrent.ConcurrentHashMap; | |||
/** | |||
* 集群普通节点管理。 | |||
*/ | |||
@Scope("prototype") // 每个集群一个实例 | |||
@Component | |||
public class ClusterNormalNodeMgr implements ClusterNodeMgrIf { | |||
private final Logger logger = LoggerFactory.getLogger(getClass()); | |||
// Map<nodeName, 恢复正常时间>, 过载节点记录在此,到了恢复时间,即可重新分配pod | |||
private final Map<String, Long> normalNodeOverloadMap = new ConcurrentHashMap<>(); | |||
// 集群 | |||
private String cluster; | |||
private volatile boolean terminated = false; | |||
@Autowired | |||
private SysConfigService sysConfigService; | |||
@Override | |||
public void start() { | |||
new Thread(() -> { | |||
this.doWork(); | |||
}).start(); | |||
} | |||
private void doWork() { | |||
while (!terminated) { | |||
try { | |||
checkoutNormalRecovery(); | |||
} catch (Throwable e) { | |||
logger.error("检查普通节点恢复正常出错, cluster:{}", cluster, e); | |||
} finally { | |||
ThreadUtils.sleep(5000); | |||
} | |||
} | |||
} | |||
private void checkoutNormalRecovery() { | |||
long now = System.currentTimeMillis(); | |||
Map<String, Long> tempMap = normalNodeOverloadMap; | |||
Set<String> keySet = tempMap.keySet(); | |||
for (String key : keySet) { | |||
if (tempMap.get(key) <= now) { | |||
normalNodeOverloadMap.remove(key); | |||
} | |||
} | |||
} | |||
@Override | |||
public void setCluster(String cluster) { | |||
this.cluster = cluster; | |||
} | |||
@Override | |||
public boolean canScheduleNormalPod(int timeLimit) { | |||
return true; | |||
} | |||
@Override | |||
public List<String> getNoSchedulableNodes() { | |||
return new ArrayList<>(normalNodeOverloadMap.keySet()); | |||
} | |||
@Override | |||
public void processPodCreateStrategy(PodCreateStrategy podCreateStrategy, String containers) { | |||
throw new UnsupportedOperationException("ClusterNormalNodeMgr 不支持的操作"); | |||
} | |||
@Override | |||
public boolean processCreatePodResult(CreatePodResult result) { | |||
Pod pod = result.getPod(); | |||
String nodeName = K8sUtils.getNodeName(pod); | |||
if (StringUtils.isBlank(nodeName)) { | |||
return false; // 调度失败,连节点都没有 | |||
} | |||
if (Boolean.FALSE.equals(result.getSuccess()) | |||
&& result.getMillisecondCost() >= sysConfigService.getCreatePodCostMaxTime()) { | |||
normalNodeOverloadMap.put(nodeName, | |||
System.currentTimeMillis() + sysConfigService.getNodeOverloadExpireTime()); | |||
logger.info("创建 pod 超时, cluster:{}, pod:{}, 耗时:{}s,节点{}超时状态开始", cluster, K8sUtils.getPodName(pod), | |||
result.getMillisecondCost() / 1000, nodeName); | |||
} | |||
return true; | |||
} | |||
@Override | |||
public void stop() { | |||
terminated = true; | |||
} | |||
} |
@@ -0,0 +1,348 @@ | |||
package com.imitate.common.k8s.mgr.node.cluster; | |||
import com.imitate.common.constant.TpCsts; | |||
import com.imitate.common.k8s.constant.BridgeNodeCsts; | |||
import com.imitate.common.k8s.bean.CreatePodResult; | |||
import com.imitate.common.k8s.bean.PodCreateStrategy; | |||
import com.imitate.common.k8s.service.K8sService; | |||
import com.imitate.common.k8s.util.K8sUtils; | |||
import com.imitate.common.sys.competitor.LockCompetitor; | |||
import com.imitate.common.sys.service.SysConfigService; | |||
import com.imitate.common.util.JedisUtil; | |||
import com.imitate.common.util.ThreadUtils; | |||
import io.fabric8.kubernetes.api.model.Node; | |||
import io.fabric8.kubernetes.api.model.Pod; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.beans.factory.annotation.Qualifier; | |||
import org.springframework.context.annotation.Scope; | |||
import org.springframework.stereotype.Component; | |||
import java.time.LocalDateTime; | |||
import java.time.format.DateTimeFormatter; | |||
import java.util.*; | |||
import java.util.concurrent.ConcurrentHashMap; | |||
/** | |||
* 集群 oj 节点管理 | |||
*/ | |||
@Scope("prototype") // 每个集群一个实例 | |||
@Component | |||
public class ClusterOjNodeMgr implements ClusterNodeMgrIf { | |||
private final Logger logger = LoggerFactory.getLogger(getClass()); | |||
// Map<nodeName, 恢复正常时间>, 过载节点记录在此,到了恢复时间,即可重新分配pod | |||
private final Map<String, Long> ojNodeOverloadMap = new ConcurrentHashMap<>(); | |||
private final int POD_CAN_TO_OJ_MAX_TIME_LIMIT = 10; | |||
private final String OJ_EVA_STAT_REDIS_CACHE_KEY = "ojEvaStat_"; | |||
private final String OJ_EVA_TIMEOUT_STAT_REDIS_CACHE_KEY = "ojEvaTimeoutStat_"; | |||
// HashMap即可,当前只要检测单线程访问 | |||
private final Map<String, Integer> ojNodeEvaCountMap = new ConcurrentHashMap<>(); | |||
private final Map<String, Integer> ojNodeEvaTimeoutCountMap = new ConcurrentHashMap<>(); | |||
private volatile long updateEvaTimeoutCountTimestamp = System.currentTimeMillis(); | |||
private volatile Map<String, Node> ojNodeMap = new ConcurrentHashMap<>(); | |||
// 集群 | |||
private String cluster; | |||
private volatile boolean terminated = false; | |||
@Qualifier("podScheduleLockCompetitor") | |||
@Autowired | |||
private LockCompetitor lockCompetitor; | |||
@Autowired | |||
private K8sService k8sService; | |||
@Autowired | |||
private SysConfigService sysConfigService; | |||
@Override | |||
public void start() { | |||
// new Thread(() -> { | |||
// this.checkOjNodePressure(); | |||
// }).start(); | |||
} | |||
private void checkOjNodePressure() { | |||
while (!terminated) { | |||
try { | |||
long nowTime = System.currentTimeMillis(); | |||
boolean cycleOver = nowTime - updateEvaTimeoutCountTimestamp > 10 * 1000;// 周期结束 | |||
if (cycleOver) { | |||
updateEvaTimeoutCountTimestamp = nowTime; | |||
} | |||
Map<String, Node> ojNodeMapTmp = new HashMap<>(); | |||
// 获取oj节点 | |||
List<Node> nodes = k8sService.getNodeListWithLabel(cluster, TpCsts.OJ_LABEL_KEY, TpCsts.OJ_LABEL_VALUE); | |||
// 更新节点压力情况 | |||
for (Node node : nodes) { | |||
String nodeName = node.getMetadata().getName(); | |||
ojNodeMapTmp.put(nodeName, node); | |||
if (lockCompetitor.winLock()) {// bridge调度中心节点 | |||
if (ojNodeOverloadMap.get(nodeName) == null) { | |||
checkOjNodePressure(node); | |||
} else if (ojNodeOverloadMap.get(nodeName) < nowTime) {// 压力时间已过 | |||
checkOjNodePressure(node); | |||
} | |||
} else {// bridge普通节点 | |||
String val = K8sUtils.getLabel(node, "type"); | |||
if (BridgeNodeCsts.NODE_LABEL_TYPE_UNDER_PRESSURE.equals(val)) { | |||
if (ojNodeOverloadMap.get(nodeName) == null) { | |||
ojNodeOverloadMap.put(nodeName, | |||
System.currentTimeMillis() + sysConfigService.getOjNodeOverloadExpireTime()); | |||
} | |||
} else { | |||
ojNodeOverloadMap.remove(nodeName); | |||
} | |||
} | |||
changeCount(nodeName, cycleOver); | |||
} | |||
ojNodeMap = ojNodeMapTmp; | |||
// 检查压力node是否仍然存在 | |||
for (String nodeName : ojNodeOverloadMap.keySet()) { | |||
boolean found = false; | |||
for (Node node : nodes) { | |||
String name = node.getMetadata().getName(); | |||
if (nodeName.equals(name)) { | |||
found = true; | |||
break; | |||
} | |||
} | |||
if (!found) {// 如果压力节点已经从k8s集群删除,则删除掉 | |||
ojNodeOverloadMap.remove(nodeName); | |||
} | |||
} | |||
} catch (Throwable t) { | |||
logger.error("检查oj节点压力出错, cluster:{}", cluster, t); | |||
} finally { | |||
ThreadUtils.sleep(5000); | |||
} | |||
} | |||
} | |||
private void checkOjNodePressure(Node node) { | |||
boolean isOverload = isOjNodeOverload(cluster, node); | |||
String nodeName = node.getMetadata().getName(); | |||
boolean isEvaTimeout = isOjNodeEvaTimeout(nodeName); | |||
if (isOverload || isEvaTimeout) { | |||
if (ojNodeOverloadMap.get(nodeName) == null) { | |||
// k8sService.updateNodeLabel(cluster, nodeName, "type", BridgeNodeCsts.NODE_LABEL_TYPE_UNDER_PRESSURE); | |||
logger.info("oj节点压力过大, cluster:{}, nodeName:{}, isOverload: {}, isEvaTimeout:{}", cluster, | |||
nodeName, isOverload, isEvaTimeout); | |||
} else { | |||
logger.info("oj节点有压力,超时状态继续, cluster:{}, nodeName:{}, isOverload: {}, isEvaTimeout:{}", cluster, | |||
nodeName, isOverload, isEvaTimeout); | |||
} | |||
ojNodeOverloadMap.put(nodeName, | |||
System.currentTimeMillis() + sysConfigService.getOjNodeOverloadExpireTime()); | |||
} else { | |||
if (ojNodeOverloadMap.get(nodeName) != null) { | |||
// k8sService.updateNodeLabel(cluster, nodeName, "type", "others"); | |||
ojNodeOverloadMap.remove(nodeName); | |||
logger.info("oj节点压力恢复正常, cluster:{}, nodeName:{}", cluster, nodeName); | |||
} | |||
} | |||
} | |||
/** | |||
* 更改计数 | |||
* | |||
* @param nodeName | |||
* @param cycleOver | |||
*/ | |||
private void changeCount(String nodeName, boolean cycleOver) { | |||
// 超时计数 | |||
if (cycleOver) { | |||
String newOjEvaTimeoutCountStr = JedisUtil.get(OJ_EVA_TIMEOUT_STAT_REDIS_CACHE_KEY + nodeName); | |||
int newOjEvaTimeoutCount = StringUtils.isNotEmpty(newOjEvaTimeoutCountStr) | |||
? Integer.parseInt(newOjEvaTimeoutCountStr) | |||
: 0; | |||
ojNodeEvaTimeoutCountMap.put(nodeName, newOjEvaTimeoutCount); | |||
} | |||
// 评测计数 | |||
String newOjEvaCountStr = JedisUtil.get(OJ_EVA_STAT_REDIS_CACHE_KEY + nodeName); | |||
int newOjEvaCount = StringUtils.isNotEmpty(newOjEvaCountStr) ? Integer.parseInt(newOjEvaCountStr) : 0; | |||
ojNodeEvaCountMap.put(nodeName, newOjEvaCount); | |||
} | |||
private boolean isOjNodeEvaTimeout(String nodeName) { | |||
String newOjEvaTimeoutCountStr = JedisUtil.get(OJ_EVA_TIMEOUT_STAT_REDIS_CACHE_KEY + nodeName); | |||
int newOjEvaTimeoutCount = StringUtils.isNotEmpty(newOjEvaTimeoutCountStr) | |||
? Integer.parseInt(newOjEvaTimeoutCountStr) | |||
: 0; | |||
Integer ojEvaTimeoutCount = ojNodeEvaTimeoutCountMap.get(nodeName) != null | |||
? ojNodeEvaTimeoutCountMap.get(nodeName) | |||
: 0; | |||
int maxEvaTimeoutNum = sysConfigService.getOjNodeMaxEvaTimeoutNum(); | |||
logger.debug( | |||
"oj超时检测详情, cluster:{}, nodeName:{}, newOjEvaTimeoutCount: {}, ojEvaTimeoutCount:{}, maxEvaTimeoutNum:{}", | |||
cluster, nodeName, newOjEvaTimeoutCount, ojEvaTimeoutCount, maxEvaTimeoutNum); | |||
int timeoutNum = newOjEvaTimeoutCount - ojEvaTimeoutCount; | |||
return timeoutNum > maxEvaTimeoutNum; | |||
} | |||
private boolean isOjNodeOverload(final String cluster, Node node) { | |||
String nodeName = node.getMetadata().getName(); | |||
List<Pod> podList = k8sService.getPodListReversely(cluster, nodeName, TpCsts.OJ_LABEL_KEY, TpCsts.OJ_LABEL_VALUE); | |||
double nodeCPU = K8sUtils.getNodeCapacityCpu(node); | |||
// 根据节点cpu与最大可使用算力百分比计算最大可使用算力(8核节点算力为100) | |||
double ojNodePowerThreshold = nodeCPU / 8 * sysConfigService.getOjNodeMaxPowPercent(); | |||
// 计算普通pod使用算力(普通pod评测使用算力为3) | |||
int evaluatePodUsedPower = podList.size() * 8; | |||
// 计算oj pod使用算力 | |||
int ojPodUsedPower = getOjPodUsedPower(nodeName); | |||
// 当前node实际使用算力 | |||
int ojNodeUsedPower = evaluatePodUsedPower + ojPodUsedPower; | |||
logger.debug( | |||
"oj压力检测详情, cluster:{}, nodeName:{}, ojNodePowerThreshold: {}, evaluatePodUsedPower:{}, ojPodUsedPower:{}", | |||
cluster, nodeName, ojNodePowerThreshold, evaluatePodUsedPower, ojPodUsedPower); | |||
return ojNodeUsedPower > ojNodePowerThreshold; | |||
} | |||
private int getOjPodUsedPower(String nodeName) { | |||
String newOjEvaCountStr = JedisUtil.get(OJ_EVA_STAT_REDIS_CACHE_KEY + nodeName); | |||
int newOjEvaCount = StringUtils.isNotEmpty(newOjEvaCountStr) ? Integer.parseInt(newOjEvaCountStr) : 0; | |||
Integer ojEvaCount = ojNodeEvaCountMap.get(nodeName) != null ? ojNodeEvaCountMap.get(nodeName) : 0; | |||
return newOjEvaCount - ojEvaCount; | |||
} | |||
/** | |||
* 清理评测统计 | |||
*/ | |||
public void clearOjEvaStat() { | |||
// 获取oj节点 | |||
List<Node> nodes = k8sService.getNodeListWithLabel(cluster, TpCsts.OJ_LABEL_KEY, TpCsts.OJ_LABEL_VALUE); | |||
for (Node node : nodes) { | |||
String nodeName = node.getMetadata().getName(); | |||
JedisUtil.del(OJ_EVA_STAT_REDIS_CACHE_KEY + nodeName); | |||
ojNodeEvaCountMap.put(nodeName, 0); | |||
JedisUtil.del(OJ_EVA_TIMEOUT_STAT_REDIS_CACHE_KEY + nodeName); | |||
ojNodeEvaTimeoutCountMap.put(nodeName, 0); | |||
} | |||
} | |||
public void ojEvaStat(String nodeName) { | |||
JedisUtil.incrBy(OJ_EVA_STAT_REDIS_CACHE_KEY + nodeName, 1); | |||
} | |||
public void ojEvaTimeoutStat(String nodeName) { | |||
JedisUtil.incrBy(OJ_EVA_TIMEOUT_STAT_REDIS_CACHE_KEY + nodeName, 1); | |||
} | |||
@Override | |||
public void setCluster(String cluster) { | |||
this.cluster = cluster; | |||
} | |||
@Override | |||
public boolean canScheduleNormalPod(int timeLimit) { | |||
return (timeLimit <= POD_CAN_TO_OJ_MAX_TIME_LIMIT) && sysConfigService.getOjNodeOpenReuse() | |||
&& ojNodeMap.size() > ojNodeOverloadMap.size(); | |||
} | |||
/** | |||
* 暂时只有超时的情况,不可调度 | |||
* | |||
* @return | |||
*/ | |||
@Override | |||
public List<String> getNoSchedulableNodes() { | |||
return new ArrayList<>(ojNodeOverloadMap.keySet()); | |||
} | |||
/** | |||
* 处理pod调度策略 | |||
* | |||
* @param podCreateStrategy | |||
*/ | |||
@Override | |||
public void processPodCreateStrategy(PodCreateStrategy podCreateStrategy, String containers) { | |||
throw new UnsupportedOperationException("oj 共享pod, 不支持processPodCreateStrategy"); | |||
} | |||
@Override | |||
public boolean processCreatePodResult(CreatePodResult result) { | |||
String nodeName = K8sUtils.getNodeName(result.getPod()); | |||
if (StringUtils.isBlank(nodeName)) { | |||
return false;// 调度失败,连节点都没有 | |||
} | |||
if (!ojNodeMap.containsKey(nodeName)) { // 不是oj节点 | |||
return false; | |||
} | |||
if (Boolean.FALSE.equals(result.getSuccess()) | |||
&& result.getMillisecondCost() >= sysConfigService.getCreatePodCostMaxTime()) { | |||
ojNodeOverloadMap.put(nodeName, | |||
System.currentTimeMillis() + sysConfigService.getOjNodeOverloadExpireTime()); | |||
// k8sService.updateNodeLabel(cluster, nodeName, "type", BridgeNodeCsts.NODE_LABEL_TYPE_UNDER_PRESSURE); | |||
logger.info("创建pod {} 超时 {}s,oj节点{}超时状态开始, cluster:{}", K8sUtils.getPodName(result.getPod()), | |||
result.getMillisecondCost() / 1000, nodeName, cluster); | |||
} | |||
return true; | |||
} | |||
@Override | |||
public void stop() { | |||
terminated = true; | |||
} | |||
public Double ojNodeOverloadRatio() { | |||
if (ojNodeMap.size() > 0) { | |||
return ojNodeOverloadMap.size() / (double) ojNodeMap.size(); | |||
} | |||
return null; | |||
} | |||
public String getNoOverloadOjNodeName() { | |||
Map<String, Node> ojNodeMapTemp = ojNodeMap; | |||
Set<String> keySet = ojNodeMapTemp.keySet(); | |||
for (String nodeName : keySet) { | |||
if (!ojNodeOverloadMap.containsKey(nodeName)) { | |||
Node node = ojNodeMapTemp.get(nodeName); | |||
String capacityLabelValue = K8sUtils.getLabel(node, TpCsts.OJ_CAPACITY_NODE_LABEL_KEY); | |||
if (StringUtils.isNotEmpty(capacityLabelValue) | |||
&& LocalDateTime.now().isAfter(LocalDateTime.parse(capacityLabelValue, DateTimeFormatter.ofPattern("yyyyMMddHHmmss")))) { | |||
return nodeName; | |||
} | |||
} | |||
} | |||
return null; | |||
} | |||
public int getOjNodeSize() { | |||
return ojNodeMap.size(); | |||
} | |||
} |
@@ -0,0 +1,299 @@ | |||
package com.imitate.common.k8s.mgr.node.cluster; | |||
import com.imitate.common.constant.TpCsts; | |||
import com.imitate.common.k8s.bean.CreatePodResult; | |||
import com.imitate.common.k8s.bean.PodCreateStrategy; | |||
import com.imitate.common.k8s.constant.BridgeNodeCsts; | |||
import com.imitate.common.k8s.bean.NodeRes; | |||
import com.imitate.common.k8s.service.K8sService; | |||
import com.imitate.common.k8s.util.ContainerUtil; | |||
import com.imitate.common.k8s.util.K8sUtils; | |||
import com.imitate.common.sys.competitor.LockCompetitor; | |||
import com.imitate.common.sys.service.SysConfigService; | |||
import com.imitate.common.sys.settings.AppConfig; | |||
import com.imitate.common.util.ThreadUtils; | |||
import io.fabric8.kubernetes.api.model.Node; | |||
import io.fabric8.kubernetes.api.model.Pod; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.beans.factory.annotation.Qualifier; | |||
import org.springframework.context.annotation.Scope; | |||
import org.springframework.stereotype.Component; | |||
import java.util.*; | |||
import java.util.concurrent.ConcurrentHashMap; | |||
/** | |||
* 集群神龙节点管理 | |||
*/ | |||
@Scope("prototype") // 每个集群一个实例 | |||
@Component | |||
public class ClusterShenlongNodeMgr implements ClusterNodeMgrIf { | |||
private final Logger logger = LoggerFactory.getLogger(getClass()); | |||
// Map<nodeName, 恢复正常时间>, 过载节点记录在此,到了恢复时间,即可重新分配pod | |||
private final Map<String, Long> shenlongNodeOverloadMap = new ConcurrentHashMap<>(); | |||
// 所有神龙节点request cpu之和阈值,达到阈值,不再分配普通pod到神龙节点 | |||
private volatile double shenlongNodeRequestCpuThreshold = 0; | |||
// 神龙pod request cpu总量 | |||
private volatile double shenlongPodRequestCpuTotal = 0; | |||
// 神龙节点资源,Map<nodeIp, NodeRes> | |||
private volatile Map<String, NodeRes> shenlongNodeResMap = new HashMap<>(); | |||
private final String CHECK_POD_CONTAINER_IMAGE = "gcc-ssh:v1.0"; | |||
private String cluster; | |||
private volatile boolean terminated = false; | |||
@Qualifier("podScheduleLockCompetitor") | |||
@Autowired | |||
private LockCompetitor lockCompetitor; | |||
@Autowired | |||
private K8sService k8sService; | |||
@Autowired | |||
private SysConfigService sysConfigService; | |||
@Autowired | |||
private AppConfig appConfig; | |||
@Override | |||
public void start() { | |||
new Thread(() -> { | |||
this.checkShenlongNodePressure(); | |||
}).start(); | |||
new Thread(() -> { | |||
this.checkShenlongNodeRecovery(); | |||
}).start(); | |||
} | |||
// 检测神龙节点是否恢复 | |||
private void checkShenlongNodeRecovery() { | |||
while (!terminated) { | |||
try { | |||
if (lockCompetitor.winLock()) { | |||
checkShenlongNodeRecoveryInner(); | |||
} | |||
} catch (Throwable t) { | |||
logger.error("神龙节点超时到期后,尝试创建pod,以检测节点是否恢复正常出错, cluster:{}", cluster, t); | |||
} finally { | |||
ThreadUtils.sleep(60 * 1000); | |||
} | |||
} | |||
} | |||
private void checkShenlongNodeRecoveryInner() { | |||
long now = System.currentTimeMillis(); | |||
Map<String, Long> tempMap = shenlongNodeOverloadMap; | |||
Set<String> keySet = tempMap.keySet(); | |||
for (String nodeName : keySet) { | |||
if (!shenlongNodeResMap.containsKey(nodeName)) { | |||
continue; | |||
} | |||
if (tempMap.get(nodeName) > now) { | |||
continue; | |||
} | |||
int timeLimit = (int) sysConfigService.getCreatePodCostMaxTime() / 1000 / 2; | |||
String podName = "checkpod-" + appConfig.getBridgeInstanceName(); | |||
try { | |||
Boolean shenlongNodeCreatePodFlag = k8sService.createCheckPod(cluster, podName, nodeName, timeLimit, | |||
CHECK_POD_CONTAINER_IMAGE); | |||
if (shenlongNodeCreatePodFlag) { | |||
k8sService.updateNodeLabel(cluster, nodeName, "type", "others"); | |||
shenlongNodeOverloadMap.remove(nodeName); | |||
logger.info("神龙节点{}创建检测pod成功,超时状态结束, cluster:{}", nodeName, cluster); | |||
} else { | |||
shenlongNodeOverloadMap.put(nodeName, | |||
System.currentTimeMillis() + sysConfigService.getShenlongNodeOverloadExpireTime()); | |||
logger.info("神龙节点{}创建检测pod失败,超时状态继续, cluster:{}", nodeName, cluster); | |||
} | |||
} catch (Exception e) { | |||
logger.error("检查神龙节点创建pod出错,image: {}, cluster:{}", CHECK_POD_CONTAINER_IMAGE, cluster, e); | |||
} finally { | |||
k8sService.deletePod(cluster, podName); | |||
} | |||
} | |||
} | |||
private void checkShenlongNodePressure() { | |||
while (!terminated) { | |||
try { | |||
double totalCpu = 0; // 所有神龙节点,cpu总量 | |||
double shenlongPodRequestCpu = 0; // 所有神龙pod request cpu总量 | |||
Map<String, NodeRes> nodeResMapTmp = new HashMap<>(); | |||
List<Node> nodes = k8sService.getNodeListWithLabel(cluster, TpCsts.SHENLONG_NODE_LABEL_KEY, | |||
TpCsts.SHENLONG_NODE_LABEL_VALUE); | |||
for (Node node : nodes) { | |||
NodeRes nodeRes = new NodeRes(); | |||
String nodeName = node.getMetadata().getName(); | |||
nodeRes.setNodeName(nodeName); | |||
String val = K8sUtils.getLabel(node, "type"); | |||
if (BridgeNodeCsts.NODE_LABEL_TYPE_UNDER_PRESSURE.equals(val)) { | |||
if (shenlongNodeOverloadMap.get(nodeName) == null) { | |||
shenlongNodeOverloadMap.put(nodeName, | |||
System.currentTimeMillis() + sysConfigService.getShenlongNodeOverloadExpireTime()); | |||
} | |||
} else { | |||
shenlongNodeOverloadMap.remove(nodeName); | |||
} | |||
// 节点已分配 cpu | |||
double requestCpuTotal = 0; | |||
List<Pod> podList = k8sService.getPodListWithNodeName(cluster, nodeName); | |||
for (Pod pod : podList) { | |||
if (pod.getMetadata() != null && pod.getMetadata().getLabels() != null) { | |||
String nodeRequire = pod.getMetadata().getLabels().get(TpCsts.NODE_REQUIRE); | |||
if (appConfig.getShenlongImageCharacter().equals(nodeRequire)) {// 神龙pod | |||
double requestCpu = K8sUtils.getRequestCpu(pod); | |||
requestCpuTotal += requestCpu; | |||
} | |||
} | |||
} | |||
nodeRes.setRequestCpu(requestCpuTotal); | |||
// 节点可分配cpu | |||
double nodeCpu = K8sUtils.getNodeAllocatableCpu(node); | |||
nodeRes.setAllocatableCpu(nodeCpu); | |||
// 此节点神龙pod request cpu总量占可用cpu的比例 | |||
double requestRate = requestCpuTotal / nodeCpu; | |||
nodeRes.setRequestCpuRate(requestRate); | |||
nodeResMapTmp.put(nodeName, nodeRes); | |||
totalCpu += nodeCpu; // 增加cpu总量 | |||
shenlongPodRequestCpu += requestCpuTotal; // 增加神龙pod request cpu总量 | |||
} | |||
shenlongNodeResMap = nodeResMapTmp; | |||
shenlongPodRequestCpuTotal = shenlongPodRequestCpu; | |||
shenlongNodeRequestCpuThreshold = totalCpu * sysConfigService.getShenlongOverloadPercent() / 100; | |||
removeNoLongerExistNode(); // 删除不再存在的神龙节点 | |||
} catch (Throwable t) { | |||
logger.error("检查节点压力出错, cluster:{}", cluster, t); | |||
} finally { | |||
ThreadUtils.sleep(5000); | |||
} | |||
} | |||
} | |||
/** | |||
* 一些神龙节点被回收,不再存在 | |||
*/ | |||
private void removeNoLongerExistNode() { | |||
Map<String, Long> tempMap = shenlongNodeOverloadMap; | |||
Set<String> keySet = tempMap.keySet(); | |||
for (String key : keySet) { | |||
if (!shenlongNodeResMap.containsKey(key)) { | |||
shenlongNodeOverloadMap.remove(key); | |||
} | |||
} | |||
} | |||
@Override | |||
public void setCluster(String cluster) { | |||
this.cluster = cluster; | |||
} | |||
@Override | |||
public boolean canScheduleNormalPod(int timeLimit) { | |||
return shenlongPodRequestCpuTotal < shenlongNodeRequestCpuThreshold; | |||
} | |||
/** | |||
* 暂时只有超时的情况,不可调度。 此处针对把普通pod调度到神龙节点。 | |||
* | |||
* @return | |||
*/ | |||
@Override | |||
public List<String> getNoSchedulableNodes() { | |||
return new ArrayList<>(shenlongNodeOverloadMap.keySet()); | |||
} | |||
/** | |||
* 处理神龙pod调度策略 | |||
* | |||
* @param podCreateStrategy | |||
*/ | |||
@Override | |||
public void processPodCreateStrategy(PodCreateStrategy podCreateStrategy, String containers) { | |||
boolean shenlong = containers.contains(appConfig.getShenlongImageCharacter());// 可在上一层处理 | |||
if (!shenlong) { | |||
return; | |||
} | |||
List<NodeRes> list = new ArrayList<>(shenlongNodeResMap.values()); | |||
if (list.size() <= 1) { | |||
return; | |||
} | |||
Collections.sort(list, new Comparator<NodeRes>() { | |||
@Override | |||
public int compare(NodeRes o1, NodeRes o2) { | |||
return o1.getRequestCpuRate().compareTo(o2.getRequestCpuRate()); | |||
} | |||
}); | |||
// bridge规模扩大时,必要改为集中分配 | |||
double requestCpu = ContainerUtil.getRequestCpu(containers); | |||
List<String> noList = new ArrayList<>(); | |||
for (int i = list.size() - 1; i > 0; i--) { | |||
NodeRes higher = list.get(i); | |||
NodeRes least = list.get(0); | |||
double unAffinityCpu = requestCpu + higher.getUnAffinityCpu(); | |||
// allocatableCpu 应该取较大值。 | |||
// k8s是整体调度的,可能把很多神龙pod调度到资源较小的节点,普通pod调度到资源较大的节点。这时候更加需要把神龙pod往资源较大节点分配。 | |||
// 例如把3个神龙pod分配到8c节点,32c节点一个神龙pod都没有。 | |||
double allocatableCpu = Math.max(higher.getAllocatableCpu(), least.getAllocatableCpu()); | |||
// 当前是2台bridge节点,每台可以调节50%的量, 再乘0.8是因为pod本来倾向于分配到较小节点 | |||
if (allocatableCpu * (higher.getRequestCpuRate() - least.getRequestCpuRate()) * 0.5 * 0.8 > unAffinityCpu) { | |||
noList.add(higher.getNodeName()); | |||
higher.addUnAffinityCpu(requestCpu); | |||
} | |||
} | |||
// 此处是针对神龙 pod | |||
podCreateStrategy.setNoSchedulableShenlongNodes(noList); | |||
} | |||
@Override | |||
public boolean processCreatePodResult(CreatePodResult result) { | |||
String nodeName = K8sUtils.getNodeName(result.getPod()); | |||
if (StringUtils.isBlank(nodeName)) { | |||
return false;// 调度失败,连节点都没有 | |||
} | |||
if (!shenlongNodeResMap.containsKey(nodeName)) { // 不是神龙节点 | |||
return false; | |||
} | |||
if (Boolean.FALSE.equals(result.getSuccess()) | |||
&& result.getMillisecondCost() >= sysConfigService.getCreatePodCostMaxTime()) { | |||
shenlongNodeOverloadMap.put(nodeName, | |||
System.currentTimeMillis() + sysConfigService.getShenlongNodeOverloadExpireTime()); | |||
// k8sService.updateNodeLabel(cluster, nodeName, "type", BridgeNodeCsts.NODE_LABEL_TYPE_UNDER_PRESSURE); | |||
logger.info("创建pod {} 超时 {}s,神龙节点{}超时状态开始, cluster:{}", | |||
K8sUtils.getPodName(result.getPod()), result.getMillisecondCost() / 1000, nodeName, cluster); | |||
} | |||
return true; | |||
} | |||
@Override | |||
public void stop() { | |||
terminated = true; | |||
} | |||
} |
@@ -0,0 +1,67 @@ | |||
package com.imitate.common.k8s.pojo; | |||
import com.imitate.common.util.AbstractDO; | |||
import lombok.Data; | |||
import lombok.EqualsAndHashCode; | |||
import javax.persistence.Table; | |||
import java.time.LocalDateTime; | |||
@Data | |||
@EqualsAndHashCode(callSuper = false) | |||
@Table(name = "bridge_pod") | |||
public class BridgePod extends AbstractDO { | |||
private String name; | |||
private String tpiID; | |||
private String buildID; | |||
private String uid; | |||
private LocalDateTime k8sCreateTime; | |||
private LocalDateTime deleteTime; | |||
private String secKey; | |||
private LocalDateTime requestTime; | |||
private Double pull; | |||
private Double createPod; | |||
private Double execute; | |||
private Double evaluateAllTime; | |||
private String nodeName; | |||
private String nodeIp; | |||
private String downloadStatus; | |||
private String createPodStatus; | |||
public static final String CREATE_POD_STATUS_SUCCESS = "1"; | |||
public static final String CREATE_POD_STATUS_FAIL = "0"; | |||
private String compileStatus; | |||
private String runStatus; | |||
public static final String RUN_STATUS_FAIL = "-1"; | |||
public static final String RUN_STATUS_SUCCESS = "0"; | |||
public static final String RUN_STATUS_EXEC_FAIL = "1"; | |||
public static final String RUN_STATUS_TIMEOUT = "2"; | |||
public static final String RUN_STATUS_OTHERS_FAIL = "3"; | |||
private String status; | |||
private String imageName; | |||
private String imageVersion; | |||
private Double cpuLimit; | |||
private Integer memoryLimit; | |||
private Double cpuRequest; | |||
private Integer memoryRequest; | |||
} |
@@ -0,0 +1,40 @@ | |||
package com.imitate.common.k8s.pojo; | |||
import com.imitate.common.util.AbstractDO; | |||
import lombok.Data; | |||
import lombok.EqualsAndHashCode; | |||
import javax.persistence.Table; | |||
import java.time.LocalDateTime; | |||
/** | |||
* @author huqifeng | |||
* @Time 2021-8-9 | |||
* @Description pod创建超时后保存的错误pod信息 | |||
*/ | |||
@Data | |||
@EqualsAndHashCode(callSuper = false) | |||
@Table(name = "error_pod_info") | |||
public class ErrorPodInfo extends AbstractDO { | |||
private LocalDateTime errorTime; | |||
private String podName; | |||
private String nodeIp; | |||
private String tpiID; | |||
private String buildID; | |||
private String podPhase; | |||
private String podStatusReason; | |||
private String podStatusMessage; | |||
private String podConditionType; | |||
private String podConditionStatus; | |||
private String podConditionReason; | |||
private String podConditionMessage; | |||
private String containerStatusReady; | |||
private String containerStatusWaiting; | |||
private String containerStatusWaitingReason; | |||
private String containerStatusWaitingMessage; | |||
private String containerStatusTerminated; | |||
private String containerStatusTerminatedReason; | |||
private String containerStatusTerminatedMessage; | |||
private String imageExist; | |||
} |
@@ -0,0 +1,74 @@ | |||
package com.imitate.common.k8s.pojo; | |||
import com.imitate.common.util.AbstractDO; | |||
import lombok.Data; | |||
import lombok.EqualsAndHashCode; | |||
import javax.persistence.Table; | |||
/** | |||
* 评测每日统计 | |||
*/ | |||
@Data | |||
@EqualsAndHashCode(callSuper = false) | |||
@Table(name = "eva_day_stat") | |||
public class EvaDayStat extends AbstractDO { | |||
/** | |||
* 统计日期,如20200101 | |||
*/ | |||
private Integer statDate; | |||
/** | |||
* 评测总数 | |||
*/ | |||
private Integer evaTotal; | |||
/** | |||
* 代码下载大于5秒数 | |||
*/ | |||
private Integer pullSlow; | |||
/** | |||
* 代码下载失败数 | |||
*/ | |||
private Integer pullFail; | |||
/** | |||
* 创建pod大于5秒数 | |||
*/ | |||
private Integer createPodSlow; | |||
/** | |||
* 创建pod失败数 | |||
*/ | |||
private Integer createPodFail; | |||
/** | |||
* 评测执行错误 | |||
*/ | |||
private Integer execFail; | |||
/** | |||
* 其它错误 | |||
*/ | |||
private Integer othersFail; | |||
/** | |||
* 评测错误比率: (execFail+othersFail)/evaTotal | |||
*/ | |||
private Double evaFailRatio; | |||
/** | |||
* 评测超时数 | |||
*/ | |||
private Integer evaTimeout; | |||
/** | |||
* 评测超时比例 | |||
*/ | |||
private Double evaTimeoutRatio; | |||
} |
@@ -0,0 +1,56 @@ | |||
package com.imitate.common.k8s.pojo; | |||
import com.imitate.common.util.AbstractDO; | |||
import lombok.Data; | |||
import lombok.EqualsAndHashCode; | |||
import javax.persistence.Table; | |||
/** | |||
* oj评测每日统计 | |||
*/ | |||
@Data | |||
@EqualsAndHashCode(callSuper = false) | |||
@Table(name = "oj_eva_day_stat") | |||
public class OjEvaDayStat extends AbstractDO { | |||
/** | |||
* 统计日期,如20200101 | |||
*/ | |||
private Integer statDate; | |||
/** | |||
* 评测总数 | |||
*/ | |||
private Integer evaTotal; | |||
/** | |||
* 评测错误 | |||
*/ | |||
private Integer evaFail; | |||
/** | |||
* 评测错误率 | |||
*/ | |||
private Double evaFailRatio; | |||
/** | |||
* 评测超时 | |||
*/ | |||
private Integer evaTimeout; | |||
/** | |||
* 评测超时率 | |||
*/ | |||
private Double evaTimeoutRatio; | |||
public OjEvaDayStat(Integer statDate, Integer evaTotal, Integer evaFail, Double evaFailRatio, Integer evaTimeout, Double evaTimeoutRatio) { | |||
this.statDate = statDate; | |||
this.evaTotal = evaTotal; | |||
this.evaFail = evaFail; | |||
this.evaFailRatio = evaFailRatio; | |||
this.evaTimeout = evaTimeout; | |||
this.evaTimeoutRatio = evaTimeoutRatio; | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
package com.imitate.common.k8s.pojo; | |||
import com.imitate.common.util.AbstractDO; | |||
import lombok.Data; | |||
import lombok.EqualsAndHashCode; | |||
import javax.persistence.Table; | |||
import java.time.LocalDateTime; | |||
@Data | |||
@EqualsAndHashCode(callSuper = false) | |||
@Table(name = "platform_config") | |||
public class PlatformConfig extends AbstractDO { | |||
private String platform; | |||
private String containers; | |||
private LocalDateTime createTime; | |||
private LocalDateTime updateTime; | |||
private String script; | |||
private String fileNameSuffix; | |||
} |
@@ -0,0 +1,78 @@ | |||
package com.imitate.common.k8s.pojo; | |||
import com.imitate.common.util.AbstractDO; | |||
import lombok.Data; | |||
import lombok.EqualsAndHashCode; | |||
import javax.persistence.Table; | |||
import java.time.LocalDateTime; | |||
@Data | |||
@Table(name = "run_pod") | |||
public class RunPod extends AbstractDO { | |||
private String name; | |||
private String cluster; | |||
private String imageName; | |||
private Double cpuLimit; | |||
private Integer memoryLimit; | |||
private Double cpuRequest; | |||
private Integer memoryRequest; | |||
private Integer runPodType; | |||
private String nodeIp; | |||
private Long priority; | |||
private String createBy; | |||
private String svcPort; | |||
private String sshPort; | |||
public static final Long RUN_POD_PRIORITY_DEFAULT = 0L; | |||
private LocalDateTime expireTime; | |||
@Override | |||
public int hashCode() { | |||
final int prime = 31; | |||
int result = 1; | |||
result = prime * result + ((super.getId() == null) ? 0 : super.getId().hashCode()); | |||
return result; | |||
} | |||
@Override | |||
public boolean equals(Object obj) { | |||
if (this == obj) { | |||
return true; | |||
} | |||
if (obj == null) { | |||
return false; | |||
} | |||
if (getClass() != obj.getClass()) { | |||
return false; | |||
} | |||
RunPod other = (RunPod) obj; | |||
if (super.getId() == null) { | |||
if (other.getId() != null) | |||
return false; | |||
} else if (!super.getId().equals(other.getId())) | |||
return false; | |||
return true; | |||
} | |||
} |
@@ -0,0 +1,30 @@ | |||
package com.imitate.common.k8s.pojo; | |||
import com.imitate.common.util.AbstractDO; | |||
import lombok.Data; | |||
import lombok.EqualsAndHashCode; | |||
import javax.persistence.Table; | |||
import java.time.LocalDateTime; | |||
/** | |||
* @author zmr | |||
*/ | |||
@Data | |||
@EqualsAndHashCode(callSuper = false) | |||
@Table(name = "security_context_config") | |||
public class SecurityContextConfig extends AbstractDO { | |||
private String imageName; | |||
private String cluster; | |||
private Boolean privileged; | |||
private String addCap; | |||
private String dropCap; | |||
} |
@@ -0,0 +1,44 @@ | |||
package com.imitate.common.k8s.pojo; | |||
import com.imitate.common.util.AbstractDO; | |||
import lombok.Data; | |||
import lombok.EqualsAndHashCode; | |||
import javax.persistence.Table; | |||
import java.time.LocalDateTime; | |||
/** | |||
* windows实例信息 | |||
*/ | |||
@Data | |||
@EqualsAndHashCode(callSuper = false) | |||
@Table(name = "windows_info") | |||
public class WindowsInfo extends AbstractDO { | |||
private String uniqId; | |||
private String instanceId; | |||
private String userID; | |||
private String templateName; | |||
private String port; | |||
private String vncPort; | |||
private String forwardTableId; | |||
private String forwardEntryId; | |||
private String vncForwardEntryId; | |||
private LocalDateTime autoReleaseTime; | |||
private Integer status; | |||
} | |||
@@ -0,0 +1,71 @@ | |||
package com.imitate.common.k8s.service; | |||
import com.imitate.common.k8s.bean.BridgeNode; | |||
import com.imitate.common.k8s.bean.NodeQueryParam; | |||
import io.fabric8.kubernetes.api.model.Node; | |||
import io.fabric8.kubernetes.api.model.NodeAddress; | |||
import io.fabric8.kubernetes.api.model.Pod; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import java.time.LocalDateTime; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
@Service | |||
public class BridgeNodeService { | |||
@Autowired | |||
private K8sService k8sService; | |||
public List<BridgeNode> getBridgePods(NodeQueryParam param) { | |||
Map<String, List<Pod>> nodePodMap = this.getNodePods(param.getCluster()); | |||
List<Node> nodeList = k8sService.getNodes(param); | |||
List<BridgeNode> list = new ArrayList<>(nodeList.size()); | |||
for (Node node : nodeList) { | |||
BridgeNode bn = new BridgeNode(); | |||
bn.setName(node.getMetadata().getName()); | |||
// ip | |||
List<NodeAddress> addrs = node.getStatus().getAddresses(); | |||
for (NodeAddress na : addrs) { | |||
if ("InternalIP".equals(na.getType())) { | |||
bn.setIp(na.getAddress()); | |||
break; | |||
} | |||
} | |||
// 创建时间 | |||
String createTime = node.getMetadata().getCreationTimestamp(); | |||
if (createTime.endsWith("Z")) { | |||
createTime = createTime.substring(0, createTime.length() - 1); | |||
} | |||
bn.setCreateTime(LocalDateTime.parse(createTime)); | |||
// pod数量 | |||
List<Pod> podList = nodePodMap.get(bn.getIp()); | |||
bn.setPodNum(podList == null ? 0 : podList.size()); | |||
list.add(bn); | |||
} | |||
return list; | |||
} | |||
private Map<String, List<Pod>> getNodePods(final String cluster) { | |||
Map<String, List<Pod>> map = new HashMap<>(); | |||
List<Pod> list = k8sService.getPods(cluster); | |||
for (Pod pod : list) { | |||
String hostIp = pod.getStatus().getHostIP(); | |||
List<Pod> podList = map.get(hostIp); | |||
if (podList == null) { | |||
podList = new ArrayList<>(); | |||
map.put(hostIp, podList); | |||
} | |||
podList.add(pod); | |||
} | |||
return map; | |||
} | |||
} |
@@ -0,0 +1,119 @@ | |||
package com.imitate.common.k8s.service; | |||
import com.alibaba.fastjson.JSONObject; | |||
import com.github.pagehelper.PageHelper; | |||
import com.imitate.common.k8s.bean.*; | |||
import com.imitate.common.k8s.constant.BridgePodCsts; | |||
import com.imitate.common.k8s.mapper.BridgePodMapper; | |||
import com.imitate.common.k8s.pojo.EvaDayStat; | |||
import com.imitate.common.k8s.pojo.*; | |||
import com.imitate.common.util.TpUtils; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import java.time.LocalDateTime; | |||
import java.time.format.DateTimeFormatter; | |||
import java.util.List; | |||
@Service | |||
public class BridgePodService { | |||
@Autowired | |||
private BridgePodMapper bridgePodMapper; | |||
public List<BridgePod> getBridgePods(PodQueryParam param) { | |||
PageHelper.startPage(param.getPageNum(), param.getPageSize()); | |||
return bridgePodMapper.selectBridgePod(param); | |||
} | |||
public BridgePod createBridgePod(String name, String tpiID, String buildID, LocalDateTime requestTime, String secKey) { | |||
BridgePod bridgePod = new BridgePod(); | |||
bridgePod.setName(name); | |||
bridgePod.setTpiID(tpiID); | |||
bridgePod.setBuildID(buildID); | |||
bridgePod.setSecKey(secKey); | |||
bridgePod.setRequestTime(requestTime); | |||
bridgePod.setStatus(BridgePodCsts.STATUS_BEGIN); | |||
bridgePodMapper.insert(bridgePod); | |||
return bridgePod; | |||
} | |||
public void updateBridgeInfo(JSONObject buildParams, TimeCost timeCost, BuildResult result) { | |||
BridgePod bridgePod = new BridgePod(); | |||
bridgePod.setId(buildParams.getLong("bridgePodId")); | |||
String podName = buildParams.getString("podName"); | |||
bridgePod.setName(podName); | |||
bridgePod.setNodeName(buildParams.getString("nodeName")); | |||
bridgePod.setNodeIp(buildParams.getString("nodeIp")); | |||
// image info | |||
JSONObject mainContainer = buildParams.getJSONObject("mainContainer"); | |||
bridgePod.setImageName(mainContainer.getString("imageName")); | |||
bridgePod.setImageVersion(mainContainer.getString("imageVersion")); | |||
bridgePod.setCpuLimit(Double.parseDouble(mainContainer.getString("cpuLimit"))); | |||
Integer memoryLimit = Integer.parseInt(mainContainer.getString("memoryLimit").replace("M", "")); | |||
bridgePod.setMemoryLimit(memoryLimit); | |||
bridgePod.setCpuRequest(Double.parseDouble(mainContainer.getString("cpuRequest"))); | |||
Integer memoryRequest = Integer.parseInt(mainContainer.getString("memoryRequest").replace("M", "")); | |||
bridgePod.setMemoryRequest(memoryRequest); | |||
// 下载代码和创建pod | |||
String pullTimeStr = timeCost.getPull(); | |||
Double pullTime = StringUtils.isNotEmpty(pullTimeStr) ? Double.parseDouble(pullTimeStr) : null; | |||
bridgePod.setPull(pullTime); | |||
String createPodStr = timeCost.getCreatePod(); | |||
Double createPod = StringUtils.isNotEmpty(createPodStr) ? Double.parseDouble(createPodStr) : null; | |||
bridgePod.setCreatePod(createPod); | |||
String createTime = buildParams.getString("k8sCreateTime"); | |||
if (StringUtils.isNotEmpty(createTime)) { | |||
if (createTime.endsWith("Z")) { | |||
createTime = createTime.substring(0, createTime.length() - 1); | |||
} | |||
LocalDateTime time = LocalDateTime.parse(createTime); | |||
// 转化为东八区时间 | |||
if (time.isBefore(LocalDateTime.now().plusHours(1))) { | |||
time.plusHours(8); | |||
} | |||
bridgePod.setK8sCreateTime(time); | |||
String uid = time.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + "-" + TpUtils.getTpiID(podName); | |||
bridgePod.setUid(uid); | |||
} | |||
// 评测时间数据 | |||
String execute = timeCost.getExecute(); | |||
Double takeTime = execute == null ? null : Double.parseDouble(execute); | |||
bridgePod.setExecute(takeTime); | |||
bridgePod.setEvaluateAllTime(Double.parseDouble(timeCost.getEvaluateAllTime())); | |||
bridgePod.setDownloadStatus(result.getDownloadStatus()); | |||
bridgePod.setCreatePodStatus(result.getCreatePodStatus()); | |||
bridgePod.setCompileStatus(result.getCompileSuccess()); | |||
String evaFailStatus = buildParams.getString("evaFailStatus"); | |||
if (StringUtils.isNotEmpty(evaFailStatus)) { | |||
bridgePod.setRunStatus(evaFailStatus); | |||
} else { | |||
bridgePod.setRunStatus(result.getStatus()); | |||
} | |||
bridgePod.setStatus(BridgePodCsts.STATUS_END); | |||
bridgePodMapper.updateByPrimaryKeySelective(bridgePod); | |||
} | |||
public void deleteHisBridgePod(LocalDateTime deleteTime) { | |||
bridgePodMapper.deleteByRequestTime(deleteTime); | |||
} | |||
public EvaDayStat doEvaDayStat(Integer date) { | |||
return bridgePodMapper.countEvaDayStat(date); | |||
} | |||
public List<NodeEvaErrorInfo> getNodeEvaErrorInfos(LocalDateTime requestTime) { | |||
return bridgePodMapper.selectNodeEvaErrorInfos(requestTime); | |||
} | |||
} |
@@ -0,0 +1,157 @@ | |||
package com.imitate.common.k8s.service; | |||
import com.imitate.common.constant.TpCsts; | |||
import com.imitate.common.sys.settings.AppConfig; | |||
import org.apache.commons.lang3.math.NumberUtils; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import org.springframework.util.StringUtils; | |||
import java.io.File; | |||
/** | |||
* 磁盘相关service | |||
* | |||
* @author 威少 | |||
*/ | |||
@Service | |||
public class DiskService { | |||
@Autowired | |||
private AppConfig appConfig; | |||
/** | |||
* 文件类型实际输出所在文件夹名 | |||
*/ | |||
public static final String TEST_CASE_ACTUAL_OUT_FILE_FOLDER = "filecase"; | |||
/** | |||
* 平台通用文件所在文件夹名 | |||
*/ | |||
public static final String PLATFORM_FILE_FOLDER = "platform"; | |||
/** | |||
* 保护文件所在文件夹名 | |||
*/ | |||
public static final String PROTECT_FILE_FOLDER = "protect"; | |||
/** | |||
* 私密版本库所在文件夹名 | |||
*/ | |||
public static final String SECRET_FILE_FOLDER = "secret"; | |||
/** | |||
* TPI工作目录文件夹名前缀 | |||
*/ | |||
public static final String TPI_WORKSPACE_FOLDER_PREFIX = "myshixun_"; | |||
/** | |||
* 获取工作目录在bridge节点挂载目录的名称 | |||
*/ | |||
public String getWorkspaceHostPath(String tpiID) { | |||
// 分盘逻辑,对磁盘个数取余,使落到对应的盘 | |||
String[] workspaceNFSConfigs = getWorkspaceNFSConfigs(); | |||
int workspacesSize = workspaceNFSConfigs.length; | |||
return workspaceNFSConfigs[(int)(NumberUtils.toLong(tpiID, 0) % workspacesSize)]; | |||
} | |||
/** | |||
* 对pod名字取hashcode取余决定盘 | |||
*/ | |||
public String getWorkspaceHostPathByPodName(String podName) { | |||
String[] workspaceNFSConfigs = getWorkspaceNFSConfigs(); | |||
int index = (int)(podName.charAt(podName.length() - 1)) % workspaceNFSConfigs.length; | |||
return workspaceNFSConfigs[index]; | |||
} | |||
/** | |||
* 获取TPI在bridge节点的工作目录 | |||
*/ | |||
public String getTpiWorkspaceHostPath(String tpiID) { | |||
String workspace = getWorkspaceHostPath(tpiID); | |||
return workspace + File.separator + TPI_WORKSPACE_FOLDER_PREFIX + tpiID; | |||
} | |||
/** | |||
* 获取TPI 测试用例在bridge节点的host path | |||
*/ | |||
public String getTpiTestCaseActualOutHostPath(String tpiID) { | |||
String testCaseActualOutHostPath = getWorkspaceHostPath(tpiID) + File.separator + TEST_CASE_ACTUAL_OUT_FILE_FOLDER; | |||
return StringUtils.isEmpty(tpiID) ? testCaseActualOutHostPath : testCaseActualOutHostPath + File.separator + tpiID; | |||
} | |||
/** | |||
* 获取私密版本库工作目录 | |||
*/ | |||
public String getTpiSecretWorkspace(String tpiID) { | |||
return getWorkspaceHostPath(tpiID) + File.separator + SECRET_FILE_FOLDER + File.separator + TPI_WORKSPACE_FOLDER_PREFIX + tpiID + File.separator + TpCsts.TP_UNIFY_REPO_NAME; | |||
} | |||
// ------------------------ 所有build的方法用给定的目录作为基础目录 ------------------------ | |||
/** | |||
* 构建数据集存放路径 | |||
*/ | |||
public String buildTpDataHostPath(String basePath, String identifier) { | |||
return basePath + File.separator + identifier; | |||
} | |||
/** | |||
* 构建平台固定脚本等存放的路径 | |||
*/ | |||
public String buildTpiPlatformHostPath(String workspaceHostPath) { | |||
return workspaceHostPath + File.separator + PLATFORM_FILE_FOLDER + | |||
File.separator + "eva"; | |||
} | |||
/** | |||
* 构建工作目录 | |||
*/ | |||
public String buildTpiWorkspace(String workspace, String tpiID) { | |||
return workspace + File.separator + TPI_WORKSPACE_FOLDER_PREFIX + tpiID; | |||
} | |||
/** | |||
* 构建保护工作空间, 存放评测执行脚本等 | |||
*/ | |||
public String buildTpiProtectSpace(String workspace, String tpiID) { | |||
return workspace + File.separator + PROTECT_FILE_FOLDER + File.separator + TPI_WORKSPACE_FOLDER_PREFIX + tpiID; | |||
} | |||
/** | |||
* 构建TPI版本库路径 | |||
*/ | |||
public String buildTpiRepoPath(String tpiWorkspace, String tpiRepoName) { | |||
return tpiWorkspace + File.separator + tpiRepoName; | |||
} | |||
/** | |||
* 构建私密版本库路径 | |||
*/ | |||
public String buildSecretRepoSpace(String tpiRepoPath, String secretDir) { | |||
return tpiRepoPath + File.separator + secretDir; | |||
} | |||
/** | |||
* 构建TPM测试用例目录 | |||
*/ | |||
public String buildTpmTestCaseHostPath(String testCaseHostPath, String tpmIdentifier) { | |||
if (StringUtils.isEmpty(tpmIdentifier)) { | |||
return testCaseHostPath; | |||
} | |||
return testCaseHostPath + File.separator + tpmIdentifier; | |||
} | |||
/** | |||
* 构建TPI测试用例的容器内挂载路径 | |||
*/ | |||
public String buildTpiTestCaseActualOutMountPath(String workspace) { | |||
return workspace + File.separator + TEST_CASE_ACTUAL_OUT_FILE_FOLDER; | |||
} | |||
/** | |||
* 取所有工作目录盘 | |||
*/ | |||
public String[] getWorkspaceNFSConfigs() { | |||
String workspaceNFSConfig = appConfig.getWorkspaceNFSConfigs(); | |||
return workspaceNFSConfig.split(","); | |||
} | |||
} |
@@ -0,0 +1,19 @@ | |||
package com.imitate.common.k8s.service; | |||
import com.imitate.common.k8s.mapper.EvaDayStatMapper; | |||
import com.imitate.common.k8s.pojo.EvaDayStat; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
@Service | |||
public class EvaDayStatService { | |||
@Autowired | |||
private EvaDayStatMapper evaDayStatMapper; | |||
public void insert(EvaDayStat evaDayStat) { | |||
evaDayStatMapper.insertSelective(evaDayStat); | |||
} | |||
} |
@@ -0,0 +1,120 @@ | |||
package com.imitate.common.k8s.service; | |||
import com.imitate.common.constant.TpCsts; | |||
import com.imitate.common.k8s.mgr.ClusterManager; | |||
import com.imitate.common.k8s.bean.ClusterInfo; | |||
import com.imitate.common.bean.ShellResult; | |||
import com.imitate.common.util.ShellUtil; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Component; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
/** | |||
* k8s节点资源使用信息服务 | |||
*/ | |||
@Component | |||
public class NodeResUsageService { | |||
private final Logger logger = LoggerFactory.getLogger(getClass()); | |||
// Map<cluster, Map<nodeName, cpuPercent>> | |||
private volatile Map<String, Map<String, Integer>> nodeCpuUsageRateMap = new HashMap<>(); | |||
@Autowired | |||
private ClusterManager clusterManager; | |||
public void refreshNodeResUsage() { | |||
Map<String, Map<String, Integer>> tmpMap = new HashMap<>(); | |||
Map<String, ClusterInfo> clusterMap = clusterManager.getClusterInfo(); | |||
for (ClusterInfo cInfo : clusterMap.values()) { | |||
String cluster = cInfo.getClusterConfig().getName(); | |||
// 加timeout避免某个集群网络缓慢等问题。可考虑集群各自管理 | |||
String cmd = "timeout 2 kubectl --kubeconfig=" + cInfo.getClusterConfig().getKubeconfig() | |||
+ " top node | awk '{print $1,$2,$3,$4,$5}'"; | |||
ShellResult result = ShellUtil.executeAndGetExitStatus(cmd, 1); | |||
if (result.getExitStatus() == 0) { | |||
String resUsageOut = result.getOut(); | |||
Map<String, Integer> resUsageMap = getNewNodeResUsageMap(cluster, resUsageOut); | |||
tmpMap.put(cluster, resUsageMap); | |||
} | |||
} | |||
nodeCpuUsageRateMap = tmpMap; | |||
} | |||
public Integer getNodeCpuUsageRate(String cluster, String nodeName) { | |||
Map<String, Integer> resUsageMap = nodeCpuUsageRateMap.get(cluster); | |||
if (resUsageMap == null) { | |||
logger.info("集群资源使用率尚未统计, cluster:{}, nodeName: {}", cluster, nodeName); | |||
return TpCsts.NODE_CPU_USAGE_UNKNOWN; | |||
} else { | |||
Integer nodeCpuUsageRate = resUsageMap.get(nodeName); | |||
if (nodeCpuUsageRate == null) { | |||
logger.info("未获取到节点cpu使用率 , cluster:{}, nodeName: {}", cluster, nodeName); | |||
return TpCsts.NODE_CPU_USAGE_UNKNOWN; | |||
} | |||
return nodeCpuUsageRate; | |||
} | |||
} | |||
private Map<String, Integer> getNewNodeResUsageMap(String cluster, String resUsageOut) { | |||
Map<String, Integer> newNodeResUsageMap = new HashMap<>(); | |||
String[] nodeResUsages = resUsageOut.split(System.getProperty("line.separator")); | |||
for (int i = 1; i < nodeResUsages.length; i++) { | |||
String nodeResUsage = nodeResUsages[i]; | |||
String[] infos = nodeResUsage.split(" "); | |||
String nodeName = infos[0]; | |||
String nodeCpuUsageStr = infos[2]; | |||
String nodeMemoryUsageStr = infos[4]; | |||
int nodeCpuUsage=0; | |||
int nodeMemoryUsage; | |||
// kubectl top 会偶尔出现获取资源数据为 <unknown> 的情况 | |||
if (!"<unknown>".equals(nodeCpuUsageStr)) { | |||
try{ | |||
nodeCpuUsage = Integer.parseInt(nodeCpuUsageStr.replace("%", "")); | |||
//若k8s节点cpu使用率超过90%,钉钉报警 | |||
if( nodeCpuUsage>90 ) | |||
{ | |||
String message = "k8s节点:"+nodeName+",cpu使用率为:"+nodeCpuUsageStr+",服务器cpu繁忙"; | |||
} | |||
newNodeResUsageMap.put(nodeName, nodeCpuUsage); | |||
} | |||
catch ( Exception e ) | |||
{ | |||
logger.error( "执行kubectl top node命令返回的结果中cpu数据不合法,nodeCpuUsage:{}",nodeCpuUsageStr,e); | |||
} | |||
if( !"<unknown>".equals(nodeMemoryUsageStr) ) | |||
{ | |||
try{ | |||
nodeMemoryUsage = Integer.parseInt(nodeMemoryUsageStr.replace("%", "")); | |||
//若k8s节点内存使用率超过90%,钉钉报警 | |||
if( nodeMemoryUsage>90 ) | |||
{ | |||
String message = "k8s节点:"+nodeName+",内存使用率为:"+nodeMemoryUsageStr+",服务器内存即将耗尽"; | |||
} | |||
} | |||
catch ( Exception e ) | |||
{ | |||
logger.error( "执行kubectl top node命令返回的结果中memory数据不合法,nodeMemoryUsage:{}",nodeMemoryUsageStr,e); | |||
} | |||
} | |||
} else { | |||
// 使用该节点上次获取到的cpu使用信息 | |||
Map<String, Integer> resUsageMap = nodeCpuUsageRateMap.get(cluster); | |||
if (resUsageMap != null && resUsageMap.get(nodeName) != null) { | |||
nodeCpuUsage = resUsageMap.get(nodeName); | |||
newNodeResUsageMap.put(nodeName, nodeCpuUsage); | |||
} | |||
} | |||
} | |||
return newNodeResUsageMap; | |||
} | |||
} |
@@ -0,0 +1,54 @@ | |||
package com.imitate.common.k8s.service; | |||
import com.imitate.common.k8s.mapper.OjEvaDayStatMapper; | |||
import com.imitate.common.k8s.bean.BuildResult; | |||
import com.imitate.common.k8s.pojo.OjEvaDayStat; | |||
import com.imitate.common.util.JedisUtil; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import java.time.LocalDateTime; | |||
import java.time.format.DateTimeFormatter; | |||
@Service | |||
public class OjEvaDayStatService { | |||
@Autowired | |||
private OjEvaDayStatMapper ojEvaDayStatMapper; | |||
private static final String OJ_EVA_TOTAL_REDIS_KEY = "oj_total_"; | |||
private static final String OJ_EVA_FAIL_REDIS_KEY = "oj_fail_"; | |||
private static final String OJ_EVA_TIMEOUT_REDIS_KEY = "oj_timeout_"; | |||
public void ojEvaDayStat(Integer statDate) { | |||
String totalStr = JedisUtil.get(OJ_EVA_TOTAL_REDIS_KEY + statDate); | |||
Integer total = StringUtils.isEmpty(totalStr) ? 0 : Integer.parseInt(totalStr); | |||
String failStr = JedisUtil.get(OJ_EVA_FAIL_REDIS_KEY + statDate); | |||
Integer fail = StringUtils.isEmpty(failStr) ? 0 : Integer.parseInt(failStr); | |||
String timeoutStr = JedisUtil.get(OJ_EVA_TIMEOUT_REDIS_KEY + statDate); | |||
Integer timeout = StringUtils.isEmpty(timeoutStr) ? 0 : Integer.parseInt(timeoutStr); | |||
double failRatio = total == 0 ? 0 : fail / (double) total; | |||
double timeoutRatio = total == 0 ? 0 : timeout / (double) total; | |||
OjEvaDayStat ojEvaDayStat = new OjEvaDayStat(statDate, total, fail, failRatio, timeout, timeoutRatio); | |||
ojEvaDayStatMapper.insertSelective(ojEvaDayStat); | |||
} | |||
public void ojEvaStat(String status, boolean isCodeExecuteFail) { | |||
Integer statDate = Integer.parseInt(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))); | |||
JedisUtil.incrBy(OJ_EVA_TOTAL_REDIS_KEY + statDate, 1); | |||
if (BuildResult.Status.RUN_TIMEOUT.getValue().equals(status)) { | |||
JedisUtil.incrBy(OJ_EVA_TIMEOUT_REDIS_KEY + statDate, 1); | |||
return; | |||
} | |||
if (BuildResult.Status.EXECUTE_ERROR.getValue().equals(status) && !isCodeExecuteFail) { | |||
JedisUtil.incrBy(OJ_EVA_FAIL_REDIS_KEY + statDate, 1); | |||
} | |||
} | |||
} |
@@ -0,0 +1,59 @@ | |||
package com.imitate.common.k8s.service; | |||
import com.imitate.common.k8s.mapper.PlatformConfigMapper; | |||
import com.imitate.common.k8s.pojo.PlatformConfig; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
/** | |||
* 平台配置表 | |||
*/ | |||
@Service | |||
public class PlatformConfigService { | |||
private volatile static Map<String, PlatformConfig> platformConfigMap = new HashMap<>(); | |||
@Autowired | |||
private PlatformConfigMapper platformConfigMapper; | |||
public void refreshPlatformConfig() { | |||
Map<String, PlatformConfig> tmpMap = new HashMap<>(); | |||
List<PlatformConfig> pcs = platformConfigMapper.selectAllConfig(); | |||
for (PlatformConfig pc : pcs) { | |||
String platform = pc.getPlatform(); | |||
tmpMap.put(platform, pc); | |||
} | |||
platformConfigMap = tmpMap; | |||
} | |||
public PlatformConfig getPlatformConfig(String platform) { | |||
PlatformConfig pc = platformConfigMap.get(platform); | |||
if (pc == null) { | |||
throw new RuntimeException("不支持的平台:" + platform); | |||
} | |||
return pc; | |||
} | |||
public Set<String> getAllPlatforms() { | |||
return platformConfigMap.keySet(); | |||
} | |||
public void savePlatformConfig(PlatformConfig pc) { | |||
platformConfigMapper.insertSelective(pc); | |||
} | |||
public void updatePlatformConfig(PlatformConfig pc) { | |||
platformConfigMapper.updateByPrimaryKeySelective(pc); | |||
} | |||
} |
@@ -0,0 +1,110 @@ | |||
package com.imitate.common.k8s.service; | |||
import com.imitate.common.k8s.util.K8sUtils; | |||
import com.imitate.common.sys.constant.SysConfigCsts; | |||
import com.imitate.common.sys.pojo.SysConfig; | |||
import com.imitate.common.sys.service.SysConfigService; | |||
import com.imitate.common.sys.settings.AppConfig; | |||
import com.imitate.common.util.JedisUtil; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import org.springframework.transaction.annotation.Transactional; | |||
@Service | |||
public class PortService { | |||
private Logger logger = LoggerFactory.getLogger(getClass()); | |||
@Autowired | |||
private SysConfigService sysConfigService; | |||
@Autowired | |||
private K8sService k8sService; | |||
@Autowired | |||
private AppConfig appConfig; | |||
@Transactional | |||
public int allocatePort(String usablePortTypeKey,int k8sMinUsablePort,int k8sMaxUsablePort){ | |||
long port = JedisUtil.incrBy(usablePortTypeKey, 1); | |||
if (port < k8sMinUsablePort) { | |||
return this.initPort(port, usablePortTypeKey,k8sMinUsablePort); | |||
} | |||
if (port >= k8sMaxUsablePort) { | |||
logger.info("分配的port {} 达到上限 {},等待端口重置后重新分配", port, k8sMaxUsablePort); | |||
SysConfig configParam = new SysConfig(); | |||
configParam.setName(SysConfigCsts.CONFIG_PORT_RESET); | |||
configParam.setVal(appConfig.getBridgeInstanceName());// 暂时写死 | |||
SysConfig sysConfig = null; | |||
try { | |||
sysConfig = sysConfigService.lockSysConfig(configParam); | |||
if (sysConfig != null) {// 成功获得锁 | |||
port = JedisUtil.incrBy(usablePortTypeKey, 1); | |||
if (port >= k8sMaxUsablePort) { | |||
logger.info("分配的port {} 达到上限 {},已经锁定端口重置锁,将重置端口到最小可用值", port, k8sMaxUsablePort); | |||
JedisUtil.set(usablePortTypeKey, k8sMinUsablePort + ""); | |||
port = JedisUtil.incrBy(usablePortTypeKey, 1); | |||
} | |||
} else {// 没有获得锁,那么是被其它任务锁定,并且重置了端口 | |||
port = JedisUtil.incrBy(usablePortTypeKey, 1); | |||
} | |||
} catch (Exception e) { | |||
logger.error("重置端口错误", e); | |||
} finally { | |||
if (sysConfig != null) { | |||
sysConfigService.releasSysConfigLock(configParam); | |||
} | |||
} | |||
} | |||
return (int) port; | |||
} | |||
@Transactional | |||
public int allocatePort(String usablePortTypeKey) { | |||
int k8sMinUsablePort = sysConfigService.getK8sMinUsablePort(); | |||
int k8sMaxUsablePort = sysConfigService.getK8sMaxUsablePort(); | |||
return allocatePort(usablePortTypeKey,k8sMinUsablePort,k8sMaxUsablePort); | |||
} | |||
private int initPort(long port, String usablePortTypeKey,int k8sMinUsablePort) { | |||
SysConfig configParam = new SysConfig(); | |||
configParam.setName(SysConfigCsts.CONFIG_PORT_RESET); | |||
configParam.setVal(appConfig.getBridgeInstanceName());// 暂时写死 | |||
SysConfig sysConfig = null; | |||
try { | |||
sysConfig = sysConfigService.lockSysConfig(configParam); | |||
if (sysConfig != null) {// 成功获得锁 | |||
port = JedisUtil.incrBy(usablePortTypeKey, 1); | |||
if (port < k8sMinUsablePort) { | |||
logger.info("分配的port {} 小于最小值 {},已经锁定端口重置锁,将重置端口到最小可用值", port, k8sMinUsablePort); | |||
JedisUtil.set(usablePortTypeKey, k8sMinUsablePort + ""); | |||
port = JedisUtil.incrBy(usablePortTypeKey, 1); | |||
} | |||
} else {// 没有获得锁,那么是被其它任务锁定,并且重置了端口 | |||
port = JedisUtil.incrBy(usablePortTypeKey, 1); | |||
} | |||
} catch (Exception e) { | |||
logger.error("重置端口错误", e); | |||
} finally { | |||
if (sysConfig != null) { | |||
sysConfigService.releasSysConfigLock(configParam); | |||
} | |||
} | |||
return (int) port; | |||
} | |||
public Integer getServicePort(final String cluster, String podName) { | |||
io.fabric8.kubernetes.api.model.Service service = k8sService.getService(cluster, podName); | |||
if (service != null) { | |||
return K8sUtils.getNodePort(service); | |||
} | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,511 @@ | |||
package com.imitate.common.k8s.service; | |||
import com.alibaba.fastjson.JSONObject; | |||
import com.imitate.common.constant.TpCsts; | |||
import com.imitate.common.k8s.bean.CreatePodResult; | |||
import com.imitate.common.k8s.bean.NodeResStat; | |||
import com.imitate.common.k8s.bean.PodCreateStrategy; | |||
import com.imitate.common.k8s.bean.RunPodQueryParam; | |||
import com.imitate.common.k8s.mapper.RunPodMapper; | |||
import com.imitate.common.k8s.mgr.ClusterManager; | |||
import com.imitate.common.k8s.mgr.NodeManager; | |||
import com.imitate.common.k8s.pojo.*; | |||
import com.imitate.common.k8s.util.ContainerUtil; | |||
import com.imitate.common.k8s.util.K8sPodCommandExecutor; | |||
import com.imitate.common.k8s.util.K8sUtils; | |||
import com.imitate.common.sys.service.SysConfigService; | |||
import com.imitate.common.sys.settings.AppConfig; | |||
import com.imitate.common.util.JedisUtil; | |||
import com.imitate.common.util.ThreadUtils; | |||
import com.imitate.common.util.TpUtils; | |||
import io.fabric8.kubernetes.api.model.Pod; | |||
import org.apache.commons.lang3.RandomStringUtils; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import org.springframework.transaction.annotation.Transactional; | |||
import java.time.LocalDateTime; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import java.util.Map; | |||
import static com.imitate.common.sys.constant.SysConfigCsts.*; | |||
@Service | |||
public class RunPodService { | |||
private Logger logger = LoggerFactory.getLogger(getClass()); | |||
public static final String DELETE_POD_REDIS_KEY = "delete_pod_key_"; | |||
public static final String DELETE_POD_REDIS_LOCK_KEY = "delete_pod_lock_key_"; | |||
@Autowired | |||
private RunPodMapper runPodMapper; | |||
@Autowired | |||
private K8sService k8sService; | |||
@Autowired | |||
private AppConfig appConfig; | |||
@Autowired | |||
private SysConfigService sysConfigService; | |||
@Autowired | |||
private NodeManager nodeManager; | |||
@Autowired | |||
private ClusterManager clusterManager; | |||
public List<RunPod> getRunPods(RunPodQueryParam param) { | |||
return runPodMapper.selectRunPods(param); | |||
} | |||
/** | |||
* 仅仅删除 RunPod记录 | |||
* | |||
* @param podName | |||
* @return | |||
*/ | |||
public void deleteRunPodRecordOnly(String podName) { | |||
runPodMapper.deleteByName(podName); | |||
} | |||
/** | |||
* 如果更新成功返回RunPod,否则返回null | |||
* | |||
* @param podName | |||
* @param delayTime | |||
* @return | |||
*/ | |||
public void deleteRunPodAfterTime(String podName, long delayTime) { | |||
RunPod runPod = new RunPod(); | |||
runPod.setName(podName); | |||
LocalDateTime now = LocalDateTime.now(); | |||
runPod.setUpdateTime(now); | |||
LocalDateTime expireTime = now.plusSeconds(delayTime); | |||
runPod.setExpireTime(expireTime); | |||
runPodMapper.updateByNameSelective(runPod); | |||
} | |||
/** | |||
* 延长runPod过期时间,当新过期时间大于旧过期时间才更新 | |||
* | |||
* @param podName | |||
* @param delayTime | |||
* @return | |||
*/ | |||
public boolean delayRunPodExpireTime(String podName, long delayTime) { | |||
RunPod runPod = new RunPod(); | |||
runPod.setName(podName); | |||
LocalDateTime now = LocalDateTime.now(); | |||
runPod.setUpdateTime(now); | |||
LocalDateTime expireTime = now.plusSeconds(delayTime); | |||
runPod.setExpireTime(expireTime); | |||
int rowNum = runPodMapper.delayRunPodExpireTime(runPod); | |||
return rowNum > 0; | |||
} | |||
public boolean deteleRunPod(RunPod runPod) { | |||
// 常驻镜像或pod不予删除 | |||
if (sysConfigService.getLongLiveImage().contains(runPod.getImageName()) || sysConfigService.getLongLivePod().contains(runPod.getName())) { | |||
return false; | |||
} | |||
//加redis锁 | |||
JedisUtil.setnx(DELETE_POD_REDIS_LOCK_KEY + runPod.getName(),runPod.getName()); | |||
int r = runPodMapper.deleteByNameExpired(runPod.getName(), LocalDateTime.now()); | |||
if(r > 0) { | |||
try { | |||
delK8sPod(runPod); | |||
logger.info("pod 过期被删除,podName:{}", runPod.getName()); | |||
} catch (Exception e) { | |||
logger.error("k8s 删除pod异常,将继续删除run_pod, podName: {}, cluster: {}", runPod.getName(), runPod.getCluster(), e); | |||
} | |||
} | |||
//删除redis锁,释放锁 | |||
JedisUtil.del(DELETE_POD_REDIS_LOCK_KEY + runPod.getName()); | |||
return r > 0; | |||
} | |||
private void delK8sPod(RunPod runPod) { | |||
String podName = runPod.getName(); | |||
String tpiID = TpUtils.getTpiID(podName); | |||
String cluster = runPod.getCluster(); | |||
JedisUtil.psetex(DELETE_POD_REDIS_KEY + podName, podName, sysConfigService.getDeletePodRedisExpireTime()); | |||
try { | |||
// 根据podName来删除pod | |||
Boolean r = k8sService.deletePod(cluster, podName); | |||
logger.info("删除k8s pod {} 结果: {}", podName, r); | |||
r = k8sService.deleteServiceByTpiId(cluster, tpiID); | |||
logger.info("删除k8s service {} 结果: {}", tpiID, r); | |||
} catch(Exception ignore) { | |||
} | |||
} | |||
public void deteleRunPodSync(final String cluster, String tpiId, String podName, Integer podType) { | |||
// 必须是先删除记录,再删除pod;否则可能删除掉其它线程刚刚创建的记录,导致pod永远不会删除掉。 | |||
// TODO 可以优化,先查询pod,后面按照时间删除,如果时间已经更新,则不再删除,否则删除。 | |||
deleteRunPodRecordOnly(podName); | |||
// 查看pod是否已经存在 | |||
Pod pod = k8sService.getPod(cluster, podName); | |||
if (pod != null) { | |||
String phase = pod.getStatus().getPhase(); | |||
// 特殊状态的pod只能立即删除,否则无法删除掉。此时node可能已经宕机 | |||
if ("Terminating".equals(phase) || "Unknown".equals(phase)) { | |||
k8sService.deletePod(cluster, podName); | |||
// 检查pod是否已成功删除,10秒内应该足够删除 | |||
int times = 10; | |||
while (times-- > 0) { | |||
if (k8sService.getPod(cluster, podName) == null) { | |||
break; | |||
} | |||
ThreadUtils.sleep(1000); | |||
} | |||
} else { | |||
// 同步删除pod | |||
k8sService.deletePodSync(cluster, podName); | |||
} | |||
} | |||
k8sService.deleteServiceByTpiId(cluster, tpiId); | |||
logger.info("同步删除run_pod {} 完成", podName); | |||
} | |||
public CreatePodResult createRunPod(final String cluster, JSONObject buildParams, String tpiID, String type, | |||
int createPodTimeLimit, int timeLimit, Boolean localNode, String bigDataFile) { | |||
CreatePodResult createPodResult = new CreatePodResult(); | |||
String podName = type + "-" + tpiID; | |||
try { | |||
Pod pod = null; | |||
Integer podType = buildParams.getInteger("podType"); | |||
if(delayRunPodExpireTime(podName, timeLimit + 60)) { | |||
pod = k8sService.getRunningPod(cluster, podName, createPodTimeLimit); | |||
} else { | |||
RunPod runPod = this.getRunPodByName(podName); | |||
if (runPod == null) { | |||
JSONObject mainContainer = buildParams.getJSONObject("mainContainer"); | |||
insertRunPod(cluster, podName, mainContainer, podType, RunPod.RUN_POD_PRIORITY_DEFAULT, timeLimit + 60); | |||
} else { | |||
pod = k8sService.getRunningPod(cluster, podName, createPodTimeLimit); | |||
} | |||
} | |||
if (pod == null) { | |||
boolean canToShenlong = Boolean.FALSE; | |||
boolean canToGpu = Boolean.FALSE; | |||
boolean canToOj = Boolean.FALSE; | |||
if (nodeManager.isCommonPod(podName, buildParams)) { | |||
canToShenlong = nodeManager.canScheduleNormalPodToShenlong(cluster, timeLimit); | |||
canToGpu = nodeManager.canScheduleNormalPodToGpu(cluster, timeLimit); | |||
canToOj = nodeManager.canScheduleNormalPodToOj(cluster, timeLimit); | |||
} | |||
List<String> noList = nodeManager.getNoSchedulableNodes(cluster); | |||
PodCreateStrategy podCreateStrategy = new PodCreateStrategy().setTimeLimit(createPodTimeLimit) | |||
.setNormalPodCanShenlong(canToShenlong).setNormalPodCanGpu(canToGpu).setNormalPodCanOj(canToOj).setNoSchedulableNodes(noList) | |||
.setPodNoSchedulableWeight(sysConfigService.getPodNoSchedulableWeight()) | |||
.setAutoscaleNodeWeight(clusterManager.getScaleNodeWeight(cluster)); | |||
//读redis锁 | |||
boolean deleteRedisLock = StringUtils.isNotEmpty(JedisUtil.get(RunPodService.DELETE_POD_REDIS_LOCK_KEY + podName)); | |||
//递归读锁,阻塞当前线程线程,等待其他线程释放锁 | |||
while (deleteRedisLock) { | |||
deleteRedisLock = StringUtils.isNotEmpty(JedisUtil.get(RunPodService.DELETE_POD_REDIS_LOCK_KEY + podName)); | |||
} | |||
//没有锁,或已经释放锁,可以创pod | |||
createPodResult = k8sService.createPod(cluster, podName, tpiID, type, buildParams.getString("containers"), | |||
podCreateStrategy, null, bigDataFile, null, buildParams.getString("buildID"), | |||
buildParams.getString("tpmIdentifier"), buildParams.getBooleanValue("mountTestCaseDir")); | |||
pod = createPodResult.getPod(); | |||
if (Boolean.TRUE.equals(createPodResult.getSuccess())) {// pod创建成功时,记录pod所在节点ip | |||
String nodeIp = K8sUtils.getNodeIp(pod); | |||
if (StringUtils.isNotEmpty(nodeIp)) { | |||
updateRunPodNodeIp(podName, K8sUtils.getNodeIp(pod)); | |||
} | |||
} else { | |||
pod = null; | |||
} | |||
nodeManager.processCreatePodResult(cluster, createPodResult); | |||
} | |||
if (pod != null) { | |||
// 按需创建端口映射svc | |||
Integer needPortMapping = buildParams.getInteger("needPortMapping"); | |||
if (needPortMapping != null && needPortMapping != -1 && needPortMapping != 0) { | |||
if (!k8sService.svcExist(cluster, podName)) { | |||
Integer nodePort = buildParams.getInteger("nodePort"); | |||
logger.info("create run pod service, tipID: {}, needPort: {}, nodePort: {}", tpiID, needPortMapping, | |||
nodePort); | |||
k8sService.createService(cluster, podName, tpiID, needPortMapping, nodePort); | |||
updateRunPodSvcPort(podName, nodePort.toString()); | |||
} | |||
} | |||
} | |||
createPodResult.setPod(pod); | |||
} catch (Exception e) { | |||
logger.error("创建pod失败 tpiID: {}", tpiID, e); | |||
createPodResult.setStatus(CreatePodResult.CREATE_POD_STATUS_FAIL_DELETE_NOW); | |||
} | |||
// 异常情况下记录当前状态 | |||
if (createPodResult.getPod() == null) { | |||
k8sService.describePod(cluster, podName); | |||
} | |||
return createPodResult; | |||
} | |||
@Transactional | |||
public Pod createJupyterPod(final String cluster, String podName, String tpiID, String baseName, String containers, Integer podType, Integer delayDeleteTime, String identifier) { | |||
RunPod runPod = this.getRunPodForUpdate(podName); | |||
if (runPod == null) { | |||
JSONObject mainContainer = ContainerUtil.getMainContainer(containers); | |||
this.insertRunPod(cluster, podName, mainContainer, podType, RunPod.RUN_POD_PRIORITY_DEFAULT, delayDeleteTime); | |||
} else { | |||
updateRunPod(runPod, delayDeleteTime); | |||
} | |||
// jupyter创建pod暂没处理超时!!! | |||
PodCreateStrategy podCreateStrategy = new PodCreateStrategy().setNormalPodCanShenlong(false).setNormalPodCanOj(false) | |||
.setAutoscaleNodeWeight(clusterManager.getScaleNodeWeight(cluster)).setTimeLimit(120);// jupyter允许创建耗时120s | |||
Map<String, String> envs = Collections.singletonMap(JUPYTER_PW_ENV, RandomStringUtils.randomAlphanumeric(16)); | |||
CreatePodResult createPodResult = k8sService.createPod(cluster, podName, tpiID, baseName, containers, podCreateStrategy, envs, identifier, null, null); | |||
Pod pod = createPodResult.getPod(); | |||
if (Boolean.TRUE.equals(createPodResult.getSuccess())) {// pod创建成功时,记录pod所在节点ip | |||
String nodeIp = K8sUtils.getNodeIp(pod); | |||
if (StringUtils.isNotEmpty(nodeIp)) { | |||
updateRunPodNodeIp(podName, K8sUtils.getNodeIp(pod)); | |||
} | |||
} else { | |||
pod = null; | |||
} | |||
return pod; | |||
} | |||
@Transactional | |||
public Pod createWebsshPod(final String cluster, String podName, String tpiID, String type, String containers, | |||
Integer podType, Integer delayDeleteTime, String bigDataFile, Boolean createImage) { | |||
RunPod runPod = this.getRunPodForUpdate(podName); | |||
if (runPod == null) { | |||
JSONObject mainContainer = ContainerUtil.getMainContainer(containers); | |||
this.insertRunPod(cluster, podName, mainContainer, podType, RunPod.RUN_POD_PRIORITY_DEFAULT, delayDeleteTime); | |||
} else { | |||
updateRunPod(runPod, delayDeleteTime); | |||
} | |||
//设置pod创建的最长时间限制等于pod所需所有容器启动时间限制之和 | |||
int createPodTimeLimit = Math.max(ContainerUtil.getContainersStartTime(containers), 15); | |||
// webssh创建pod暂没处理超时!!! | |||
PodCreateStrategy podCreateStrategy = new PodCreateStrategy().setNormalPodCanShenlong(false).setNormalPodCanOj(false) | |||
.setAutoscaleNodeWeight(clusterManager.getScaleNodeWeight(cluster)).setTimeLimit(createPodTimeLimit); | |||
// 随机生成自定义pwd | |||
CreatePodResult createPodResult = k8sService.createPod(cluster, podName, tpiID, type, containers, podCreateStrategy, Collections.singletonMap(WEBSSH_PW_ENV ,RandomStringUtils.randomAlphanumeric(16)), bigDataFile, createImage, null); | |||
Pod pod = createPodResult.getPod(); | |||
if (Boolean.TRUE.equals(createPodResult.getSuccess())) {// pod创建成功时,记录pod所在节点ip | |||
String nodeIp = K8sUtils.getNodeIp(pod); | |||
if (StringUtils.isNotEmpty(nodeIp)) { | |||
updateRunPodNodeIp(podName, K8sUtils.getNodeIp(pod)); | |||
} | |||
} else { | |||
pod = null; | |||
} | |||
return pod; | |||
} | |||
@Transactional | |||
public Pod createVncPod(final String cluster, String podName, String tpiID, String baseName, String containers, Integer podType, Integer delayDeleteTime, String bigDataFile, Boolean createImage) { | |||
RunPod runPod = this.getRunPodForUpdate(podName); | |||
if (runPod == null) { | |||
JSONObject mainContainer = ContainerUtil.getMainContainer(containers); | |||
this.insertRunPod(cluster, podName, mainContainer, podType, RunPod.RUN_POD_PRIORITY_DEFAULT, delayDeleteTime); | |||
} else { | |||
updateRunPod(runPod, delayDeleteTime); | |||
} | |||
//设置pod创建的最长时间限制等于pod所需所有容器启动时间限制之和 | |||
int createPodTimeLimit= ContainerUtil.getContainersStartTime( containers ); | |||
createPodTimeLimit = Math.max(createPodTimeLimit, 15); | |||
// vnc创建pod暂没处理超时!!! | |||
PodCreateStrategy podCreateStrategy = new PodCreateStrategy().setNormalPodCanShenlong(Boolean.FALSE) | |||
.setNormalPodCanOj(Boolean.FALSE) | |||
.setPodNoSchedulableWeight(sysConfigService.getPodNoSchedulableWeight()) | |||
.setAutoscaleNodeWeight(clusterManager.getScaleNodeWeight(cluster)) | |||
.setTimeLimit(createPodTimeLimit); | |||
nodeManager.processShenlongPodStrategy(cluster, podCreateStrategy, containers); | |||
CreatePodResult createPodResult = k8sService.createPod(cluster, podName, tpiID, baseName, containers, | |||
podCreateStrategy, Collections.singletonMap(VNC_PW_ENV, RandomStringUtils.randomAlphanumeric(16)), bigDataFile, createImage, null); | |||
Pod pod = createPodResult.getPod(); | |||
if (Boolean.TRUE.equals(createPodResult.getSuccess())) {// pod创建成功时,记录pod所在节点ip | |||
String nodeIp = K8sUtils.getNodeIp(pod); | |||
if (StringUtils.isNotEmpty(nodeIp)) { | |||
updateRunPodNodeIp(podName, K8sUtils.getNodeIp(pod)); | |||
} | |||
} else { | |||
pod = null; | |||
} | |||
return pod; | |||
} | |||
public RunPod getRunPodForUpdate(String podName) { | |||
return runPodMapper.selectRunPodForUpdate(podName); | |||
} | |||
public RunPod getRunPodByName(String podName) { | |||
return runPodMapper.selectByName(podName); | |||
} | |||
public RunPod getSshRunPodByTpiID(String tpiID) { | |||
return runPodMapper.selectSshRunPodByTpiID(tpiID); | |||
} | |||
public RunPod insertRunPod(final String cluster, String podName, JSONObject mainContainer, Integer runPodType, Long priority, int timeLimit) { | |||
RunPod runPod = new RunPod(); | |||
runPod.setName(podName); | |||
runPod.setCluster(cluster); | |||
runPod.setRunPodType(runPodType); | |||
runPod.setPriority(priority); | |||
runPod.setCreateBy(appConfig.getBridgeInstanceName()); | |||
runPod.setImageName(mainContainer.getString("imageName")); | |||
runPod.setCpuLimit(Double.parseDouble(mainContainer.getString("cpuLimit"))); | |||
Integer memoryLimit = Integer.parseInt(mainContainer.getString("memoryLimit").replace("M", "")); | |||
runPod.setMemoryLimit(memoryLimit); | |||
runPod.setCpuRequest(Double.parseDouble(mainContainer.getString("cpuRequest"))); | |||
Integer memoryRequest = Integer.parseInt(mainContainer.getString("memoryRequest").replace("M", "")); | |||
runPod.setMemoryRequest(memoryRequest); | |||
LocalDateTime now = LocalDateTime.now(); | |||
runPod.setCreateTime(now); | |||
runPod.setUpdateTime(now); | |||
LocalDateTime expireTime = now.plusSeconds(timeLimit); | |||
runPod.setExpireTime(expireTime); | |||
runPodMapper.insertSelective(runPod); | |||
return runPod; | |||
} | |||
private void updateRunPod(RunPod runPod, int timeLimit) { | |||
RunPod param = new RunPod(); | |||
param.setId(runPod.getId()); | |||
LocalDateTime now = LocalDateTime.now(); | |||
param.setUpdateTime(now); | |||
now = now.plusSeconds(timeLimit); | |||
param.setExpireTime(now); | |||
runPodMapper.updateByPrimaryKeySelective(param); | |||
} | |||
public void updateRunPodSvcPort(String podName, String svcPort) { | |||
RunPodQueryParam param = new RunPodQueryParam(); | |||
param.setName(podName); | |||
param.setSvcPort(svcPort); | |||
runPodMapper.updatePortByName(param); | |||
} | |||
public void updateRunPodSshPort(String podName, String sshPort) { | |||
RunPodQueryParam param = new RunPodQueryParam(); | |||
param.setName(podName); | |||
param.setSshPort(sshPort); | |||
runPodMapper.updatePortByName(param); | |||
} | |||
public void updateRunPodNodeIp(String podName, String nodeIp) { | |||
RunPodQueryParam param = new RunPodQueryParam(); | |||
param.setName(podName); | |||
param.setNodeIp(nodeIp); | |||
runPodMapper.updatePortByName(param); | |||
} | |||
/** | |||
* 统计run_pod | |||
* @return | |||
*/ | |||
public List<NodeResStat> statNodeRes() { | |||
return runPodMapper.statNodeRes(null); | |||
} | |||
public Integer getServicePort(String podName) { | |||
RunPod runPod = getRunPodByName(podName); | |||
if (runPod != null) { | |||
String port = runPod.getSvcPort(); | |||
if (StringUtils.isNotEmpty(port)) { | |||
return Integer.parseInt(port); | |||
} | |||
} | |||
return null; | |||
} | |||
public String getRunPodCluster(String tpiID, Integer podType) { | |||
String podName = TpUtils.buildEvaPodName(tpiID, podType); | |||
RunPod runPod = this.getRunPodByName(podName); | |||
if (runPod != null) { | |||
return runPod.getCluster(); | |||
} | |||
return null; | |||
} | |||
public List<String> podNumStatGroupByNodeIp(String cluster) { | |||
return runPodMapper.podNumStatGroupByNodeIp(cluster); | |||
} | |||
public List<RunPod> getRunPodsByNodeIp(String cluster, String nodeIp) { | |||
RunPodQueryParam runPodQueryParam = new RunPodQueryParam(); | |||
runPodQueryParam.setCluster(cluster); | |||
runPodQueryParam.setNodeIp(nodeIp); | |||
return runPodMapper.selectRunPods(runPodQueryParam); | |||
} | |||
public void deleteRunPodByNodeIp(String cluster, String nodeIp) { | |||
RunPodQueryParam runPodQueryParam = new RunPodQueryParam(); | |||
runPodQueryParam.setCluster(cluster); | |||
runPodQueryParam.setExpireTime(LocalDateTime.now()); | |||
runPodQueryParam.setNodeIp(nodeIp); | |||
runPodMapper.updateExpireTimeByNodeIp(runPodQueryParam); | |||
} | |||
/** | |||
* 杀死运行中的评测进程 | |||
*/ | |||
public void killEvaProcess(String tpiID) { | |||
List<Pod> podList = k8sService.getPodList(TpCsts.LOCAL_CLUSTER, TpCsts.TPI_ID, tpiID); | |||
podList.forEach( pod -> | |||
K8sPodCommandExecutor.execCommandInPod(pod.getMetadata().getName(), new String[]{"bash", "/data/platform/eva/kill.sh"}, 3) | |||
); | |||
} | |||
@Transactional | |||
public Pod createVscodePod(String cluster, String podName, String tpiID, String type, String containers, Integer podType, int delayDeleteTime, String bigDataFile, Boolean createImage) { | |||
RunPod runPod = this.getRunPodForUpdate(podName); | |||
if (runPod == null) { | |||
JSONObject mainContainer = ContainerUtil.getMainContainer(containers); | |||
this.insertRunPod(cluster, podName, mainContainer, podType, RunPod.RUN_POD_PRIORITY_DEFAULT, delayDeleteTime); | |||
} else { | |||
updateRunPod(runPod, delayDeleteTime); | |||
} | |||
//设置pod创建的最长时间限制等于pod所需所有容器启动时间限制之和 | |||
int createPodTimeLimit = Math.max(ContainerUtil.getContainersStartTime(containers), 15); | |||
// vscode创建pod暂没处理超时!!! | |||
PodCreateStrategy podCreateStrategy = new PodCreateStrategy().setNormalPodCanShenlong(false).setNormalPodCanOj(false) | |||
.setAutoscaleNodeWeight(clusterManager.getScaleNodeWeight(cluster)).setTimeLimit(createPodTimeLimit); | |||
CreatePodResult createPodResult = k8sService.createPod(cluster, podName, tpiID, type, containers, podCreateStrategy, null, bigDataFile, createImage, null); | |||
Pod pod = createPodResult.getPod(); | |||
// pod创建成功时,记录pod所在节点ip | |||
if (Boolean.TRUE.equals(createPodResult.getSuccess())) { | |||
String nodeIp = K8sUtils.getNodeIp(pod); | |||
if (StringUtils.isNotEmpty(nodeIp)) { | |||
updateRunPodNodeIp(podName, K8sUtils.getNodeIp(pod)); | |||
} | |||
} else { | |||
pod = null; | |||
} | |||
return pod; | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
package com.imitate.common.k8s.service; | |||
import com.imitate.common.k8s.mapper.SecurityContextConfigMapper; | |||
import com.imitate.common.k8s.pojo.SecurityContextConfig; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
@Service | |||
public class SecurityContextService { | |||
private static final Logger logger = LoggerFactory.getLogger(SecurityContextService.class); | |||
private volatile Map<String, SecurityContextConfig> securityContextConfigMap = new HashMap<>(); | |||
@Autowired | |||
private SecurityContextConfigMapper securityContextConfigMapper; | |||
public SecurityContextConfig getSecurityContextConfig(String cluster, String imageName) { | |||
SecurityContextConfig config = securityContextConfigMap.get(imageName); | |||
if (config != null && cluster.equals(config.getCluster())) { | |||
logger.info("getSecurityContextConfig success, config: {}", config); | |||
return config; | |||
} | |||
return null; | |||
} | |||
public void refreshSecurityContextConfigs() { | |||
List<SecurityContextConfig> configs = securityContextConfigMapper.selectAll(); | |||
Map<String, SecurityContextConfig> securityContextConfigMapTemp = new HashMap<>(); | |||
for (SecurityContextConfig config : configs) { | |||
securityContextConfigMapTemp.put(config.getImageName(), config); | |||
} | |||
securityContextConfigMap = securityContextConfigMapTemp; | |||
} | |||
} |
@@ -0,0 +1,227 @@ | |||
package com.imitate.common.k8s.util; | |||
import com.alibaba.fastjson.JSONArray; | |||
import com.alibaba.fastjson.JSONObject; | |||
import com.imitate.common.k8s.bean.BridgeContainer; | |||
import io.fabric8.kubernetes.api.model.*; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import java.util.*; | |||
public final class ContainerUtil { | |||
private static final Logger log = LoggerFactory.getLogger(ContainerUtil.class); | |||
public static JSONObject getMainContainer(String containers) { | |||
JSONArray jsonArray = JSONArray.parseArray(containers); | |||
JSONObject mainContainer = jsonArray.getJSONObject(0); | |||
for (int i = 0; i < jsonArray.size(); i++) { | |||
JSONObject container = jsonArray.getJSONObject(i); | |||
if ("main".equals(container.getString("type"))) { | |||
mainContainer = container; | |||
} | |||
} | |||
String image = mainContainer.getString("image"); | |||
String[] imageInfo = image.split(":"); | |||
String imageName = imageInfo[0]; | |||
String imageVersion = imageInfo.length > 1 ? imageInfo[1] : ""; | |||
mainContainer.put("imageName", imageName); | |||
mainContainer.put("imageVersion", imageVersion); | |||
return mainContainer; | |||
} | |||
public static BridgeContainer parseBridgeContainer(String container) { | |||
BridgeContainer bc = new BridgeContainer(); | |||
JSONObject obj = JSONObject.parseObject(container); | |||
bc.setName(obj.getString("imageName")); | |||
bc.setImage(obj.getString("image")); | |||
bc.setCpuLimit(Double.parseDouble(obj.getString("cpuLimit"))); | |||
bc.setCpuRequest(Double.parseDouble(obj.getString("cpuRequest"))); | |||
Double memoryLimit = Double.parseDouble(obj.getString("memoryLimit").replace("M", "")); | |||
bc.setMemoryLimit(memoryLimit); | |||
Double memoryRequest = Double.parseDouble(obj.getString("memoryRequest").replace("M", "")); | |||
bc.setMemoryRequest(memoryRequest); | |||
return bc; | |||
} | |||
public static double getRequestCpu(String containers) { | |||
double sum = 0; | |||
List<Container> cs = parseContainers(containers); | |||
for (Container c:cs) { | |||
String cpu = c.getResources().getRequests().get("cpu").getAmount(); | |||
try{ | |||
if( cpu!=null ) | |||
sum += Double.parseDouble(cpu); | |||
}catch ( Exception e ) | |||
{ | |||
log.error( "web层传递的containers参数中的cpuRequest不合法,cpuRequest:{}",cpu,e ); | |||
} | |||
} | |||
return sum; | |||
} | |||
public static double getLimitCpu(String containers) { | |||
double sum = 0; | |||
List<Container> cs = parseContainers(containers); | |||
for (Container c:cs) { | |||
String cpu = c.getResources().getLimits().get("cpu").getAmount(); | |||
try{ | |||
if( cpu!=null ) | |||
sum += Double.parseDouble(cpu); | |||
}catch ( Exception e ) | |||
{ | |||
log.error( "web层传递的containers参数中的cpuLimit不合法,cpuLimit:{}",cpu,e ); | |||
} | |||
} | |||
return sum; | |||
} | |||
public static double getRequestMemory(String containers) { | |||
double sum = 0L; | |||
List<Container> cs = parseContainers(containers); | |||
for (Container c:cs) { | |||
String memory = c.getResources().getRequests().get("memory").getAmount(); | |||
memory=memory.replaceAll( "M","" ); | |||
try{ | |||
sum += Double.parseDouble(memory); | |||
}catch ( Exception e ) | |||
{ | |||
log.error( "web层传递的containers参数中的memoryRequest不合法,memoryRequest:{}",memory,e ); | |||
} | |||
} | |||
return sum; | |||
} | |||
public static double getLimitMemory(String containers) { | |||
double sum = 0; | |||
List<Container> cs = parseContainers(containers); | |||
for (Container c:cs) { | |||
String memory = c.getResources().getLimits().get("memory").getAmount(); | |||
memory=memory.replaceAll( "M","" ); | |||
try{ | |||
sum += Double.parseDouble(memory); | |||
}catch ( Exception e ) | |||
{ | |||
log.error( "web层传递的containers参数中的memoryLimit不合法,memoryLimit:{}",memory,e ); | |||
} | |||
} | |||
return sum; | |||
} | |||
public static List<Container> parseContainers(String containers) { | |||
JSONArray jsonArray = JSONArray.parseArray(containers); | |||
List<Container> containerList = new ArrayList<>(); | |||
for (int i = 0; i < jsonArray.size(); i++) { | |||
Container container = new Container(); | |||
JSONObject props = jsonArray.getJSONObject(i); | |||
container.setName(props.getString("image").split(":")[0]); | |||
container.setImage(props.getString("image")); | |||
container.setImagePullPolicy("IfNotPresent"); | |||
Map<String, Quantity> limits = new HashMap<>(2); | |||
limits.put("cpu", new Quantity(props.getString("cpuLimit"))); | |||
limits.put("memory", new Quantity(props.getString("memoryLimit"))); | |||
// 最低资源限制 | |||
Map<String, Quantity> requests = new HashMap<>(2); | |||
requests.put("cpu", new Quantity(props.getString("cpuRequest"))); | |||
requests.put("memory", new Quantity(props.getString("memoryRequest"))); | |||
ResourceRequirements resourceRequirements = new ResourceRequirements(limits, requests); | |||
container.setResources(resourceRequirements); | |||
// 次类别设置启动脚本为不包含ssh启动命令的脚本 | |||
String cmdParams = props.getString("cmd_params"); | |||
if ("sub".equals(props.getString("type"))) { | |||
container.setCommand(StringUtils.isNotEmpty(cmdParams) ? Arrays.asList(cmdParams.split(",")) : Collections.singletonList("/start2.sh")); | |||
containerList.add(container); | |||
} else { | |||
if(StringUtils.isNotEmpty(cmdParams)){ | |||
container.setCommand(Arrays.asList(cmdParams.split(","))); | |||
} | |||
containerList.add(0, container); | |||
} | |||
} | |||
return containerList; | |||
} | |||
public static void setLifecycleStartCommand(Container container, List<String> command) { | |||
Lifecycle lifecycle = new Lifecycle(); | |||
Handler postStart = new Handler(); | |||
ExecAction exec = new ExecAction(); | |||
exec.setCommand(command); | |||
postStart.setExec(exec); | |||
lifecycle.setPostStart(postStart); | |||
container.setLifecycle(lifecycle); | |||
} | |||
public static boolean imagesMatch(String containers, List<Container> containersList) { | |||
List<String> images = getContainersImages(containers); | |||
boolean match = false; | |||
for (String image : images) { | |||
for (Container container : containersList) { | |||
match = false; | |||
if (image.equals(container.getImage())) { | |||
match = true; | |||
break; | |||
} | |||
} | |||
if (!match) { | |||
return false; | |||
} | |||
} | |||
return true; | |||
} | |||
private static List<String> getContainersImages(String containers) { | |||
JSONArray jsonArray = JSONArray.parseArray(containers); | |||
List<String> images = new ArrayList<>(jsonArray.size()); | |||
for (int i = 0; i < jsonArray.size(); i++) { | |||
images.add(jsonArray.getJSONObject(i).getString("image")); | |||
} | |||
return images; | |||
} | |||
/** | |||
* 获取实训所需容器启动时间之和 | |||
* @param containers 实训所需容器 | |||
* @return 实训所需容器启动时间之和 | |||
*/ | |||
public static int getContainersStartTime(String containers) { | |||
JSONArray jsonArray = JSONArray.parseArray(containers); | |||
int containersStartTime = 0; | |||
for (int i = 0; i < jsonArray.size(); i++) { | |||
try { | |||
String startTime = jsonArray.getJSONObject(i).getString("startTime"); | |||
if (startTime != null) { | |||
containersStartTime += Integer.parseInt(startTime); | |||
} else { | |||
containersStartTime += 15; | |||
} | |||
} catch (Exception e) { | |||
log.error("web层传递的startTime参数不合法,containers参数:{}", containers, e); | |||
} | |||
} | |||
return containersStartTime; | |||
} | |||
} |
@@ -0,0 +1,30 @@ | |||
package com.imitate.common.k8s.util; | |||
import com.imitate.common.bean.ShellResult; | |||
import com.imitate.common.util.ShellUtil; | |||
/** | |||
* @author huqifeng | |||
* @Time 2021-7-23 | |||
* @Description 当k8s pod出现问题时,执行shell语句返回错误的原因 | |||
*/ | |||
public final class GetPodErrorReasonUtil { | |||
/** | |||
* @Description 当k8s pod为Failed状态时,执行shell语句返回错误的原因 | |||
* @param podName | |||
* @return errReasonOut,即pod为Failed状态的原因 | |||
*/ | |||
public static String getPodFailedReason( String podName ) | |||
{ | |||
String cmd = "timeout 2 kubectl " + "describe pod " + podName + " | awk '$2==\"Failed\"'"; | |||
ShellResult result = ShellUtil.executeAndGetExitStatus(cmd, 1); | |||
String errReasonOut=null; | |||
if (result.getExitStatus() == 0) | |||
{ | |||
errReasonOut = result.getOut(); | |||
} | |||
return errReasonOut; | |||
} | |||
} |
@@ -0,0 +1,93 @@ | |||
package com.imitate.common.k8s.util; | |||
import com.imitate.common.sys.pojo.ClusterConfig; | |||
import com.imitate.common.sys.service.ClusterConfigService; | |||
import com.imitate.common.sys.settings.AppConfig; | |||
import com.imitate.common.bean.BeanFactory; | |||
import io.fabric8.kubernetes.client.Config; | |||
import io.fabric8.kubernetes.client.ConfigBuilder; | |||
import io.fabric8.kubernetes.client.DefaultKubernetesClient; | |||
import io.fabric8.kubernetes.client.KubernetesClient; | |||
import org.apache.commons.lang3.StringUtils; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
public final class K8sClientUtil { | |||
private static volatile Map<String, KubernetesClient> clients = new HashMap<>(); | |||
public static KubernetesClient getClient(String cluster) { | |||
KubernetesClient client = clients.get(cluster); | |||
if (client == null) { | |||
synchronized (K8sClientUtil.class) { | |||
client = clients.get(cluster); | |||
if (client == null) { | |||
ClusterConfigService clusterConfigService = (ClusterConfigService) BeanFactory | |||
.getObejct("clusterConfigService"); | |||
AppConfig appConfig = (AppConfig) BeanFactory.getObejct("appConfig"); | |||
ClusterConfig clusterConfig = clusterConfigService.getClusterConfig(cluster); | |||
if (StringUtils.isNotEmpty(clusterConfig.getUsername())) { | |||
Config config = new ConfigBuilder().withMasterUrl(clusterConfig.getMasterUrl()) | |||
.withNamespace("default").withTrustCerts(true).withUsername(clusterConfig.getUsername()) | |||
.withPassword(clusterConfig.getPassword()) | |||
.withMaxConcurrentRequests(appConfig.getK8sMaxConcurrentRequests()) | |||
.withMaxConcurrentRequestsPerHost(appConfig.getK8sMaxConcurrentRequestsPerHost()) | |||
.withWebsocketTimeout(150000) | |||
.withWebsocketPingInterval(0) | |||
.build(); | |||
client = new DefaultKubernetesClient(config); | |||
} else if (StringUtils.isNotEmpty(clusterConfig.getCaCertData())) { | |||
Config config = new ConfigBuilder().withMasterUrl(clusterConfig.getMasterUrl()) | |||
.withNamespace("default").withTrustCerts(true) | |||
.withCaCertData(clusterConfig.getCaCertData()) | |||
.withClientCertData(clusterConfig.getClientCerData()) | |||
.withClientKeyData(clusterConfig.getClientKeyData()) | |||
.withMaxConcurrentRequests(appConfig.getK8sMaxConcurrentRequests()) | |||
.withMaxConcurrentRequestsPerHost(appConfig.getK8sMaxConcurrentRequestsPerHost()) | |||
.withWebsocketTimeout(150000) | |||
.withWebsocketPingInterval(0) | |||
.build(); | |||
client = new DefaultKubernetesClient(config); | |||
} | |||
Map<String, KubernetesClient> tmp = new HashMap<>(clients); | |||
tmp.put(cluster, client); | |||
clients = tmp; | |||
} | |||
} | |||
} | |||
return client; | |||
} | |||
public static void refreshK8sClients() { | |||
ClusterConfigService clusterConfigService = (ClusterConfigService) BeanFactory | |||
.getObejct("clusterConfigService"); | |||
List<String> availableClusters = clusterConfigService.getAvailableClusters(); | |||
List<String> list = new ArrayList<>(clients.keySet()); | |||
for (String cluster : list) { | |||
boolean exist = false; | |||
for (String availableCluster : availableClusters) { | |||
if (cluster.equals(availableCluster)) { | |||
exist = true; | |||
break; | |||
} | |||
} | |||
if (!exist) { | |||
removeClient(cluster); | |||
} | |||
} | |||
} | |||
private static synchronized void removeClient(String cluster) { | |||
Map<String, KubernetesClient> tmp = new HashMap<>(clients); | |||
tmp.remove(cluster); | |||
clients = tmp; | |||
} | |||
} |
@@ -0,0 +1,119 @@ | |||
package com.imitate.common.k8s.util; | |||
/** | |||
* k8s 命令执行器 | |||
* | |||
* @author 威少 | |||
*/ | |||
import com.imitate.common.bean.ShellResult; | |||
import io.fabric8.kubernetes.api.model.Pod; | |||
import io.fabric8.kubernetes.client.KubernetesClient; | |||
import io.fabric8.kubernetes.client.dsl.ExecListener; | |||
import io.fabric8.kubernetes.client.dsl.ExecWatch; | |||
import lombok.SneakyThrows; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.apache.commons.lang3.StringUtils; | |||
import java.io.ByteArrayOutputStream; | |||
import java.util.concurrent.CompletableFuture; | |||
import java.util.concurrent.TimeUnit; | |||
import java.util.concurrent.TimeoutException; | |||
import static com.imitate.common.constant.TpCsts.*; | |||
@Slf4j | |||
public class K8sPodCommandExecutor { | |||
/** | |||
* Pod中执行命令 | |||
* | |||
* @param podName pod名 | |||
* @param cmd 命令 | |||
* @param timeout 超时时间 | |||
* @return 执行结果 | |||
*/ | |||
public static ShellResult execCommandInPod(String podName, String[] cmd, long timeout) { | |||
return execCommandInPod(LOCAL_CLUSTER, DEFAULT_NAMESPACE, podName, cmd, timeout); | |||
} | |||
/** | |||
* Pod中执行命令 | |||
* | |||
* @param cluster 集群 | |||
* @param namespace 命名空间 | |||
* @param podName pod名 | |||
* @param cmd 命令 | |||
* @param timeout 超时时间 | |||
* @return 执行结果 | |||
*/ | |||
public static ShellResult execCommandInPod(String cluster, String namespace, String podName, String[] cmd, long timeout) { | |||
ShellResult result = new ShellResult(ShellResult.ExitStatus.FAIL.getCode(), StringUtils.EMPTY); | |||
KubernetesClient client = K8sClientUtil.getClient(cluster); | |||
Pod pod = client.pods().inNamespace(namespace).withName(podName).get(); | |||
CompletableFuture<ShellResult> data = new CompletableFuture<>(); | |||
try (ExecWatch ignored = execCmd(client, pod, data, cmd)) { | |||
result = data.get(timeout, TimeUnit.SECONDS); | |||
result.setExitStatus(ShellResult.ExitStatus.SUCCESS.getCode()); | |||
} catch (TimeoutException e) { | |||
log.warn("exec command in pod timeout, cmd: {}, pod: {}", cmd, podName, e); | |||
result.setExitStatus(ShellResult.ExitStatus.TIMEOUT.getCode()); | |||
} catch (Exception e) { | |||
log.error("exec command in pod failure, cmd: {}, pod: {}", cmd, podName, e); | |||
} | |||
log.info("execute command: {} in pod: {} with timeout: {}, result: {}", cmd, podName, timeout, result); | |||
return result; | |||
} | |||
/** | |||
* 执行命令 | |||
* | |||
* @param client 客户端 | |||
* @param pod pod | |||
* @param data 返回数据future | |||
* @param command 命令 | |||
*/ | |||
private static ExecWatch execCmd(KubernetesClient client, Pod pod, CompletableFuture<ShellResult> data, String... command) { | |||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | |||
ByteArrayOutputStream errorChannelStream = new ByteArrayOutputStream(); | |||
String firstContainerName = pod.getSpec().getContainers().get(0).getName(); | |||
return client.pods() | |||
.inNamespace(pod.getMetadata().getNamespace()) | |||
.withName(pod.getMetadata().getName()) | |||
.inContainer(firstContainerName) | |||
.writingOutput(outputStream) | |||
.writingError(outputStream) | |||
.writingErrorChannel(errorChannelStream) | |||
.usingListener(new PodBashExecListener(data, outputStream)) | |||
.exec(command); | |||
} | |||
static class PodBashExecListener implements ExecListener { | |||
private CompletableFuture<ShellResult> data; | |||
private ByteArrayOutputStream baos; | |||
public PodBashExecListener(CompletableFuture<ShellResult> data, ByteArrayOutputStream baos) { | |||
this.data = data; | |||
this.baos = baos; | |||
} | |||
@SneakyThrows | |||
@Override | |||
public void onFailure(Throwable t, Response failureResponse) { | |||
if (failureResponse == null) { | |||
data.completeExceptionally(t); | |||
} else { | |||
data.complete(new ShellResult(failureResponse.code(), failureResponse.body())); | |||
} | |||
} | |||
@Override | |||
public void onClose(int code, String reason) { | |||
data.complete(new ShellResult(code, baos.toString())); | |||
} | |||
} | |||
} |
@@ -0,0 +1,371 @@ | |||
package com.imitate.common.k8s.util; | |||
import com.google.common.collect.Lists; | |||
import com.imitate.common.constant.TpCsts; | |||
import com.imitate.common.k8s.pojo.ErrorPodInfo; | |||
import com.imitate.common.bean.ShellResult; | |||
import com.imitate.common.sys.constant.SysConfigCsts; | |||
import com.imitate.common.util.ShellUtil; | |||
import io.fabric8.kubernetes.api.model.*; | |||
import io.fabric8.kubernetes.client.utils.PodStatusUtil; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.util.CollectionUtils; | |||
import java.time.LocalDateTime; | |||
import java.util.List; | |||
public final class K8sUtils { | |||
private static final Logger logger = LoggerFactory.getLogger(K8sUtils.class); | |||
public static int getNodePort(Service svc) { | |||
List<ServicePort> list = svc.getSpec().getPorts(); | |||
for (ServicePort sp : list) { | |||
return sp.getNodePort(); | |||
} | |||
throw new RuntimeException("svc: " + svc.getMetadata().getName() + " 中没有找到NodePort"); | |||
} | |||
public static double getNodeAllocatableCpu(Node node) { | |||
String amount = node.getStatus().getAllocatable().get("cpu").getAmount(); | |||
return parseCpuAmount(amount); | |||
} | |||
public static double getNodeCapacityCpu(Node node) { | |||
String amount = node.getStatus().getCapacity().get("cpu").getAmount(); | |||
return parseCpuAmount(amount); | |||
} | |||
public static double getRequestCpu(Pod pod) { | |||
String amount = pod.getSpec().getContainers().get(0).getResources().getRequests().get("cpu").getAmount(); | |||
return parseCpuAmount(amount); | |||
} | |||
public static double getNodeAllocatableMemory(Node node) { | |||
String amount = node.getStatus().getAllocatable().get("memory").getAmount(); | |||
return parseMemoryAmount(amount); | |||
} | |||
public static double getNodeCapacityMemory(Node node) { | |||
String amount = node.getStatus().getCapacity().get("memory").getAmount(); | |||
return parseCpuAmount(amount); | |||
} | |||
public static boolean isOjNode(Node node) { | |||
String label = node.getMetadata().getLabels().get(TpCsts.OJ_LABEL_KEY); | |||
return StringUtils.isNotEmpty(label) && TpCsts.OJ_LABEL_VALUE.equals(label); | |||
} | |||
public static boolean isOjPod(Pod pod) { | |||
String label = pod.getMetadata().getLabels().get(TpCsts.OJ_LABEL_KEY); | |||
return StringUtils.isNotEmpty(label) && TpCsts.OJ_LABEL_VALUE.equals(label); | |||
} | |||
private static double parseCpuAmount(String amount) { | |||
// cpu可能 的形式 100m, "2", 1 | |||
amount = amount.trim().replaceAll("\"", ""); // \"有这种情况? | |||
if (amount.endsWith("m")) { | |||
amount = amount.substring(0, amount.length() - 1); | |||
return Double.parseDouble(amount) / 1000; | |||
} else { | |||
return Double.parseDouble(amount); | |||
} | |||
} | |||
private static double parseMemoryAmount(String amount) { | |||
// memory返回的格式为:16267956Ki.需要去除最后两个字符 | |||
return Double.parseDouble(amount.substring(0, amount.length() - 2)) / 1024; | |||
} | |||
public static String getIp(Node node) { | |||
return node.getStatus().getAddresses().get(0).getAddress(); | |||
} | |||
public static String getLabel(Node node, String labelKey) { | |||
return node.getMetadata().getLabels().get(labelKey); | |||
} | |||
public static String getNodeIp (Pod pod) { | |||
return pod.getStatus().getHostIP(); | |||
} | |||
public static String getNodeName (Node node) { | |||
return node.getMetadata().getName(); | |||
} | |||
public static String getNodeName(Pod pod) { | |||
return pod == null ? null : pod.getSpec().getNodeName(); | |||
} | |||
public static String getPodName(Pod pod) { | |||
return pod == null ? null : pod.getMetadata().getName(); | |||
} | |||
public static boolean isErrImagePull(Pod pod) { | |||
List<ContainerStatus> list = pod.getStatus().getContainerStatuses(); | |||
for (ContainerStatus cs : list) { | |||
ContainerState state = cs.getState(); | |||
if (state == null) { | |||
continue; | |||
} | |||
ContainerStateWaiting wating = state.getWaiting(); | |||
if (wating == null) { | |||
continue; | |||
} | |||
boolean r = "ErrImagePull".equals(wating.getReason()); | |||
if (r) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
/** | |||
* @author huqifeng | |||
* @Time 2021-7-19 | |||
* @Description 查询错误pod在节点上是否在服务器上不存在对应的镜像 | |||
* @param pod k8s pod | |||
* @return 不存在的镜像列表 | |||
*/ | |||
public static List<String> podImagesNotExist(Pod pod) { | |||
List<ContainerStatus> containerStatusList = pod.getStatus().getContainerStatuses(); | |||
List<PodCondition> podConditionList = pod.getStatus().getConditions(); | |||
String nodeIp = pod.getStatus().getHostIP(); | |||
//带版本号的镜像名 | |||
String imageNameWithVersion; | |||
//镜像名imageName | |||
String imageName; | |||
//缺失的镜像 | |||
List<String> imageList = Lists.newArrayList(); | |||
//判断pod创建失败是否是由于镜像拉取失败而引起的 | |||
for (PodCondition podCondition : podConditionList) { | |||
if ("False".equals(podCondition.getStatus()) && "ContainersNotReady".equals(podCondition.getReason())) { | |||
String podConditionMessage = podCondition.getMessage(); | |||
if (podConditionMessage.startsWith("containers with unready status: [")) { | |||
//获取镜像名 | |||
for (ContainerStatus containerStatus : containerStatusList) { | |||
imageName=containerStatus.getName(); | |||
imageNameWithVersion = containerStatus.getImage(); | |||
if (imageNameWithVersion == null) { | |||
continue; | |||
} | |||
//执行shell命令查询镜像是否存在 | |||
//镜像不存在,则保存缺失的镜像名 | |||
if (!K8sUtils.searchDockerImage(imageName, nodeIp)) { | |||
imageList.add(imageNameWithVersion); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
return imageList; | |||
} | |||
/** | |||
* @author huqifeng | |||
* @Time 2021-7-19 | |||
* @Description 判断k8s节点上是否有某一个镜像 | |||
* @param imageName 镜像名 | |||
* @param nodeIp k8s节点ip | |||
* @return 若存在该镜像,返回true,否则返回false | |||
*/ | |||
public static boolean searchDockerImage(String imageName, String nodeIp) { | |||
String remoteConnection = String.format(SysConfigCsts.REMOTE_CONN_PREFIX_DEF, nodeIp); | |||
//在shell中执行查询镜像语句 | |||
String getCommand = remoteConnection + " docker images | grep " + imageName + " | awk '{print $1}'"; | |||
ShellResult result = ShellUtil.executeAndGetExitStatus(getCommand); | |||
return 0 == result.getExitStatus() && !"".equals(result.getOut()); | |||
} | |||
public static boolean isReadyStatus(Node node) { | |||
List<NodeCondition> conditions = node.getStatus().getConditions(); | |||
// 只统计Ready状态的节点 | |||
for (NodeCondition condition : conditions) { | |||
if ("Ready".equalsIgnoreCase(condition.getType()) | |||
&& "True".equalsIgnoreCase(condition.getStatus())) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
/** | |||
* 容器是否处于running的状态 | |||
*/ | |||
public static boolean isPodRunning(Pod pod) { | |||
return PodStatusUtil.isRunning(pod) && | |||
pod.getStatus().getContainerStatuses().stream().allMatch( | |||
status -> status.getReady() != null | |||
&& status.getState() != null | |||
&& status.getState().getRunning() != null); | |||
} | |||
public static boolean isNewCapacityNode(Node node, Integer time) { | |||
String createTimestamp = node.getMetadata().getCreationTimestamp(); | |||
if (createTimestamp.endsWith("Z")) { | |||
createTimestamp = createTimestamp.substring(0, createTimestamp.length() - 1); | |||
} | |||
// 转化为东八区时间 | |||
LocalDateTime createTime = LocalDateTime.parse(createTimestamp).plusHours(8); | |||
return LocalDateTime.now().plusMinutes(-time).isBefore(createTime); | |||
} | |||
/** | |||
* @author huqifeng | |||
* @Time 2021-8-6 | |||
* @Description 当pod创建超时时,捕获pod的phase,以及处于该phase的原因 | |||
* @param podStatus pod状况 | |||
* @param podName pod名称 | |||
* @param nodeIp 节点ip | |||
* @param errorPodInfo pod失败信息对象 | |||
*/ | |||
public static void catchPodPhaseReasonMessage(ErrorPodInfo errorPodInfo, PodStatus podStatus, String podName, String nodeIp ) | |||
{ | |||
String podPhase=podStatus.getPhase(); | |||
String podStatusReason=podStatus.getReason(); | |||
String podStatusMessage=podStatus.getMessage(); | |||
errorPodInfo.setPodPhase(podPhase); | |||
errorPodInfo.setPodStatusReason(podStatusReason); | |||
errorPodInfo.setPodConditionMessage(podStatusMessage); | |||
logger.info( "创建pod {}失败,nodeIp: {},podPhase: {},podStatusReason: {},podStatusMessage: {}",podName,nodeIp,podPhase,podStatusReason,podStatusMessage ); | |||
} | |||
/** | |||
* @author huqifeng | |||
* @Time 2021-8-6 | |||
* @Description 当pod创建超时时,捕获podCondition的状态,以及处于该状态的原因 | |||
* @param podStatus pod状况 | |||
* @param podName pod名称 | |||
* @param nodeIp 节点ip | |||
* @param errorPodInfo pod失败信息对象 | |||
*/ | |||
public static void catchPodConditionReasonMessage(ErrorPodInfo errorPodInfo,PodStatus podStatus,String podName,String nodeIp) | |||
{ | |||
List<PodCondition> conditions=podStatus.getConditions(); | |||
String podConditionType = null; | |||
String podConditionStatus = null; | |||
String podConditionReason = null; | |||
String podConditionMessage = null; | |||
for(PodCondition podCondition:conditions) | |||
{ | |||
podConditionType = StringUtils.join(podConditionType,podCondition.getType(),"_"); | |||
podConditionStatus = StringUtils.join(podConditionStatus,podCondition.getType(),":",podCondition.getStatus(),"_"); | |||
podConditionReason = StringUtils.join(podConditionReason,podCondition.getType(),":",podCondition.getReason(),"_"); | |||
podConditionMessage = StringUtils.join(podConditionMessage,podCondition.getType(),":",podCondition.getMessage(),"_"); | |||
logger.info("创建pod {}失败,nodeIp: {},podConditionType: {},podConditionReason: {},podConditionMessage: {}",podName,nodeIp,podCondition.getType(),podCondition.getReason(),podCondition.getMessage()); | |||
} | |||
errorPodInfo.setPodConditionType(podConditionType); | |||
errorPodInfo.setPodConditionStatus(podConditionStatus); | |||
errorPodInfo.setPodConditionReason(podConditionReason); | |||
errorPodInfo.setPodConditionMessage(podConditionMessage); | |||
} | |||
/** | |||
* @author huqifeng | |||
* @Time 2021-8-6 | |||
* @Description 当pod创建超时时,捕获Container的状态,以及处于该状态的原因 | |||
* @param podStatus pod状况 | |||
* @param podName pod名称 | |||
* @param nodeIp 节点ip | |||
* @param errorPodInfo pod失败信息对象 | |||
*/ | |||
public static void catchContainerStateReasonMessage(ErrorPodInfo errorPodInfo, PodStatus podStatus, String podName, String nodeIp ) | |||
{ | |||
List<ContainerStatus> containerStatuses=podStatus.getContainerStatuses(); | |||
String image; | |||
String containerStatusReady = null; | |||
String containerStatusWaiting = null; | |||
String containerStatusWaitingReason = null; | |||
String containerStatusWaitingMessage = null; | |||
String containerStatusTerminated = null; | |||
String containerStatusTerminatedReason = null; | |||
String containerStatusTerminatedMessage = null; | |||
for( ContainerStatus containerStatus:containerStatuses ) | |||
{ | |||
image = containerStatus.getImage(); | |||
if (containerStatus.getReady()) { | |||
containerStatusReady=StringUtils.join(containerStatusReady,image,"_","true","_"); | |||
} | |||
else { | |||
containerStatusReady=StringUtils.join(containerStatusReady,image,"_","false","_"); | |||
} | |||
ContainerState containerState=containerStatus.getState(); | |||
ContainerStateWaiting waiting = containerState.getWaiting(); | |||
//containerStatus为waiting状态 | |||
if (waiting!=null) { | |||
containerStatusWaiting = StringUtils.join(containerStatusWaiting,image,"_","true","_"); | |||
containerStatusWaitingReason = StringUtils.join(containerStatusWaitingReason,image,"_",waiting.getReason(),"_"); | |||
containerStatusWaitingMessage = StringUtils.join(containerStatusWaitingMessage,image,"_",waiting.getMessage(),"_"); | |||
logger.info( "创建pod {}失败,nodeIp: {},image: {},ContainerState: waiting,ContainerStateReason: {},ContainerStateMessage: {}",podName,nodeIp,image,waiting.getReason(),waiting.getMessage() ); | |||
} | |||
else { | |||
containerStatusWaiting = StringUtils.join(containerStatusWaiting,image,"_","null","_"); | |||
containerStatusWaitingReason = StringUtils.join(containerStatusWaitingReason,image,"_","null","_"); | |||
containerStatusWaitingMessage = StringUtils.join(containerStatusWaitingMessage,image,"_","null","_"); | |||
} | |||
ContainerStateTerminated terminated=containerState.getTerminated(); | |||
//containerStatus为terminated状态 | |||
if (terminated!=null) { | |||
containerStatusTerminated = StringUtils.join(containerStatusTerminated,image,"_","true","_"); | |||
containerStatusTerminatedReason = StringUtils.join(containerStatusTerminatedReason,image,"_",terminated.getReason(),"_"); | |||
containerStatusTerminatedMessage = StringUtils.join(containerStatusTerminatedMessage,image,"_",terminated.getMessage(),"_"); | |||
logger.info( "创建pod {}失败,nodeIp: {},image: {},ContainerState: terminated,ContainerStateReason: {},ContainerStateMessage: {}",podName,nodeIp,image,terminated.getReason(),terminated.getMessage() ); | |||
} | |||
else { | |||
containerStatusTerminated = StringUtils.join(containerStatusTerminated,image,"_","null","_"); | |||
containerStatusTerminatedReason = StringUtils.join(containerStatusTerminatedReason,image,"_","null","_"); | |||
containerStatusTerminatedMessage = StringUtils.join(containerStatusTerminatedMessage,image,"_","null","_"); | |||
} | |||
} | |||
errorPodInfo.setContainerStatusReady(containerStatusReady); | |||
errorPodInfo.setContainerStatusWaiting(containerStatusWaiting); | |||
errorPodInfo.setContainerStatusWaitingReason(containerStatusWaitingReason); | |||
errorPodInfo.setContainerStatusWaitingMessage(containerStatusWaitingMessage); | |||
errorPodInfo.setContainerStatusTerminated(containerStatusTerminated); | |||
errorPodInfo.setContainerStatusTerminatedReason(containerStatusTerminatedReason); | |||
errorPodInfo.setContainerStatusTerminatedMessage(containerStatusTerminatedMessage); | |||
} | |||
/** | |||
* @author huqifeng | |||
* @Time 2021-8-9 | |||
* @Description 当pod创建超时时,捕获Container的状态,以及处于该状态的原因 | |||
* @param podStatus pod状况 | |||
* @param podName pod名称 | |||
* @param nodeIp 节点ip | |||
*/ | |||
public static void createErrorPodInfo(ErrorPodInfo errorPodInfo, PodStatus podStatus, String podName, String nodeIp, String tpiID, String buildID, boolean imageExist) | |||
{ | |||
errorPodInfo.setErrorTime(LocalDateTime.now()); | |||
errorPodInfo.setPodName(podName); | |||
errorPodInfo.setNodeIp(nodeIp); | |||
errorPodInfo.setTpiID(tpiID); | |||
errorPodInfo.setBuildID(buildID); | |||
if (imageExist) { | |||
errorPodInfo.setImageExist("true"); | |||
} | |||
else { | |||
errorPodInfo.setImageExist("false"); | |||
} | |||
K8sUtils.catchPodPhaseReasonMessage(errorPodInfo, podStatus, podName, nodeIp); | |||
K8sUtils.catchPodConditionReasonMessage(errorPodInfo, podStatus, podName, nodeIp); | |||
K8sUtils.catchContainerStateReasonMessage(errorPodInfo, podStatus, podName, nodeIp); | |||
} | |||
/** | |||
* 获取环境变量 | |||
*/ | |||
public static String getEnv(Pod pod, String key){ | |||
String value = ""; | |||
if (!CollectionUtils.isEmpty(pod.getSpec().getContainers().get(0).getEnv())) { | |||
value = pod.getSpec().getContainers().get(0).getEnv().stream().filter(envVar -> envVar.getName().equals(key)).findFirst().get().getValue(); | |||
} | |||
return value; | |||
} | |||
} |
@@ -0,0 +1,22 @@ | |||
package com.imitate.common.shiro.config; | |||
import org.springframework.boot.web.servlet.FilterRegistrationBean; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.web.filter.DelegatingFilterProxy; | |||
/** | |||
* Filter配置 | |||
* @author yanchao | |||
*/ | |||
public class FilterConfig { | |||
@Bean | |||
public FilterRegistrationBean shiroFilterRegistration(){ | |||
FilterRegistrationBean registrationBean = new FilterRegistrationBean(); | |||
registrationBean.setFilter(new DelegatingFilterProxy("shiroFilter")); | |||
registrationBean.addInitParameter("targetFilteerLifecycle","true"); | |||
registrationBean.setEnabled(true); | |||
registrationBean.setOrder(Integer.MAX_VALUE - 1); | |||
registrationBean.addUrlPatterns("/*"); | |||
return registrationBean; | |||
} | |||
} |
@@ -0,0 +1,108 @@ | |||
package com.imitate.common.shiro.config; | |||
import com.google.gson.Gson; | |||
import com.imitate.common.enums.ErrorCodeEnum; | |||
import com.imitate.common.shiro.realm.OAuth2Token; | |||
import com.imitate.common.util.R; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.apache.shiro.authc.AuthenticationException; | |||
import org.apache.shiro.authc.AuthenticationToken; | |||
import org.apache.shiro.web.filter.authc.AuthenticatingFilter; | |||
import org.springframework.web.bind.annotation.RequestMethod; | |||
import javax.servlet.ServletRequest; | |||
import javax.servlet.ServletResponse; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import java.io.IOException; | |||
/** | |||
* oauth2过滤器 | |||
* @author yanchao | |||
*/ | |||
public class OAuth2Filter extends AuthenticatingFilter { | |||
/** | |||
* 取token | |||
* @author yanchao | |||
*/ | |||
@Override | |||
protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { | |||
//获取请求token | |||
String token = getRequestToken((HttpServletRequest) servletRequest); | |||
if(StringUtils.isBlank(token)){ | |||
return null; | |||
} | |||
return new OAuth2Token(token); | |||
} | |||
@Override | |||
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { | |||
if(((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())){ | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* 认证之前调用的方法 | |||
* @author yanchao | |||
* @return boolean | |||
*/ | |||
@Override | |||
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { | |||
//获取请求中的token,如果不存在,直接返回401 | |||
String token = getRequestToken((HttpServletRequest)request); | |||
if(StringUtils.isBlank(token)){ | |||
HttpServletResponse httpResponse = (HttpServletResponse) response; | |||
httpResponse.setHeader("Access-Control-Allow-Credentials","true"); | |||
httpResponse.setHeader("Access-Control-Allow-Origin", "*"); | |||
httpResponse.setContentType("application/json;charset=UTF-8"); | |||
R r = R.error(ErrorCodeEnum.NO_AUTH.getValue(),ErrorCodeEnum.NO_AUTH.getDescription()); | |||
String json = new Gson().toJson(r); | |||
httpResponse.getWriter().print(json); | |||
return false; | |||
} | |||
return executeLogin(request,response); | |||
} | |||
/** | |||
* 登陆认证失败调用的方法 | |||
* @author yanchao | |||
* @return boolean | |||
*/ | |||
@Override | |||
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { | |||
HttpServletResponse httpResponse = (HttpServletResponse) response; | |||
httpResponse.setContentType("application/json;charset=utf-8"); | |||
httpResponse.setHeader("Access-Control-Allow-Credentials","true"); | |||
httpResponse.setHeader("Access-Control-Allow-Origin", "*"); | |||
try { | |||
//处理登录失败的异常 | |||
R r = R.error(ErrorCodeEnum.NO_AUTH.getValue(),ErrorCodeEnum.NO_AUTH.getDescription()); | |||
String json = new Gson().toJson(r); | |||
httpResponse.getWriter().print(json); | |||
}catch (IOException e1){ | |||
} | |||
return false; | |||
} | |||
/** | |||
* 获取请求的token | |||
* @author yanchao | |||
*/ | |||
private String getRequestToken(HttpServletRequest httpRequest){ | |||
//从header中获取token | |||
String token = httpRequest.getHeader("token"); | |||
//如果header中不存在token,则从参数中获取 | |||
if(StringUtils.isBlank(token)){ | |||
token = httpRequest.getParameter("token"); | |||
} | |||
return token; | |||
} | |||
} |
@@ -0,0 +1,106 @@ | |||
package com.imitate.common.shiro.config; | |||
import com.imitate.common.shiro.realm.OAuth2Realm; | |||
import org.apache.shiro.mgt.SecurityManager; | |||
import org.apache.shiro.session.mgt.SessionManager; | |||
import org.apache.shiro.spring.LifecycleBeanPostProcessor; | |||
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; | |||
import org.apache.shiro.spring.web.ShiroFilterFactoryBean; | |||
import org.apache.shiro.web.mgt.DefaultWebSecurityManager; | |||
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; | |||
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import javax.servlet.Filter; | |||
import java.util.HashMap; | |||
import java.util.LinkedHashMap; | |||
import java.util.Map; | |||
/** | |||
* Shiro配置类 | |||
* @author yanchao | |||
*/ | |||
@Configuration | |||
public class ShiroConfig { | |||
/** | |||
* 会话管理器 | |||
* @author yanchao | |||
*/ | |||
@Bean("sessionManager") | |||
public SessionManager sessionManager(){ | |||
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); | |||
sessionManager.setSessionValidationSchedulerEnabled(true); | |||
sessionManager.setSessionIdCookieEnabled(true); | |||
return sessionManager; | |||
} | |||
/** | |||
* 安全管理器 | |||
* @author yanchao | |||
*/ | |||
@Bean("securityManager") | |||
public SecurityManager securityManager(OAuth2Realm oAuth2Realm, SessionManager sessionManager){ | |||
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); | |||
securityManager.setRealm(oAuth2Realm); | |||
securityManager.setSessionManager(sessionManager); | |||
return securityManager; | |||
} | |||
/** | |||
* shiro过滤器工厂 | |||
* @author yanchao | |||
*/ | |||
@Bean("shiroFilter") | |||
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){ | |||
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); | |||
shiroFilter.setSecurityManager(securityManager); | |||
//oauth | |||
Map<String,Filter> filters = new HashMap<>(); | |||
filters.put("oauth2",new OAuth2Filter()); | |||
shiroFilter.setFilters(filters); | |||
Map<String, String> filterMap = new LinkedHashMap<>(); | |||
//filterMap.put("/**","oauth2"); | |||
filterMap.put("/**","anon"); | |||
shiroFilter.setFilterChainDefinitionMap(filterMap); | |||
return shiroFilter; | |||
} | |||
/** | |||
* 启用Shiro注解,保证实现了shiro内部lifecycle函数的bean执行 | |||
* @author yanchao | |||
*/ | |||
@Bean("lifecycleBeanPostProcessor") | |||
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){ | |||
return new LifecycleBeanPostProcessor(); | |||
} | |||
/** | |||
* 启用shiro注解 | |||
* @author yanchao | |||
*/ | |||
@Bean | |||
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){ | |||
DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator(); | |||
proxyCreator.setProxyTargetClass(true); | |||
return proxyCreator; | |||
} | |||
@Bean | |||
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){ | |||
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); | |||
advisor.setSecurityManager(securityManager); | |||
return advisor; | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.imitate.common.shiro.mapper; | |||
import com.imitate.common.shiro.pojo.SysMenu; | |||
import com.imitate.common.util.BaseMapper; | |||
import org.apache.ibatis.annotations.Mapper; | |||
import org.springframework.stereotype.Repository; | |||
/** | |||
* 权限菜单 | |||
* @author yanchao | |||
*/ | |||
@Repository | |||
@Mapper | |||
public interface SysMenuMapper extends BaseMapper<SysMenu> { | |||
} |
@@ -0,0 +1,31 @@ | |||
package com.imitate.common.shiro.mapper; | |||
import com.imitate.common.shiro.pojo.SysRole; | |||
import com.imitate.common.util.BaseMapper; | |||
import org.apache.ibatis.annotations.Mapper; | |||
import org.apache.ibatis.annotations.Param; | |||
import org.springframework.stereotype.Repository; | |||
import java.util.List; | |||
/** | |||
* 角色管理 | |||
* @author yanchao | |||
*/ | |||
@Mapper | |||
@Repository | |||
public interface SysRoleMapper extends BaseMapper<SysRole> { | |||
/** | |||
* 查询用户创建的角色ID列表 | |||
*/ | |||
List<Long> queryRoleIdList(Long createUserId); | |||
/** | |||
* 获取角色详情 | |||
*/ | |||
SysRole get(@Param(value = "id") Long id); | |||
} |
@@ -0,0 +1,29 @@ | |||
package com.imitate.common.shiro.mapper; | |||
import com.imitate.common.shiro.pojo.SysRoleMenu; | |||
import com.imitate.common.util.BaseMapper; | |||
import org.apache.ibatis.annotations.Mapper; | |||
import org.springframework.stereotype.Repository; | |||
import java.util.List; | |||
/** | |||
* 角色与菜单对应关系 | |||
* @author yanchao | |||
*/ | |||
@Mapper | |||
@Repository | |||
public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenu> { | |||
/** | |||
* 根据角色ID,获取菜单ID列表 | |||
*/ | |||
List<Long> queryMenuIdList(Long roleId); | |||
/** | |||
* 根据角色ID数组,批量删除 | |||
*/ | |||
int deleteBatch(Long[] roleIds); | |||
} |
@@ -0,0 +1,14 @@ | |||
package com.imitate.common.shiro.mapper; | |||
import com.imitate.common.shiro.pojo.SysToken; | |||
import com.imitate.common.util.BaseMapper; | |||
import org.springframework.stereotype.Repository; | |||
/** | |||
* 登录会话 | |||
* @author yanchao | |||
*/ | |||
@Repository | |||
public interface SysTokenMapper extends BaseMapper<SysToken> { | |||
} |
@@ -0,0 +1,23 @@ | |||
package com.imitate.common.shiro.mapper; | |||
import com.imitate.common.shiro.pojo.SysUser; | |||
import com.imitate.common.util.BaseMapper; | |||
import org.springframework.stereotype.Repository; | |||
import java.util.List; | |||
/** | |||
* 后台用户mapper | |||
* @author yanchao | |||
*/ | |||
@Repository | |||
public interface SysUserMapper extends BaseMapper<SysUser> { | |||
/** | |||
* 查询用户的所有权限 | |||
*/ | |||
List<String> queryAllPerms(Long id); | |||
} |