[BAEL-5014] Kubernetes Admission Controller (#11044)
* [BAEL-4849] Article code * [BAEL-4968] Article code * [BAEL-4968] Article code * [BAEL-4968] Article code
This commit is contained in:
parent
3f18d3ae53
commit
d2035e86af
11
kubernetes/k8s-admission-controller/Dockerfile
Normal file
11
kubernetes/k8s-admission-controller/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM adoptopenjdk:11-jre-hotspot as builder
|
||||
ARG JAR_FILE=target/*.jar
|
||||
COPY ${JAR_FILE} application.jar
|
||||
RUN java -Djarmode=layertools -jar application.jar extract
|
||||
|
||||
FROM adoptopenjdk:11-jre-hotspot
|
||||
COPY --from=builder dependencies/ ./
|
||||
COPY --from=builder snapshot-dependencies/ ./
|
||||
COPY --from=builder spring-boot-loader/ ./
|
||||
COPY --from=builder application/ ./
|
||||
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
|
86
kubernetes/k8s-admission-controller/pom.xml
Normal file
86
kubernetes/k8s-admission-controller/pom.xml
Normal file
@ -0,0 +1,86 @@
|
||||
<?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>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>./../../parent-boot-2</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>k8s-admission-controller</artifactId>
|
||||
<name>k8s-admission-controller</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
<mainClass>com.baeldung.kubernetes.admission.Application</mainClass>
|
||||
<layers>
|
||||
<enabled>true</enabled>
|
||||
</layers>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -0,0 +1,17 @@
|
||||
package com.baeldung.kubernetes.admission;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
||||
import com.baeldung.kubernetes.admission.config.AdmissionControllerProperties;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableConfigurationProperties(AdmissionControllerProperties.class)
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.baeldung.kubernetes.admission.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author lighthouse.psevestre
|
||||
*
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "admission-controller")
|
||||
@Data
|
||||
public class AdmissionControllerProperties {
|
||||
|
||||
private boolean disabled;
|
||||
private String annotation = "com.baeldung/wait-for-it";
|
||||
private String waitForItImage = "willwill/wait-for-it";
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.baeldung.kubernetes.admission.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.baeldung.kubernetes.admission.dto.AdmissionReviewResponse;
|
||||
import com.baeldung.kubernetes.admission.service.AdmissionService;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class AdmissionReviewController {
|
||||
|
||||
private final AdmissionService admissionService;
|
||||
|
||||
@PostMapping(path = "/mutate")
|
||||
public Mono<AdmissionReviewResponse> processAdmissionReviewRequest(@RequestBody Mono<ObjectNode> request) {
|
||||
return request.map((body) -> admissionService.processAdmission(body));
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.baeldung.kubernetes.admission.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Result sent to the API server after reviewing and, possibly
|
||||
* modifying the incoming request
|
||||
*/
|
||||
@Builder
|
||||
@Data
|
||||
public class AdmissionReviewData {
|
||||
|
||||
final String uid;
|
||||
final boolean allowed;
|
||||
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
final String patchType;
|
||||
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
final String patch;
|
||||
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
final AdmissionStatus status;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.baeldung.kubernetes.admission.dto;
|
||||
|
||||
/**
|
||||
* Exceção utilizada para reportar erros de validação no manifesto recebido para admissão
|
||||
* @author lighthouse.psevestre
|
||||
*
|
||||
*/
|
||||
public class AdmissionReviewException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final int code;
|
||||
|
||||
public AdmissionReviewException(int code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public AdmissionReviewException(String message) {
|
||||
super(message);
|
||||
this.code = 400;
|
||||
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.baeldung.kubernetes.admission.dto;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Builder.Default;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Response "envelope" sent back to the API Server
|
||||
*/
|
||||
@Builder
|
||||
@Data
|
||||
public class AdmissionReviewResponse {
|
||||
|
||||
@Default
|
||||
final String apiVersion = "admission.k8s.io/v1";
|
||||
|
||||
@Default
|
||||
final String kind = "AdmissionReview";
|
||||
|
||||
final AdmissionReviewData response;
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.baeldung.kubernetes.admission.dto;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Builder
|
||||
@Data
|
||||
public class AdmissionStatus {
|
||||
|
||||
int code;
|
||||
String message;
|
||||
|
||||
}
|
@ -0,0 +1,218 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.baeldung.kubernetes.admission.service;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.baeldung.kubernetes.admission.config.AdmissionControllerProperties;
|
||||
import com.baeldung.kubernetes.admission.dto.AdmissionReviewData;
|
||||
import com.baeldung.kubernetes.admission.dto.AdmissionReviewException;
|
||||
import com.baeldung.kubernetes.admission.dto.AdmissionReviewResponse;
|
||||
import com.baeldung.kubernetes.admission.dto.AdmissionStatus;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Process an incoming admission request and add the "wait-for-it" init container
|
||||
* if there's an appropriate annotation
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class AdmissionService {
|
||||
|
||||
private final AdmissionControllerProperties admissionControllerProperties;
|
||||
private final ObjectMapper om;
|
||||
|
||||
public AdmissionReviewResponse processAdmission(ObjectNode body) {
|
||||
|
||||
String uid = body.path("request")
|
||||
.required("uid")
|
||||
.asText();
|
||||
|
||||
log.info("[I42] processAdmission: uid={}",uid);
|
||||
if ( log.isDebugEnabled()) {
|
||||
log.debug("processAdmission: body={}", body.toPrettyString());
|
||||
}
|
||||
|
||||
// Get request annotations
|
||||
JsonNode annotations = body.path("request")
|
||||
.path("object")
|
||||
.path("metadata")
|
||||
.path("annotations");
|
||||
log.info("processAdmision: annotations={}", annotations.toString());
|
||||
|
||||
AdmissionReviewData data;
|
||||
try {
|
||||
if (admissionControllerProperties.isDisabled()) {
|
||||
log.info("[I58] 'disabled' option in effect. No changes to current request will be made");
|
||||
data = createSimpleAllowedReview(body);
|
||||
} else if (annotations.isMissingNode()) {
|
||||
log.info("[I68] No annotations found in request. No changes will be made");
|
||||
data = createSimpleAllowedReview(body);
|
||||
} else {
|
||||
data = processAnnotations(body, annotations);
|
||||
}
|
||||
|
||||
log.info("[I65] Review result: isAllowed=" + data.isAllowed());
|
||||
log.info("[I64] AdmissionReviewData= {}", data);
|
||||
|
||||
return AdmissionReviewResponse.builder()
|
||||
.apiVersion(body.required("apiVersion").asText())
|
||||
.kind(body.required("kind").asText())
|
||||
.response(data)
|
||||
.build();
|
||||
} catch (AdmissionReviewException ex) {
|
||||
log.error("[E72] Error processing AdmissionRequest: code={}, message={}", ex.getCode(), ex.getMessage());
|
||||
data = createRejectedAdmissionReview(body, ex.getCode(), ex.getMessage());
|
||||
|
||||
return AdmissionReviewResponse.builder()
|
||||
.apiVersion(body.required("apiVersion").asText())
|
||||
.kind(body.required("kind").asText())
|
||||
.response(data)
|
||||
.build();
|
||||
} catch (Exception ex) {
|
||||
log.error("[E72] Unable to process AdmissionRequest: " + ex.getMessage(), ex);
|
||||
data = createRejectedAdmissionReview(body, 500, ex.getMessage());
|
||||
return AdmissionReviewResponse.builder()
|
||||
.apiVersion(body.required("apiVersion").asText())
|
||||
.kind(body.required("kind").asText())
|
||||
.response(data)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param body
|
||||
* @return
|
||||
*/
|
||||
protected AdmissionReviewData createSimpleAllowedReview(ObjectNode body) {
|
||||
AdmissionReviewData data;
|
||||
String requestId = body.path("request")
|
||||
.required("uid")
|
||||
.asText();
|
||||
|
||||
data = AdmissionReviewData.builder()
|
||||
.allowed(true)
|
||||
.uid(requestId)
|
||||
.build();
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param body
|
||||
* @return
|
||||
*/
|
||||
protected AdmissionReviewData createRejectedAdmissionReview(ObjectNode body, int code, String message) {
|
||||
AdmissionReviewData data;
|
||||
String requestId = body.path("request")
|
||||
.required("uid")
|
||||
.asText();
|
||||
|
||||
AdmissionStatus status = AdmissionStatus.builder()
|
||||
.code(code)
|
||||
.message(message)
|
||||
.build();
|
||||
|
||||
data = AdmissionReviewData.builder()
|
||||
.allowed(false)
|
||||
.uid(requestId)
|
||||
.status(status)
|
||||
.build();
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Processa anotações incluídas no deployment
|
||||
* @param annotations
|
||||
* @return
|
||||
*/
|
||||
protected AdmissionReviewData processAnnotations(ObjectNode body, JsonNode annotations) {
|
||||
|
||||
if (annotations.path(admissionControllerProperties.getAnnotation())
|
||||
.isMissingNode()) {
|
||||
log.info("[I78] processAnnotations: Annotation {} not found in deployment deployment.", admissionControllerProperties.getAnnotation());
|
||||
return createSimpleAllowedReview(body);
|
||||
}
|
||||
else {
|
||||
log.info("[I163] annotation found: {}", annotations.path(admissionControllerProperties.getAnnotation()));
|
||||
}
|
||||
|
||||
// Get wait-for-it arguments from the annotation
|
||||
String waitForArgs = annotations.path(admissionControllerProperties.getAnnotation())
|
||||
.asText();
|
||||
|
||||
log.info("[I169] waitForArgs={}", waitForArgs);
|
||||
// Create a PATCH object
|
||||
String patch = injectInitContainer(body, waitForArgs);
|
||||
|
||||
return AdmissionReviewData.builder()
|
||||
.allowed(true)
|
||||
.uid(body.path("request")
|
||||
.required("uid")
|
||||
.asText())
|
||||
.patch(Base64.getEncoder()
|
||||
.encodeToString(patch.getBytes()))
|
||||
.patchType("JSONPatch")
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the JSONPatch to be included in the admission response
|
||||
* @param body
|
||||
* @param waitForArgs
|
||||
* @return JSONPatch string
|
||||
*/
|
||||
protected String injectInitContainer(ObjectNode body, String waitForArgs) {
|
||||
|
||||
// Recover original init containers from the request
|
||||
JsonNode originalSpec = body.path("request")
|
||||
.path("object")
|
||||
.path("spec")
|
||||
.path("template")
|
||||
.path("spec")
|
||||
.require();
|
||||
|
||||
JsonNode maybeInitContainers = originalSpec.path("initContainers");
|
||||
ArrayNode initContainers =
|
||||
maybeInitContainers.isMissingNode()?
|
||||
om.createArrayNode():(ArrayNode) maybeInitContainers;
|
||||
|
||||
// Create the patch array
|
||||
ArrayNode patchArray = om.createArrayNode();
|
||||
ObjectNode addNode = patchArray.addObject();
|
||||
|
||||
addNode.put("op", "add");
|
||||
addNode.put("path", "/spec/template/spec/initContainers");
|
||||
ArrayNode values = addNode.putArray("value");
|
||||
|
||||
// Preserve original init containers
|
||||
values.addAll(initContainers);
|
||||
|
||||
// append the "wait-for-it" container
|
||||
ObjectNode wfi = values.addObject();
|
||||
wfi.put("name", "wait-for-it-" + UUID.randomUUID()); // Create an unique name, JIC
|
||||
wfi.put("image", admissionControllerProperties.getWaitForItImage());
|
||||
|
||||
ArrayNode args = wfi.putArray("args");
|
||||
for (String s : waitForArgs.split("\\s")) {
|
||||
args.add(s);
|
||||
}
|
||||
|
||||
return patchArray.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package com.baeldung.kubernetes.admission.service;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Base64;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import com.baeldung.kubernetes.admission.config.AdmissionControllerProperties;
|
||||
import com.baeldung.kubernetes.admission.dto.AdmissionReviewResponse;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
@SpringBootTest
|
||||
@ActiveProfiles("test")
|
||||
@EnableConfigurationProperties(AdmissionControllerProperties.class)
|
||||
class AdmissionServiceUnitTest {
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper mapper;
|
||||
|
||||
@Autowired
|
||||
private AdmissionService admissionService;
|
||||
|
||||
@Test
|
||||
void whenAnnotationPresent_thenAddContainer() throws Exception {
|
||||
|
||||
InputStream is = this.getClass()
|
||||
.getClassLoader()
|
||||
.getResourceAsStream("test1.json");
|
||||
JsonNode body = mapper.readTree(is);
|
||||
AdmissionReviewResponse response = admissionService.processAdmission((ObjectNode) body);
|
||||
assertNotNull(response);
|
||||
assertNotNull(response.getResponse());
|
||||
assertNotNull(response.getResponse());
|
||||
assertTrue(response.getResponse()
|
||||
.isAllowed());
|
||||
|
||||
String jsonResponse = mapper.writeValueAsString(response);
|
||||
System.out.println(jsonResponse);
|
||||
|
||||
// Decode Patch data
|
||||
String b64patch = response.getResponse()
|
||||
.getPatch();
|
||||
assertNotNull(b64patch);
|
||||
byte[] patch = Base64.getDecoder()
|
||||
.decode(b64patch);
|
||||
|
||||
JsonNode root = mapper.reader()
|
||||
.readTree(new ByteArrayInputStream(patch));
|
||||
assertTrue(root instanceof ArrayNode);
|
||||
|
||||
assertEquals(1, ((ArrayNode) root).size());
|
||||
|
||||
}
|
||||
|
||||
}
|
23
kubernetes/k8s-admission-controller/src/test/k8s/nginx.yaml
Normal file
23
kubernetes/k8s-admission-controller/src/test/k8s/nginx.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: frontend
|
||||
labels:
|
||||
app: nginx
|
||||
annotations:
|
||||
com.baeldung/wait-for-it: "www.google.com:80"
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.14.2
|
||||
ports:
|
||||
- containerPort: 80
|
@ -0,0 +1,84 @@
|
||||
{
|
||||
"kind": "AdmissionReview",
|
||||
"apiVersion": "admission.k8s.io/v1",
|
||||
"request": {
|
||||
"uid": "c46a6607-129d-425b-af2f-c6f87a0756da",
|
||||
"kind": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"kind": "Deployment"
|
||||
},
|
||||
"resource": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"resource": "deployments"
|
||||
},
|
||||
"requestKind": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"kind": "Deployment"
|
||||
},
|
||||
"requestResource": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"resource": "deployments"
|
||||
},
|
||||
"name": "test-deployment",
|
||||
"namespace": "test-namespace",
|
||||
"operation": "CREATE",
|
||||
"object": {
|
||||
"kind": "Deployment",
|
||||
"apiVersion": "apps/v1",
|
||||
"metadata": {
|
||||
"name": "test-deployment",
|
||||
"namespace": "test-namespace",
|
||||
"annotations": {
|
||||
"com.baeldung/wait-for-it": "www.google.com:80"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "test-app"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"name": "test-app",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "test-app"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "app",
|
||||
"image": "test-app-image:latest",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"containerPort": 8080,
|
||||
"protocol": "TCP"
|
||||
}
|
||||
],
|
||||
"resources": {},
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "Always"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"status": {}
|
||||
},
|
||||
"oldObject": null,
|
||||
"dryRun": false,
|
||||
"options": {
|
||||
"kind": "CreateOptions",
|
||||
"apiVersion": "meta.k8s.io/v1"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
{
|
||||
"kind": "AdmissionReview",
|
||||
"apiVersion": "admission.k8s.io/v1",
|
||||
"request": {
|
||||
"uid": "c46a6607-129d-425b-af2f-c6f87a0756da",
|
||||
"kind": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"kind": "Deployment"
|
||||
},
|
||||
"resource": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"resource": "deployments"
|
||||
},
|
||||
"requestKind": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"kind": "Deployment"
|
||||
},
|
||||
"requestResource": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"resource": "deployments"
|
||||
},
|
||||
"name": "test-deployment",
|
||||
"namespace": "test-namespace",
|
||||
"operation": "CREATE",
|
||||
"object": {
|
||||
"kind": "Deployment",
|
||||
"apiVersion": "apps/v1",
|
||||
"metadata": {
|
||||
"name": "test-deployment",
|
||||
"namespace": "test-namespace",
|
||||
"annotations": {
|
||||
"com.baeldung/wait-for-it": "www.google.com:80"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "test-app"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"name": "test-app",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "test-app"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"initContainers": [],
|
||||
"containers": [
|
||||
{
|
||||
"name": "app",
|
||||
"image": "test-app-image:latest",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"containerPort": 8080,
|
||||
"protocol": "TCP"
|
||||
}
|
||||
],
|
||||
"resources": {},
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "Always"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"status": {}
|
||||
},
|
||||
"oldObject": null,
|
||||
"dryRun": false,
|
||||
"options": {
|
||||
"kind": "CreateOptions",
|
||||
"apiVersion": "meta.k8s.io/v1"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
{
|
||||
"kind": "AdmissionReview",
|
||||
"apiVersion": "admission.k8s.io/v1",
|
||||
"request": {
|
||||
"uid": "c46a6607-129d-425b-af2f-c6f87a0756da",
|
||||
"kind": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"kind": "Deployment"
|
||||
},
|
||||
"resource": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"resource": "deployments"
|
||||
},
|
||||
"requestKind": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"kind": "Deployment"
|
||||
},
|
||||
"requestResource": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"resource": "deployments"
|
||||
},
|
||||
"name": "test-deployment",
|
||||
"namespace": "test-namespace",
|
||||
"operation": "CREATE",
|
||||
"object": {
|
||||
"kind": "Deployment",
|
||||
"apiVersion": "apps/v1",
|
||||
"metadata": {
|
||||
"name": "test-deployment",
|
||||
"namespace": "test-namespace",
|
||||
"annotations": {
|
||||
"com.baeldung/wait-for-it": "www.google.com:80"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "test-app"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"name": "test-app",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "test-app"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"initContainers": [
|
||||
{
|
||||
"name": "init1",
|
||||
"image": "test-app-image:latest",
|
||||
"resources": {},
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "Always"
|
||||
}
|
||||
],
|
||||
"containers": [
|
||||
{
|
||||
"name": "app",
|
||||
"image": "test-app-image:latest",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
"containerPort": 8080,
|
||||
"protocol": "TCP"
|
||||
}
|
||||
],
|
||||
"resources": {},
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"imagePullPolicy": "Always"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"status": {}
|
||||
},
|
||||
"oldObject": null,
|
||||
"dryRun": false,
|
||||
"options": {
|
||||
"kind": "CreateOptions",
|
||||
"apiVersion": "meta.k8s.io/v1"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
{
|
||||
"kind": "AdmissionReview",
|
||||
"apiVersion": "admission.k8s.io/v1",
|
||||
"request": {
|
||||
"uid": "26beb334-739a-48d2-b04d-25f6e5e7c106",
|
||||
"kind": {
|
||||
"group": "apps",
|
||||
"version": "v1",
|
||||
"kind": "Deployment"
|
||||
},
|
||||
"resource": {},
|
||||
"f:type": {}
|
||||
},
|
||||
"f:template": {
|
||||
"f:metadata": {
|
||||
"f:labels": {
|
||||
".": {},
|
||||
"f:app": {}
|
||||
}
|
||||
},
|
||||
"f:spec": {
|
||||
"f:containers": {
|
||||
"k:{\"name\":\"nginx\"}": {
|
||||
".": {},
|
||||
"f:image": {},
|
||||
"f:imagePullPolicy": {},
|
||||
"f:name": {},
|
||||
"f:ports": {
|
||||
".": {},
|
||||
"k:{\"containerPort\":80,\"protocol\":\"TCP\"}": {
|
||||
".": {},
|
||||
"f:containerPort": {},
|
||||
"f:protocol": {}
|
||||
}
|
||||
},
|
||||
"f:resources": {},
|
||||
"f:terminationMessagePath": {},
|
||||
"f:terminationMessagePolicy": {}
|
||||
}
|
||||
},
|
||||
"f:dnsPolicy": {},
|
||||
"f:restartPolicy": {},
|
||||
"f:schedulerName": {},
|
||||
"f:securityContext": {},
|
||||
"f:terminationGracePeriodSeconds": {}
|
||||
}
|
||||
}
|
||||
}
|
3
kubernetes/k8s-admission-controller/src/test/terraform/.gitignore
vendored
Normal file
3
kubernetes/k8s-admission-controller/src/test/terraform/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.terraform
|
||||
terraform.tfstate
|
||||
terraform.tfstate.backup
|
57
kubernetes/k8s-admission-controller/src/test/terraform/.terraform.lock.hcl
generated
Normal file
57
kubernetes/k8s-admission-controller/src/test/terraform/.terraform.lock.hcl
generated
Normal file
@ -0,0 +1,57 @@
|
||||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/hashicorp/kubernetes" {
|
||||
version = "2.3.2"
|
||||
constraints = "2.3.2"
|
||||
hashes = [
|
||||
"h1:D8HWX3vouTPI3Jicq43xOQyoYWtSsVua92cBVrJ3ZMs=",
|
||||
"zh:10f71c170be13538374a4b9553fcb3d98a6036bcd1ca5901877773116c3f828e",
|
||||
"zh:11d2230e531b7480317e988207a73cb67b332f225b0892304983b19b6014ebe0",
|
||||
"zh:3317387a9a6cc27fd7536b8f3cad4b8a9285e9461f125c5a15d192cef3281856",
|
||||
"zh:458a9858362900fbe97e00432ae8a5bef212a4dacf97a57ede7534c164730da4",
|
||||
"zh:50ea297007d9fe53e5411577f87a4b13f3877ce732089b42f938430e6aadff0d",
|
||||
"zh:56705c959e4cbea3b115782d04c62c68ac75128c5c44ee7aa4043df253ffbfe3",
|
||||
"zh:7eb3722f7f036e224824470c3e0d941f1f268fcd5fa2f8203e0eee425d0e1484",
|
||||
"zh:9f408a6df4d74089e6ce18f9206b06b8107ddb57e2bc9b958a6b7dc352c62980",
|
||||
"zh:aadd25ccc3021040808feb2645779962f638766eb583f586806e59f24dde81bb",
|
||||
"zh:b101c3456e4309b09aab129b0118561178c92cb4be5d96dec553189c3084dca1",
|
||||
"zh:ec08478573b4953764099fbfd670fae81dc24b60e467fb3b023e6fab50b70a9e",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.terraform.io/hashicorp/null" {
|
||||
version = "3.1.0"
|
||||
hashes = [
|
||||
"h1:SFT7X3zY18CLWjoH2GfQyapxsRv6GDKsy9cF1aRwncc=",
|
||||
"zh:02a1675fd8de126a00460942aaae242e65ca3380b5bb192e8773ef3da9073fd2",
|
||||
"zh:53e30545ff8926a8e30ad30648991ca8b93b6fa496272cd23b26763c8ee84515",
|
||||
"zh:5f9200bf708913621d0f6514179d89700e9aa3097c77dac730e8ba6e5901d521",
|
||||
"zh:9ebf4d9704faba06b3ec7242c773c0fbfe12d62db7d00356d4f55385fc69bfb2",
|
||||
"zh:a6576c81adc70326e4e1c999c04ad9ca37113a6e925aefab4765e5a5198efa7e",
|
||||
"zh:a8a42d13346347aff6c63a37cda9b2c6aa5cc384a55b2fe6d6adfa390e609c53",
|
||||
"zh:c797744d08a5307d50210e0454f91ca4d1c7621c68740441cf4579390452321d",
|
||||
"zh:cecb6a304046df34c11229f20a80b24b1603960b794d68361a67c5efe58e62b8",
|
||||
"zh:e1371aa1e502000d9974cfaff5be4cfa02f47b17400005a16f14d2ef30dc2a70",
|
||||
"zh:fc39cc1fe71234a0b0369d5c5c7f876c71b956d23d7d6f518289737a001ba69b",
|
||||
"zh:fea4227271ebf7d9e2b61b89ce2328c7262acd9fd190e1fd6d15a591abfa848e",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.terraform.io/hashicorp/tls" {
|
||||
version = "3.1.0"
|
||||
hashes = [
|
||||
"h1:ekOxs6MjdIElt8h9crEVaOwWbEqtfUUfArtA13Jkk6A=",
|
||||
"zh:3d46616b41fea215566f4a957b6d3a1aa43f1f75c26776d72a98bdba79439db6",
|
||||
"zh:623a203817a6dafa86f1b4141b645159e07ec418c82fe40acd4d2a27543cbaa2",
|
||||
"zh:668217e78b210a6572e7b0ecb4134a6781cc4d738f4f5d09eb756085b082592e",
|
||||
"zh:95354df03710691773c8f50a32e31fca25f124b7f3d6078265fdf3c4e1384dca",
|
||||
"zh:9f97ab190380430d57392303e3f36f4f7835c74ea83276baa98d6b9a997c3698",
|
||||
"zh:a16f0bab665f8d933e95ca055b9c8d5707f1a0dd8c8ecca6c13091f40dc1e99d",
|
||||
"zh:be274d5008c24dc0d6540c19e22dbb31ee6bfdd0b2cddd4d97f3cd8a8d657841",
|
||||
"zh:d5faa9dce0a5fc9d26b2463cea5be35f8586ab75030e7fa4d4920cd73ee26989",
|
||||
"zh:e9b672210b7fb410780e7b429975adcc76dd557738ecc7c890ea18942eb321a5",
|
||||
"zh:eb1f8368573d2370605d6dbf60f9aaa5b64e55741d96b5fb026dbfe91de67c0d",
|
||||
"zh:fc1e12b713837b85daf6c3bb703d7795eaf1c5177aebae1afcf811dd7009f4b0",
|
||||
]
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
#
|
||||
# Sample variable values.
|
||||
#
|
||||
namespace="default"
|
||||
deployment_name="wait-for-it-admission-controller"
|
||||
replicas=1
|
||||
image="psevestre/wait-for-it-admission-controller"
|
||||
image_prefix=""
|
||||
image_version="latest"
|
||||
k8s_config_context="minikube"
|
277
kubernetes/k8s-admission-controller/src/test/terraform/main.tf
Normal file
277
kubernetes/k8s-admission-controller/src/test/terraform/main.tf
Normal file
@ -0,0 +1,277 @@
|
||||
|
||||
locals {
|
||||
prefix = var.image_prefix != "" ? "${var.image_prefix}/":""
|
||||
image = "${local.prefix}${var.image}:${var.image_version}"
|
||||
cloud_sdk_image = "${local.prefix}frapsoft/openssl"
|
||||
ns = data.kubernetes_namespace.ns.metadata[0].name
|
||||
|
||||
# Spring SSL Configuration
|
||||
webhook_config_json = jsonencode({
|
||||
server = {
|
||||
port = 443
|
||||
ssl = {
|
||||
"key-store" = "/shared-config/webhook.p12"
|
||||
"key-store-type" = "PKCS12"
|
||||
"key-alias" = "webhook"
|
||||
"key-store-password" = ""
|
||||
}
|
||||
}
|
||||
|
||||
admission-controller = {
|
||||
disabled = false
|
||||
image-prefix = "gcr.io/sandboxbv-01"
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
# Resource namespace
|
||||
data "kubernetes_namespace" "ns" {
|
||||
metadata {
|
||||
name = var.namespace
|
||||
}
|
||||
}
|
||||
|
||||
# TLS Key
|
||||
resource "tls_private_key" "tls" {
|
||||
algorithm = "RSA"
|
||||
}
|
||||
|
||||
# CSR
|
||||
resource "tls_cert_request" "tls" {
|
||||
key_algorithm = "RSA"
|
||||
private_key_pem = tls_private_key.tls.private_key_pem
|
||||
subject {
|
||||
common_name = "${var.deployment_name}.${var.namespace}.svc"
|
||||
}
|
||||
|
||||
dns_names = [
|
||||
var.deployment_name,
|
||||
"${var.deployment_name}.${var.namespace}",
|
||||
"${var.deployment_name}.${var.namespace}.svc",
|
||||
"${var.deployment_name}.${var.namespace}.svc.cluster.local"
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
# HTTPS Certificate
|
||||
resource "tls_self_signed_cert" "tls" {
|
||||
key_algorithm = tls_private_key.tls.algorithm
|
||||
private_key_pem = tls_private_key.tls.private_key_pem
|
||||
|
||||
subject {
|
||||
common_name = "${var.deployment_name}.${local.ns}"
|
||||
}
|
||||
|
||||
validity_period_hours = 24*365*20
|
||||
|
||||
dns_names = [
|
||||
var.deployment_name,
|
||||
"${var.deployment_name}.${var.namespace}",
|
||||
"${var.deployment_name}.${var.namespace}.svc",
|
||||
"${var.deployment_name}.${var.namespace}.svc.cluster.local"
|
||||
]
|
||||
|
||||
allowed_uses = [
|
||||
"key_encipherment",
|
||||
"digital_signature",
|
||||
"server_auth"
|
||||
]
|
||||
}
|
||||
|
||||
# Certificado
|
||||
# Obs: Desativado pois o certificado fica preso no estado "Issued"
|
||||
resource "kubernetes_certificate_signing_request" "tls" {
|
||||
count = 0
|
||||
metadata {
|
||||
name = "${var.deployment_name}.${var.namespace}"
|
||||
}
|
||||
|
||||
auto_approve = true
|
||||
|
||||
spec {
|
||||
usages = [
|
||||
"key encipherment",
|
||||
"digital signature",
|
||||
"server auth"
|
||||
]
|
||||
|
||||
signer_name = "kubernetes.io/kubelet-serving"
|
||||
|
||||
request = tls_cert_request.tls.cert_request_pem
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Secret to store TLS key/cert
|
||||
resource "kubernetes_secret" "tls" {
|
||||
metadata {
|
||||
namespace = local.ns
|
||||
name = var.deployment_name
|
||||
}
|
||||
|
||||
data = {
|
||||
"webhook-key.pem" = tls_private_key.tls.private_key_pem
|
||||
"webhook-cert.pem" = tls_self_signed_cert.tls.cert_pem
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Deployment
|
||||
resource "kubernetes_deployment" "main" {
|
||||
metadata {
|
||||
name = var.deployment_name
|
||||
namespace = local.ns
|
||||
}
|
||||
|
||||
spec {
|
||||
replicas = var.replicas
|
||||
selector {
|
||||
match_labels = {
|
||||
app = var.deployment_name
|
||||
}
|
||||
}
|
||||
|
||||
template {
|
||||
metadata {
|
||||
labels = {
|
||||
app = var.deployment_name
|
||||
}
|
||||
}
|
||||
|
||||
spec {
|
||||
container {
|
||||
image = local.image
|
||||
name = var.deployment_name
|
||||
volume_mount {
|
||||
mount_path = "/shared-config"
|
||||
name = "shared-config"
|
||||
}
|
||||
|
||||
env {
|
||||
name = "SPRING_APPLICATION_JSON"
|
||||
value = local.webhook_config_json
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
init_container {
|
||||
name = "setup-keystore"
|
||||
image = local.cloud_sdk_image
|
||||
|
||||
args = [
|
||||
"pkcs12", "-export",
|
||||
"-in", "/secret/webhook-cert.pem",
|
||||
"-inkey", "/secret/webhook-key.pem",
|
||||
"-name", "webhook",
|
||||
"-out", "/shared-config/webhook.p12",
|
||||
"-passout", "pass:"
|
||||
]
|
||||
|
||||
volume_mount {
|
||||
mount_path = "/shared-config"
|
||||
name = "shared-config"
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
mount_path = "/secret/webhook-cert.pem"
|
||||
name = "webhook-secret"
|
||||
sub_path = "webhook-cert.pem"
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
mount_path = "/secret/webhook-key.pem"
|
||||
name = "webhook-secret"
|
||||
sub_path = "webhook-key.pem"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
volume {
|
||||
name = "shared-config"
|
||||
empty_dir {}
|
||||
}
|
||||
|
||||
volume {
|
||||
name = "webhook-secret"
|
||||
secret {
|
||||
secret_name = kubernetes_secret.tls.metadata[0].name
|
||||
items {
|
||||
key = "webhook-cert.pem"
|
||||
path = "webhook-cert.pem"
|
||||
}
|
||||
items {
|
||||
key = "webhook-key.pem"
|
||||
path = "webhook-key.pem"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Service
|
||||
resource "kubernetes_service" "svc" {
|
||||
metadata {
|
||||
name = var.deployment_name
|
||||
namespace = local.ns
|
||||
}
|
||||
|
||||
spec {
|
||||
selector = {
|
||||
"app" = var.deployment_name
|
||||
}
|
||||
|
||||
port {
|
||||
name = "https"
|
||||
port = 443
|
||||
protocol = "TCP"
|
||||
target_port = 443
|
||||
}
|
||||
|
||||
type = "ClusterIP"
|
||||
}
|
||||
}
|
||||
|
||||
# Admission Controller
|
||||
resource "kubernetes_mutating_webhook_configuration" "waitforit" {
|
||||
metadata {
|
||||
name = var.deployment_name
|
||||
}
|
||||
|
||||
webhook {
|
||||
name = var.admission_controller_name
|
||||
admission_review_versions = [ "v1", "v1beta1" ]
|
||||
|
||||
#failure_policy = "Ignore" #
|
||||
|
||||
client_config {
|
||||
|
||||
service {
|
||||
name = kubernetes_service.svc.metadata[0].name
|
||||
namespace = local.ns
|
||||
path = "/mutate"
|
||||
port = 443
|
||||
}
|
||||
|
||||
# IMPORTANT: CA_BUNDLE must be Base64-encoded
|
||||
ca_bundle = tls_self_signed_cert.tls.cert_pem
|
||||
}
|
||||
|
||||
rule {
|
||||
api_groups = [ "*" ]
|
||||
api_versions = [ "*" ]
|
||||
operations = [ "CREATE", "UPDATE" ]
|
||||
resources = [ "deployments", "statefulsets" ]
|
||||
}
|
||||
|
||||
side_effects = "None" #
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
kubernetes_deployment.main
|
||||
]
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
version = "2.3.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Use standard kubectl environment to get connection details
|
||||
provider "kubernetes" {
|
||||
config_context = var.k8s_config_context
|
||||
config_path = var.k8s_config_path
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
variable "namespace" {
|
||||
type = string
|
||||
description = "Namespace where the Admission Controller will be deploymed"
|
||||
}
|
||||
|
||||
variable "deployment_name" {
|
||||
type = string
|
||||
description = "Admission Controller Deployment Name"
|
||||
}
|
||||
|
||||
variable "replicas" {
|
||||
type = number
|
||||
description = "Number of replicas used in the deployment"
|
||||
default = 3
|
||||
}
|
||||
|
||||
variable "image" {
|
||||
type = string
|
||||
description = "Admission Controller image name"
|
||||
}
|
||||
|
||||
variable "image_version" {
|
||||
type = string
|
||||
description = "Admission Controller image version name"
|
||||
default = "latest"
|
||||
}
|
||||
|
||||
variable "image_prefix" {
|
||||
type = string
|
||||
description = "Image repository prefix"
|
||||
default = "gcr.io/baeldung"
|
||||
}
|
||||
|
||||
variable "admission_controller_name" {
|
||||
type = string
|
||||
description = "Admission Controller name"
|
||||
default = "wait-for-it.service.local"
|
||||
}
|
||||
|
||||
variable "k8s_config_context" {
|
||||
type = string
|
||||
description = "Name of the K8S config context"
|
||||
}
|
||||
|
||||
variable "k8s_config_path" {
|
||||
type = string
|
||||
description = "Location of the standard K8S configuration"
|
||||
default = "~/.kube/config"
|
||||
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>kubernetes</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
@ -13,5 +12,6 @@
|
||||
|
||||
<modules>
|
||||
<module>k8s-intro</module>
|
||||
</modules>
|
||||
<module>k8s-admission-controller</module>
|
||||
</modules>
|
||||
</project>
|
Loading…
x
Reference in New Issue
Block a user