@@ -1,6 +1,8 @@ | |||||
# ---> Java | # ---> Java | ||||
# Compiled class file | # Compiled class file | ||||
*.class | *.class | ||||
./.idea/ | |||||
.idea/ | |||||
# Log file | # Log file | ||||
*.log | *.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); | |||||
} |