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