Skip to content

Commit

Permalink
Admission Webhook Sample (#69)
Browse files Browse the repository at this point in the history

Signed-off-by: Attila Mészáros <[email protected]>
  • Loading branch information
csviri authored Apr 5, 2024
1 parent 74e8028 commit 3e7c7e3
Show file tree
Hide file tree
Showing 15 changed files with 278 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,5 @@ jobs:
- name: Run E2E tests
run: |
eval $(minikube -p minikube docker-env)
./mvnw clean install -DskipTests -Dquarkus.container-image.build=true -Dquarkus.kubernetes.namespace=default
./mvnw clean install -DskipTests -Dquarkus.container-image.build=true
./mvnw test -Pe2e-tests
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
QUARKUS_CONTAINER_IMAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
run: |
./mvnw versions:set -DnewVersion="${RELEASE_VERSION}" versions:commit
./mvnw clean install -DskipTests -Dquarkus.container-image.build=true -Dquarkus.kubernetes.namespace=default -Dquarkus.container-image.push=true
./mvnw clean install -DskipTests -Dquarkus.container-image.build=true -Dquarkus.container-image.push=true
./mvnw ${MAVEN_ARGS} -q build-helper:parse-version versions:set -DnewVersion=\${parsedVersion.majorVersion}.\${parsedVersion.minorVersion}.\${parsedVersion.nextIncrementalVersion}-SNAPSHOT versions:commit
git config --local user.email "[email protected]"
git config --local user.name "GitHub Action"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;

public class PodsReadyCondition<R extends HasMetadata> implements Condition<R, Glue> {
public class ReadyCondition<R extends HasMetadata> implements Condition<R, Glue> {

private final Readiness readiness = Readiness.getInstance();

private final boolean negated;

public PodsReadyCondition(boolean negated) {
public ReadyCondition(boolean negated) {
this.negated = negated;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use = JsonTypeInfo.Id.NAME,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = PodsReadyConditionSpec.class, name = "PodsReady"),
@JsonSubTypes.Type(value = ReadyConditionSpec.class, name = "ReadyCondition"),
@JsonSubTypes.Type(value = JavaScriptConditionSpec.class, name = "JSCondition")
})
public class ConditionSpec {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package io.csviri.operator.resourceglue.customresource.glue.condition;

public class PodsReadyConditionSpec extends ConditionSpec {
public class ReadyConditionSpec extends ConditionSpec {

private final boolean negated;

public PodsReadyConditionSpec(boolean negated) {
public ReadyConditionSpec(boolean negated) {
this.negated = negated;
}

public PodsReadyConditionSpec() {
public ReadyConditionSpec() {
this(false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@

import io.csviri.operator.resourceglue.Utils;
import io.csviri.operator.resourceglue.conditions.JavaScripCondition;
import io.csviri.operator.resourceglue.conditions.PodsReadyCondition;
import io.csviri.operator.resourceglue.conditions.ReadyCondition;
import io.csviri.operator.resourceglue.customresource.glue.DependentResourceSpec;
import io.csviri.operator.resourceglue.customresource.glue.Glue;
import io.csviri.operator.resourceglue.customresource.glue.condition.ConditionSpec;
import io.csviri.operator.resourceglue.customresource.glue.condition.JavaScriptConditionSpec;
import io.csviri.operator.resourceglue.customresource.glue.condition.PodsReadyConditionSpec;
import io.csviri.operator.resourceglue.customresource.glue.condition.ReadyConditionSpec;
import io.csviri.operator.resourceglue.dependent.GCGenericDependentResource;
import io.csviri.operator.resourceglue.dependent.GenericDependentResource;
import io.csviri.operator.resourceglue.dependent.GenericResourceDiscriminator;
Expand Down Expand Up @@ -148,10 +148,11 @@ private void createAndAddDependentToWorkflow(Glue primary, Context<Glue> context
Map<String, GenericDependentResource> genericDependentResourceMap,
WorkflowBuilder<Glue> builder, boolean leafDependent) {

// todo test processing ns not as template
// todo test processing ns as template
// name can reference related resources todo doc
var targetNamespace = Utils.getNamespace(spec).map(ns -> genericTemplateHandler
.processTemplate(Utils.getName(spec), primary, context));
.processTemplate(ns, primary, context));
var resourceInSameNamespaceAsPrimary =
targetNamespace.map(n -> n.trim().equals(primary.getMetadata().getNamespace().trim()))
.orElse(true);
Expand Down Expand Up @@ -197,8 +198,8 @@ private static GenericDependentResource createDependentResource(DependentResourc

@SuppressWarnings({"rawtypes"})
private Condition toCondition(ConditionSpec condition) {
if (condition instanceof PodsReadyConditionSpec readyConditionSpec) {
return new PodsReadyCondition(readyConditionSpec.isNegated());
if (condition instanceof ReadyConditionSpec readyConditionSpec) {
return new ReadyCondition(readyConditionSpec.isNegated());
} else if (condition instanceof JavaScriptConditionSpec jsCondition) {
return new JavaScripCondition(jsCondition.getScript());
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ quarkus.jib.jvm-additional-arguments=-Dpolyglot.engine.WarnInterpreterOnly=false
# To inherit visibility from the repo
quarkus.container-image.labels."org.opencontainers.image.source"=https://github.com/csviri/resource-glue-operator
quarkus.container-image.labels."org.opencontainers.image.documentation"=https://github.com/csviri/resource-glue-operator?tab=readme-ov-file#documentation
# Generate apply-able cluster role bindings
quarkus.kubernetes.namespace=default
8 changes: 8 additions & 0 deletions src/test/java/io/csviri/operator/resourceglue/TestUtils.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.csviri.operator.resourceglue;

import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
Expand Down Expand Up @@ -144,4 +145,11 @@ public static void applyAndWait(KubernetesClient client, InputStream is,
applyAndWait(client, resources, transformer);
}

public static void applyAndWait(KubernetesClient client, URL url) {
try (InputStream is = url.openStream()) {
applyAndWait(client, is);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package io.csviri.operator.resourceglue.sample.mutation;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import io.csviri.operator.resourceglue.TestUtils;
import io.csviri.operator.resourceglue.customresource.glue.Glue;
import io.csviri.operator.resourceglue.customresource.operator.GlueOperator;
import io.fabric8.kubernetes.api.model.*;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.fabric8.kubernetes.client.dsl.NonDeletingOperation;

import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

public class MutationWebhookDeploymentE2E {

public static final String TEST_POD_NAME = "testpod";
private final KubernetesClient client = new KubernetesClientBuilder().build();

@BeforeEach
void applyCRDs() throws MalformedURLException, URISyntaxException {
TestUtils.applyCrd(client, Glue.class, GlueOperator.class);
TestUtils.applyAndWait(client,
new URI(
"https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml")
.toURL());
TestUtils.applyAndWait(client, "target/kubernetes/kubernetes.yml");
TestUtils.applyAndWait(client, "src/test/resources/sample/mutation/related.resource.yaml");
}

@Test
void testMutationHookDeployment() {
client.resource(TestUtils.load("/sample/mutation/mutation.glue.yaml"))
.createOr(NonDeletingOperation::update);

await().atMost(Duration.ofMinutes(5)).untilAsserted(() -> {
var conf = client.admissionRegistration().v1().mutatingWebhookConfigurations()
.withName("pod-mutating-webhook").get();
var deployment = client.apps().deployments().withName("pod-mutating-hook").get();
assertThat(conf).isNotNull();
assertThat(deployment.getStatus().getReadyReplicas()).isGreaterThan(0);
});

var pod = client.resource(testPod()).create();
assertThat(pod.getMetadata().getAnnotations()).containsEntry("sample.annotation.present",
"true");
}

Pod testPod() {
return new PodBuilder()
.withNewMetadata()
.withName(TEST_POD_NAME)
.endMetadata()
.withNewSpec()
.withContainers(new ContainerBuilder()
.withName("nginx")
.withImage("nginx:1.14.2")
.withPorts(new ContainerPortBuilder()
.withContainerPort(80)
.build())
.build())
.endSpec()
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ void applyCRDs() {
}

@Test
void testDeployment() {
void testWebPageCRUDOperations() {
client.resource(TestUtils.load("/sample/webpage/webpage.operator.yaml"))
.createOr(NonDeletingOperation::update);
var webPage = TestUtils.load("/sample/webpage/webpage.sample.yaml", WebPage.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class WebPageSampleTest extends TestBase {

@Test
void webPageCRUD() {
createOrUpdate(TestUtils.load("/sample/webpage/webpage.crd.yml"));
createOrUpdate(TestUtils.load("/sample/webpage/webpage.crd.yaml"));
createOrUpdate(TestUtils.load("/sample/webpage/webpage.operator.yaml"));
WebPage webPage =
createOrUpdate(TestUtils.load("/sample/webpage/webpage.sample.yaml", WebPage.class));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ spec:
kind: ConfigMap
metadata:
name: "configmap1"
namespace: default
data:
key: "v1"
- name: configMap2
Expand Down
109 changes: 109 additions & 0 deletions src/test/resources/sample/mutation/mutation.glue.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
apiVersion: io.csviri.operator.resourceglue/v1beta1
kind: Glue
metadata:
name: mutation-webhook-deployment
spec:
resources:
- name: service
resource:
apiVersion: v1
kind: Service
metadata:
name: pod-mutating-hook
spec:
ports:
- name: https
port: 443
protocol: TCP
targetPort: 443
selector:
app.kubernetes.io/name: pod-mutating-hook
app.kubernetes.io/version: 0.1.0
type: NodePort
- name: deployment
readyPostCondition:
type: ReadyCondition
resource:
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-mutating-hook
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: pod-mutating-hook
app.kubernetes.io/version: 0.1.0
template:
metadata:
labels:
app.kubernetes.io/name: pod-mutating-hook
app.kubernetes.io/version: 0.1.0
namespace: default
spec:
containers:
- env:
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: QUARKUS_HTTP_SSL_CERTIFICATE_KEY_STORE_FILE
value: /etc/certs/keystore.p12
- name: QUARKUS_HTTP_SSL_CERTIFICATE_KEY_STORE_FILE_TYPE
value: PKCS12
- name: QUARKUS_HTTP_SSL_CERTIFICATE_KEY_STORE_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: pkcs12-pass
image: ghcr.io/csviri/sample-pod-mutating-webhook:0.1.0
imagePullPolicy: IfNotPresent
name: pod-mutating-hook
ports:
- containerPort: 443
name: https
protocol: TCP
volumeMounts:
- mountPath: /etc/certs
name: sample-pod-mutating-webhook
readOnly: true
serviceAccountName: pod-mutating-hook
volumes:
- name: sample-pod-mutating-webhook
secret:
optional: false
secretName: tls-secret
- name: mutation_hook_config
dependsOn:
- deployment
- service
resource:
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
annotations:
cert-manager.io/inject-ca-from: default/sample-pod-mutating-webhook
name: pod-mutating-webhook
webhooks:
- admissionReviewVersions:
- v1
clientConfig:
service:
name: pod-mutating-hook
namespace: default
path: /mutate
failurePolicy: Fail
name: sample.mutating.webhook
rules:
- apiGroups:
- ""
apiVersions:
- v1
operations:
- UPDATE
- CREATE
resources:
- pods
sideEffects: None
timeoutSeconds: 5

Loading

0 comments on commit 3e7c7e3

Please sign in to comment.