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