get() {
return new GerritIngressReconciler();
case ISTIO:
return new GerritIstioReconciler();
+ case AMBASSADOR:
+ return new GerritAmbassadorReconciler();
default:
return new GerritNoIngressReconciler();
}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/IngressType.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/IngressType.java
index a4af09e02..9c5383c44 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/IngressType.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/IngressType.java
@@ -17,5 +17,6 @@
public enum IngressType {
NONE,
INGRESS,
- ISTIO
+ ISTIO,
+ AMBASSADOR
}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/GerritAmbassadorReconciler.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/GerritAmbassadorReconciler.java
new file mode 100644
index 000000000..2b9f594fc
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/GerritAmbassadorReconciler.java
@@ -0,0 +1,156 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador;
+
+import static com.google.gerrit.k8s.operator.network.ambassador.GerritAmbassadorReconciler.MAPPING_EVENT_SOURCE;
+import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMapping.GERRIT_MAPPING;
+import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingGETReplica.GERRIT_MAPPING_GET_REPLICA;
+import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingPOSTReplica.GERRIT_MAPPING_POST_REPLICA;
+import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingPrimary.GERRIT_MAPPING_PRIMARY;
+import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingReceiver.GERRIT_MAPPING_RECEIVER;
+import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingReceiverGET.GERRIT_MAPPING_RECEIVER_GET;
+
+import com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMapping;
+import com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingGETReplica;
+import com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingPOSTReplica;
+import com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingPrimary;
+import com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingReceiver;
+import com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingReceiverGET;
+import com.google.gerrit.k8s.operator.network.ambassador.dependent.LoadBalanceCondition;
+import com.google.gerrit.k8s.operator.network.ambassador.dependent.ReceiverMappingCondition;
+import com.google.gerrit.k8s.operator.network.ambassador.dependent.SingleMappingCondition;
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.inject.Singleton;
+import io.getambassador.v2.Mapping;
+import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
+import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext;
+import io.javaoperatorsdk.operator.api.reconciler.EventSourceInitializer;
+import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
+import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
+import io.javaoperatorsdk.operator.processing.event.source.EventSource;
+import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Provides an Ambassador-based implementation for GerritNetworkReconciler.
+ *
+ * Creates and manages Ambassador Custom Resources using the "managed dependent resources"
+ * approach in josdk. Since multiple dependent resources of the same type (`Mapping`) need to be
+ * created, "resource discriminators" are used for each of the different Mapping dependent
+ * resources.
+ *
+ *
Ambassador custom resource POJOs are generated via the `java-generator-maven-plugin` in the
+ * fabric8 project.
+ *
+ *
Mapping logic
+ *
+ *
The Mappings are created based on the composition of Gerrit instances in the GerritCluster.
+ *
+ *
There are three cases:
+ *
+ *
1. 0 Primary 1 Replica
+ *
+ *
Direct all traffic (read/write) to the Replica
+ *
+ *
2. 1 Primary 0 Replica
+ *
+ *
Direct all traffic (read/write) to the Primary
+ *
+ *
3. 1 Primary 1 Replica
+ *
+ *
Direct write traffic to Primary and read traffic to Replica. To capture this requirement,
+ * three different Mappings have to be created.
+ *
+ *
Note: git fetch/clone operations result in two HTTP requests to the git server. The first is
+ * of the form `GET /my-test-repo/info/refs?service=git-upload-pack` and the second is of the form
+ * `POST /my-test-repo/git-upload-pack`.
+ *
+ *
Note: git push operations result in two HTTP requests to the git server. The first is of the
+ * form `GET /my-test-repo/info/refs?service=git-receive-pack` and the second is of the form `POST
+ * /my-test-repo/git-receive-pack`.
+ *
+ *
If a Receiver is part of the GerritCluster, additional mappings are created such that all
+ * requests that the replication plugin sends to the `adminUrl` [1] are routed to the Receiver. This
+ * includes `git push` related `GET` and `POST` requests, and requests to the `/projects` REST API
+ * endpoints.
+ *
+ *
[1]
+ * https://gerrit.googlesource.com/plugins/replication/+/refs/heads/master/src/main/resources/Documentation/config.md
+ */
+@Singleton
+@ControllerConfiguration(
+ // namespaces = "gerrit-operator",
+ dependents = {
+ @Dependent(
+ name = GERRIT_MAPPING,
+ type = GerritClusterMapping.class,
+ // Cluster has only either Primary or Replica instance
+ reconcilePrecondition = SingleMappingCondition.class,
+ useEventSourceWithName = MAPPING_EVENT_SOURCE),
+ @Dependent(
+ name = GERRIT_MAPPING_POST_REPLICA,
+ type = GerritClusterMappingPOSTReplica.class,
+ // Cluster has both Primary and Replica instances
+ reconcilePrecondition = LoadBalanceCondition.class,
+ useEventSourceWithName = MAPPING_EVENT_SOURCE),
+ @Dependent(
+ name = GERRIT_MAPPING_GET_REPLICA,
+ type = GerritClusterMappingGETReplica.class,
+ reconcilePrecondition = LoadBalanceCondition.class,
+ useEventSourceWithName = MAPPING_EVENT_SOURCE),
+ @Dependent(
+ name = GERRIT_MAPPING_PRIMARY,
+ type = GerritClusterMappingPrimary.class,
+ reconcilePrecondition = LoadBalanceCondition.class,
+ useEventSourceWithName = MAPPING_EVENT_SOURCE),
+ @Dependent(
+ name = GERRIT_MAPPING_RECEIVER,
+ type = GerritClusterMappingReceiver.class,
+ reconcilePrecondition = ReceiverMappingCondition.class,
+ useEventSourceWithName = MAPPING_EVENT_SOURCE),
+ @Dependent(
+ name = GERRIT_MAPPING_RECEIVER_GET,
+ type = GerritClusterMappingReceiverGET.class,
+ reconcilePrecondition = ReceiverMappingCondition.class,
+ useEventSourceWithName = MAPPING_EVENT_SOURCE)
+ })
+public class GerritAmbassadorReconciler
+ implements Reconciler, EventSourceInitializer {
+
+ public static final String MAPPING_EVENT_SOURCE = "mapping-event-source";
+
+ // Because we have multiple dependent resources of the same type `Mapping`, we need to specify
+ // a named event source.
+ @Override
+ public Map prepareEventSources(EventSourceContext context) {
+ InformerEventSource mappingEventSource =
+ new InformerEventSource<>(
+ InformerConfiguration.from(Mapping.class, context).build(), context);
+
+ Map eventSources = new HashMap<>();
+ eventSources.put(MAPPING_EVENT_SOURCE, mappingEventSource);
+ return eventSources;
+ }
+
+ @Override
+ public UpdateControl reconcile(
+ GerritNetwork resource, Context context) throws Exception {
+ return UpdateControl.noUpdate();
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/AbstractAmbassadorDependentResource.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/AbstractAmbassadorDependentResource.java
new file mode 100644
index 000000000..b6438eee6
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/AbstractAmbassadorDependentResource.java
@@ -0,0 +1,62 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import io.fabric8.kubernetes.api.model.HasMetadata;
+import io.fabric8.kubernetes.api.model.ObjectMeta;
+import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
+import io.getambassador.v2.MappingSpec;
+import io.getambassador.v2.MappingSpecBuilder;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
+import java.util.List;
+
+public abstract class AbstractAmbassadorDependentResource
+ extends CRUDKubernetesDependentResource {
+
+ public AbstractAmbassadorDependentResource(Class dependentResourceClass) {
+ super(dependentResourceClass);
+ }
+
+ public ObjectMeta getCommonMetadata(GerritNetwork gerritnetwork, String name, String className) {
+ ObjectMeta metadata =
+ new ObjectMetaBuilder()
+ .withName(name)
+ .withNamespace(gerritnetwork.getMetadata().getNamespace())
+ .withLabels(
+ GerritCluster.getLabels(gerritnetwork.getMetadata().getName(), name, className))
+ .build();
+ return metadata;
+ }
+
+ public MappingSpec getCommonSpec(GerritNetwork gerritnetwork, String serviceName) {
+ MappingSpec spec =
+ new MappingSpecBuilder()
+ .withAmbassadorId(getAmbassadorIds(gerritnetwork))
+ .withHost(gerritnetwork.getSpec().getIngress().getHost())
+ .withPrefix("/")
+ .withService(serviceName)
+ .withBypassAuth(true)
+ .withRewrite("") // important - so the prefix doesn't get overwritten to "/"
+ .build();
+ return spec;
+ }
+
+ public List getAmbassadorIds(GerritNetwork gerritnetwork) {
+ // TODO: Allow users to configure ambassador_id
+ return null;
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMapping.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMapping.java
new file mode 100644
index 000000000..e6f63edbf
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMapping.java
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.network.model.NetworkMemberWithSsh;
+import io.getambassador.v2.Mapping;
+import io.getambassador.v2.MappingBuilder;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
+
+@KubernetesDependent(resourceDiscriminator = GerritClusterMappingDiscriminator.class)
+public class GerritClusterMapping extends AbstractAmbassadorDependentResource
+ implements MappingDependentResourceInterface {
+
+ public static final String GERRIT_MAPPING = "gerrit-mapping";
+
+ public GerritClusterMapping() {
+ super(Mapping.class);
+ }
+
+ @Override
+ public Mapping desired(GerritNetwork gerritNetwork, Context context) {
+
+ // If only one Gerrit instance in GerritCluster, send all git-over-https requests to it
+ NetworkMemberWithSsh gerrit =
+ gerritNetwork.hasGerritReplica()
+ ? gerritNetwork.getSpec().getGerritReplica()
+ : gerritNetwork.getSpec().getPrimaryGerrit();
+ String serviceName = gerrit.getName() + ":" + gerrit.getHttpPort();
+ Mapping mapping =
+ new MappingBuilder()
+ .withNewMetadataLike(
+ getCommonMetadata(gerritNetwork, GERRIT_MAPPING, this.getClass().getSimpleName()))
+ .endMetadata()
+ .withNewSpecLike(getCommonSpec(gerritNetwork, serviceName))
+ .endSpec()
+ .build();
+ return mapping;
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingDiscriminator.java
new file mode 100644
index 000000000..e4bd776ed
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingDiscriminator.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMapping.GERRIT_MAPPING;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import io.getambassador.v2.Mapping;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
+import io.javaoperatorsdk.operator.processing.event.ResourceID;
+import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
+import java.util.Optional;
+
+public class GerritClusterMappingDiscriminator
+ implements ResourceDiscriminator {
+ @Override
+ public Optional distinguish(
+ Class resource, GerritNetwork network, Context context) {
+ InformerEventSource ies =
+ (InformerEventSource)
+ context.eventSourceRetriever().getResourceEventSourceFor(Mapping.class);
+ return ies.get(new ResourceID(GERRIT_MAPPING, network.getMetadata().getNamespace()));
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingGETReplica.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingGETReplica.java
new file mode 100644
index 000000000..6619c7520
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingGETReplica.java
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import static com.google.gerrit.k8s.operator.network.Constants.INFO_REFS_PATTERN;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import io.getambassador.v2.Mapping;
+import io.getambassador.v2.MappingBuilder;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
+import java.util.HashMap;
+
+@KubernetesDependent(resourceDiscriminator = GerritClusterMappingGETReplicaDiscriminator.class)
+public class GerritClusterMappingGETReplica extends AbstractAmbassadorDependentResource
+ implements MappingDependentResourceInterface {
+
+ public static final String GERRIT_MAPPING_GET_REPLICA = "gerrit-mapping-get-replica";
+
+ public GerritClusterMappingGETReplica() {
+ super(Mapping.class);
+ }
+
+ @Override
+ public Mapping desired(GerritNetwork gerritNetwork, Context context) {
+
+ String replicaServiceName =
+ gerritNetwork.getSpec().getGerritReplica().getName()
+ + ":"
+ + gerritNetwork.getSpec().getGerritReplica().getHttpPort();
+
+ // Send fetch/clone GET requests to the Replica
+ Mapping mapping =
+ new MappingBuilder()
+ .withNewMetadataLike(
+ getCommonMetadata(
+ gerritNetwork, GERRIT_MAPPING_GET_REPLICA, this.getClass().getSimpleName()))
+ .endMetadata()
+ .withNewSpecLike(getCommonSpec(gerritNetwork, replicaServiceName))
+ .withNewV2QueryParameters()
+ .withAdditionalProperties(
+ new HashMap() {
+ {
+ put("service", "git-upload-pack");
+ }
+ })
+ .endV2QueryParameters()
+ .withMethod("GET")
+ .withPrefix(INFO_REFS_PATTERN)
+ .withPrefixRegex(true)
+ .endSpec()
+ .build();
+
+ return mapping;
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingGETReplicaDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingGETReplicaDiscriminator.java
new file mode 100644
index 000000000..42dd429fb
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingGETReplicaDiscriminator.java
@@ -0,0 +1,38 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingGETReplica.GERRIT_MAPPING_GET_REPLICA;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import io.getambassador.v2.Mapping;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
+import io.javaoperatorsdk.operator.processing.event.ResourceID;
+import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
+import java.util.Optional;
+
+public class GerritClusterMappingGETReplicaDiscriminator
+ implements ResourceDiscriminator {
+ @Override
+ public Optional distinguish(
+ Class resource, GerritNetwork network, Context context) {
+ InformerEventSource ies =
+ (InformerEventSource)
+ context.eventSourceRetriever().getResourceEventSourceFor(Mapping.class);
+ return ies.get(
+ new ResourceID(GERRIT_MAPPING_GET_REPLICA, network.getMetadata().getNamespace()));
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPOSTReplica.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPOSTReplica.java
new file mode 100644
index 000000000..5274f5096
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPOSTReplica.java
@@ -0,0 +1,58 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import static com.google.gerrit.k8s.operator.network.Constants.UPLOAD_PACK_URL_PATTERN;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import io.getambassador.v2.Mapping;
+import io.getambassador.v2.MappingBuilder;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
+
+@KubernetesDependent(resourceDiscriminator = GerritClusterMappingPOSTReplicaDiscriminator.class)
+public class GerritClusterMappingPOSTReplica extends AbstractAmbassadorDependentResource
+ implements MappingDependentResourceInterface {
+
+ public static final String GERRIT_MAPPING_POST_REPLICA = "gerrit-mapping-post-replica";
+
+ public GerritClusterMappingPOSTReplica() {
+ super(Mapping.class);
+ }
+
+ @Override
+ public Mapping desired(GerritNetwork gerritNetwork, Context context) {
+
+ String replicaServiceName =
+ gerritNetwork.getSpec().getGerritReplica().getName()
+ + ":"
+ + gerritNetwork.getSpec().getGerritReplica().getHttpPort();
+
+ // Send fetch/clone POST requests to the Replica
+ Mapping mapping =
+ new MappingBuilder()
+ .withNewMetadataLike(
+ getCommonMetadata(
+ gerritNetwork, GERRIT_MAPPING_POST_REPLICA, this.getClass().getSimpleName()))
+ .endMetadata()
+ .withNewSpecLike(getCommonSpec(gerritNetwork, replicaServiceName))
+ .withPrefix(UPLOAD_PACK_URL_PATTERN)
+ .withPrefixRegex(true)
+ .withMethod("POST")
+ .endSpec()
+ .build();
+ return mapping;
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPOSTReplicaDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPOSTReplicaDiscriminator.java
new file mode 100644
index 000000000..cb1b8d968
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPOSTReplicaDiscriminator.java
@@ -0,0 +1,38 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingPOSTReplica.GERRIT_MAPPING_POST_REPLICA;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import io.getambassador.v2.Mapping;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
+import io.javaoperatorsdk.operator.processing.event.ResourceID;
+import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
+import java.util.Optional;
+
+public class GerritClusterMappingPOSTReplicaDiscriminator
+ implements ResourceDiscriminator {
+ @Override
+ public Optional distinguish(
+ Class resource, GerritNetwork network, Context context) {
+ InformerEventSource ies =
+ (InformerEventSource)
+ context.eventSourceRetriever().getResourceEventSourceFor(Mapping.class);
+ return ies.get(
+ new ResourceID(GERRIT_MAPPING_POST_REPLICA, network.getMetadata().getNamespace()));
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPrimary.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPrimary.java
new file mode 100644
index 000000000..0c936836f
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPrimary.java
@@ -0,0 +1,55 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import io.getambassador.v2.Mapping;
+import io.getambassador.v2.MappingBuilder;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
+
+@KubernetesDependent(resourceDiscriminator = GerritClusterMappingPrimaryDiscriminator.class)
+public class GerritClusterMappingPrimary extends AbstractAmbassadorDependentResource
+ implements MappingDependentResourceInterface {
+
+ public static final String GERRIT_MAPPING_PRIMARY = "gerrit-mapping-primary";
+
+ public GerritClusterMappingPrimary() {
+ super(Mapping.class);
+ }
+
+ @Override
+ public Mapping desired(GerritNetwork gerritNetwork, Context context) {
+
+ String primaryServiceName =
+ gerritNetwork.getSpec().getPrimaryGerrit().getName()
+ + ":"
+ + gerritNetwork.getSpec().getPrimaryGerrit().getHttpPort();
+
+ // Send all write traffic (non git fetch/clone traffic) to the Primary.
+ // Emissary evaluates more constrained Mappings first.
+ Mapping mapping =
+ new MappingBuilder()
+ .withNewMetadataLike(
+ getCommonMetadata(
+ gerritNetwork, GERRIT_MAPPING_PRIMARY, this.getClass().getSimpleName()))
+ .endMetadata()
+ .withNewSpecLike(getCommonSpec(gerritNetwork, primaryServiceName))
+ .endSpec()
+ .build();
+
+ return mapping;
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPrimaryDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPrimaryDiscriminator.java
new file mode 100644
index 000000000..abb7b9089
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPrimaryDiscriminator.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingPrimary.GERRIT_MAPPING_PRIMARY;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import io.getambassador.v2.Mapping;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
+import io.javaoperatorsdk.operator.processing.event.ResourceID;
+import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
+import java.util.Optional;
+
+public class GerritClusterMappingPrimaryDiscriminator
+ implements ResourceDiscriminator {
+ @Override
+ public Optional distinguish(
+ Class resource, GerritNetwork network, Context context) {
+ InformerEventSource ies =
+ (InformerEventSource)
+ context.eventSourceRetriever().getResourceEventSourceFor(Mapping.class);
+ return ies.get(new ResourceID(GERRIT_MAPPING_PRIMARY, network.getMetadata().getNamespace()));
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiver.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiver.java
new file mode 100644
index 000000000..a35ac1ffb
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiver.java
@@ -0,0 +1,58 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import static com.google.gerrit.k8s.operator.network.Constants.PROJECTS_URL_PATTERN;
+import static com.google.gerrit.k8s.operator.network.Constants.RECEIVE_PACK_URL_PATTERN;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.receiver.dependent.ReceiverService;
+import io.getambassador.v2.Mapping;
+import io.getambassador.v2.MappingBuilder;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
+
+@KubernetesDependent(resourceDiscriminator = GerritClusterMappingReceiverDiscriminator.class)
+public class GerritClusterMappingReceiver extends AbstractAmbassadorDependentResource
+ implements MappingDependentResourceInterface {
+
+ public static final String GERRIT_MAPPING_RECEIVER = "gerrit-mapping-receiver";
+
+ public GerritClusterMappingReceiver() {
+ super(Mapping.class);
+ }
+
+ @Override
+ public Mapping desired(GerritNetwork gerritNetwork, Context context) {
+
+ String receiverServiceName =
+ ReceiverService.getName(gerritNetwork.getSpec().getReceiver().getName())
+ + ":"
+ + gerritNetwork.getSpec().getReceiver().getHttpPort();
+
+ Mapping mapping =
+ new MappingBuilder()
+ .withNewMetadataLike(
+ getCommonMetadata(
+ gerritNetwork, GERRIT_MAPPING_RECEIVER, this.getClass().getSimpleName()))
+ .endMetadata()
+ .withNewSpecLike(getCommonSpec(gerritNetwork, receiverServiceName))
+ .withPrefix(PROJECTS_URL_PATTERN + "|" + RECEIVE_PACK_URL_PATTERN)
+ .withPrefixRegex(true)
+ .endSpec()
+ .build();
+ return mapping;
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverDiscriminator.java
new file mode 100644
index 000000000..f5aa07fed
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverDiscriminator.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingReceiver.GERRIT_MAPPING_RECEIVER;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import io.getambassador.v2.Mapping;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
+import io.javaoperatorsdk.operator.processing.event.ResourceID;
+import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
+import java.util.Optional;
+
+public class GerritClusterMappingReceiverDiscriminator
+ implements ResourceDiscriminator {
+ @Override
+ public Optional distinguish(
+ Class resource, GerritNetwork network, Context context) {
+ InformerEventSource ies =
+ (InformerEventSource)
+ context.eventSourceRetriever().getResourceEventSourceFor(Mapping.class);
+ return ies.get(new ResourceID(GERRIT_MAPPING_RECEIVER, network.getMetadata().getNamespace()));
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverGET.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverGET.java
new file mode 100644
index 000000000..7f5948823
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverGET.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import static com.google.gerrit.k8s.operator.network.Constants.INFO_REFS_PATTERN;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.receiver.dependent.ReceiverService;
+import io.getambassador.v2.Mapping;
+import io.getambassador.v2.MappingBuilder;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
+import java.util.HashMap;
+
+@KubernetesDependent(resourceDiscriminator = GerritClusterMappingReceiverGETDiscriminator.class)
+public class GerritClusterMappingReceiverGET extends AbstractAmbassadorDependentResource
+ implements MappingDependentResourceInterface {
+
+ public static final String GERRIT_MAPPING_RECEIVER_GET = "gerrit-mapping-receiver-get";
+
+ public GerritClusterMappingReceiverGET() {
+ super(Mapping.class);
+ }
+
+ @Override
+ public Mapping desired(GerritNetwork gerritNetwork, Context context) {
+
+ String receiverServiceName =
+ ReceiverService.getName(gerritNetwork.getSpec().getReceiver().getName())
+ + ":"
+ + gerritNetwork.getSpec().getReceiver().getHttpPort();
+
+ Mapping mapping =
+ new MappingBuilder()
+ .withNewMetadataLike(
+ getCommonMetadata(
+ gerritNetwork, GERRIT_MAPPING_RECEIVER_GET, this.getClass().getSimpleName()))
+ .endMetadata()
+ .withNewSpecLike(getCommonSpec(gerritNetwork, receiverServiceName))
+ .withNewV2QueryParameters()
+ .withAdditionalProperties(
+ new HashMap() {
+ {
+ put("service", "git-receive-pack");
+ }
+ })
+ .endV2QueryParameters()
+ .withMethod("GET")
+ .withPrefix(INFO_REFS_PATTERN)
+ .withPrefixRegex(true)
+ .endSpec()
+ .build();
+ return mapping;
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverGETDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverGETDiscriminator.java
new file mode 100644
index 000000000..27b36e85c
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverGETDiscriminator.java
@@ -0,0 +1,38 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingReceiverGET.GERRIT_MAPPING_RECEIVER_GET;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import io.getambassador.v2.Mapping;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
+import io.javaoperatorsdk.operator.processing.event.ResourceID;
+import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
+import java.util.Optional;
+
+public class GerritClusterMappingReceiverGETDiscriminator
+ implements ResourceDiscriminator {
+ @Override
+ public Optional distinguish(
+ Class resource, GerritNetwork network, Context context) {
+ InformerEventSource ies =
+ (InformerEventSource)
+ context.eventSourceRetriever().getResourceEventSourceFor(Mapping.class);
+ return ies.get(
+ new ResourceID(GERRIT_MAPPING_RECEIVER_GET, network.getMetadata().getNamespace()));
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/LoadBalanceCondition.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/LoadBalanceCondition.java
new file mode 100644
index 000000000..6f9b3b1d6
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/LoadBalanceCondition.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import io.getambassador.v2.Mapping;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
+import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
+
+public class LoadBalanceCondition implements Condition {
+
+ @Override
+ public boolean isMet(
+ DependentResource dependentResource,
+ GerritNetwork gerritNetwork,
+ Context context) {
+
+ return gerritNetwork.getSpec().getIngress().isEnabled()
+ && gerritNetwork.hasPrimaryGerrit()
+ && gerritNetwork.hasGerritReplica();
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/MappingDependentResourceInterface.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/MappingDependentResourceInterface.java
new file mode 100644
index 000000000..097e97242
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/MappingDependentResourceInterface.java
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import io.getambassador.v2.Mapping;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+
+public interface MappingDependentResourceInterface {
+ public Mapping desired(GerritNetwork gerritNetwork, Context context);
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/ReceiverMappingCondition.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/ReceiverMappingCondition.java
new file mode 100644
index 000000000..b9f88fd1f
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/ReceiverMappingCondition.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import io.getambassador.v2.Mapping;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
+import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
+
+public class ReceiverMappingCondition implements Condition {
+
+ @Override
+ public boolean isMet(
+ DependentResource dependentResource,
+ GerritNetwork gerritNetwork,
+ Context context) {
+
+ return gerritNetwork.getSpec().getIngress().isEnabled() && gerritNetwork.hasReceiver();
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/SingleMappingCondition.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/SingleMappingCondition.java
new file mode 100644
index 000000000..07606c596
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/SingleMappingCondition.java
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import io.getambassador.v2.Mapping;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
+import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
+
+public class SingleMappingCondition implements Condition {
+
+ @Override
+ public boolean isMet(
+ DependentResource dependentResource,
+ GerritNetwork gerritNetwork,
+ Context context) {
+
+ return gerritNetwork.getSpec().getIngress().isEnabled()
+ && (gerritNetwork.hasPrimaryGerrit() ^ gerritNetwork.hasGerritReplica());
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverTemplate.java b/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverTemplate.java
index 5c4332a38..eb932eb98 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverTemplate.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverTemplate.java
@@ -33,7 +33,7 @@
@Buildable(
editableEnabled = false,
validationEnabled = false,
- generateBuilderPackage = true,
+ generateBuilderPackage = false,
lazyCollectionInitEnabled = false,
builderPackage = "io.fabric8.kubernetes.api.builder")
public class ReceiverTemplate implements KubernetesResource {
diff --git a/operator/src/main/resources/crd/emissary-crds.yaml b/operator/src/main/resources/crd/emissary-crds.yaml
new file mode 100644
index 000000000..5ead04e27
--- /dev/null
+++ b/operator/src/main/resources/crd/emissary-crds.yaml
@@ -0,0 +1,1952 @@
+# This file is downloaded from the Emissary repository on GitHub:
+# https://github.com/emissary-ingress/emissary/blob/master/manifests/emissary/emissary-crds.yaml.in
+#
+# Several modifications have been manually made:
+# 1. Only the `Mapping` and `TLSContext` CRDs have been kept from the source file. The source file
+# defines many CRDs that are not required by this operator project so the unnecessary CRDs have
+# been deleted.
+# 2. `v2ExplicitTLS` field has been removed from the Mapping CRD `v3alpha1` version. This is because
+# the "crd-to-java" generator plugin we use has a bug (https://github.com/fabric8io/kubernetes-client/issues/5457)
+# while converting enum types and the bug is triggered by the `v2ExplicitTLS` field. This field
+# may be added back in once we upgrade our fabric8 version to 6.8.x, where this bug is resolved.
+# 3. `ambassador_id` property is added to `Mapping` and `TLSContext` CRD version `v2`, by copying it
+# over from `v3`.
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.12.0
+ labels:
+ app.kubernetes.io/instance: emissary-apiext
+ app.kubernetes.io/managed-by: kubectl_apply_-f_emissary-apiext.yaml
+ app.kubernetes.io/name: emissary-apiext
+ app.kubernetes.io/part-of: emissary-apiext
+ name: mappings.getambassador.io
+spec:
+ conversion:
+ strategy: Webhook
+ webhook:
+ clientConfig:
+ service:
+ name: emissary-apiext
+ namespace: emissary-system
+ conversionReviewVersions:
+ - v1
+ group: getambassador.io
+ names:
+ categories:
+ - ambassador-crds
+ kind: Mapping
+ listKind: MappingList
+ plural: mappings
+ singular: mapping
+ preserveUnknownFields: false
+ scope: Namespaced
+ versions:
+ - additionalPrinterColumns:
+ - jsonPath: .spec.host
+ name: Source Host
+ type: string
+ - jsonPath: .spec.prefix
+ name: Source Prefix
+ type: string
+ - jsonPath: .spec.service
+ name: Dest Service
+ type: string
+ - jsonPath: .status.state
+ name: State
+ type: string
+ - jsonPath: .status.reason
+ name: Reason
+ type: string
+ name: v1
+ schema:
+ openAPIV3Schema:
+ description: Mapping is the Schema for the mappings API
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation
+ of an object. Servers should convert recognized schemas to the latest
+ internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this
+ object represents. Servers may infer this from the endpoint the client
+ submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: MappingSpec defines the desired state of Mapping
+ properties:
+ add_linkerd_headers:
+ type: boolean
+ add_request_headers:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ add_response_headers:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ allow_upgrade:
+ description: "A case-insensitive list of the non-HTTP protocols to
+ allow \"upgrading\" to from HTTP via the \"Connection: upgrade\"
+ mechanism[1]. After the upgrade, Ambassador does not interpret
+ the traffic, and behaves similarly to how it does for TCPMappings.
+ \n [1]: https://tools.ietf.org/html/rfc7230#section-6.7 \n For example,
+ if your upstream service supports WebSockets, you would write \n
+ allow_upgrade: - websocket \n Or if your upstream service supports
+ upgrading from HTTP to SPDY (as the Kubernetes apiserver does for
+ `kubectl exec` functionality), you would write \n allow_upgrade:
+ - spdy/3.1"
+ items:
+ type: string
+ type: array
+ auth_context_extensions:
+ additionalProperties:
+ type: string
+ type: object
+ auto_host_rewrite:
+ type: boolean
+ bypass_auth:
+ type: boolean
+ bypass_error_response_overrides:
+ description: If true, bypasses any `error_response_overrides` set
+ on the Ambassador module.
+ type: boolean
+ case_sensitive:
+ type: boolean
+ circuit_breakers:
+ items:
+ properties:
+ max_connections:
+ type: integer
+ max_pending_requests:
+ type: integer
+ max_requests:
+ type: integer
+ max_retries:
+ type: integer
+ priority:
+ enum:
+ - default
+ - high
+ type: string
+ type: object
+ type: array
+ cluster_idle_timeout_ms:
+ type: integer
+ cluster_max_connection_lifetime_ms:
+ type: integer
+ cluster_tag:
+ type: string
+ connect_timeout_ms:
+ type: integer
+ cors:
+ properties:
+ credentials:
+ type: boolean
+ max_age:
+ type: string
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ dns_type:
+ type: string
+ docs:
+ description: DocsInfo provides some extra information about the docs
+ for the Mapping (used by the Dev Portal)
+ properties:
+ display_name:
+ type: string
+ ignored:
+ type: boolean
+ path:
+ type: string
+ timeout_ms:
+ type: integer
+ url:
+ type: string
+ type: object
+ enable_ipv4:
+ type: boolean
+ enable_ipv6:
+ type: boolean
+ envoy_override:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ error_response_overrides:
+ description: Error response overrides for this Mapping. Replaces all
+ of the `error_response_overrides` set on the Ambassador module,
+ if any.
+ items:
+ description: A response rewrite for an HTTP error response
+ properties:
+ body:
+ description: The new response body
+ properties:
+ content_type:
+ description: The content type to set on the error response
+ body when using text_format or text_format_source. Defaults
+ to 'text/plain'.
+ type: string
+ json_format:
+ additionalProperties:
+ type: string
+ description: 'A JSON response with content-type: application/json.
+ The values can contain format text like in text_format.'
+ type: object
+ text_format:
+ description: A format string representing a text response
+ body. Content-Type can be set using the `content_type`
+ field below.
+ type: string
+ text_format_source:
+ description: A format string sourced from a file on the
+ Ambassador container. Useful for larger response bodies
+ that should not be placed inline in configuration.
+ properties:
+ filename:
+ description: The name of a file on the Ambassador pod
+ that contains a format text string.
+ type: string
+ type: object
+ type: object
+ on_status_code:
+ description: The status code to match on -- not a pointer because
+ it's required.
+ maximum: 599
+ minimum: 400
+ type: integer
+ required:
+ - body
+ - on_status_code
+ type: object
+ minItems: 1
+ type: array
+ grpc:
+ type: boolean
+ headers:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ host:
+ type: string
+ host_redirect:
+ type: boolean
+ host_regex:
+ type: boolean
+ host_rewrite:
+ type: string
+ idle_timeout_ms:
+ type: integer
+ keepalive:
+ properties:
+ idle_time:
+ type: integer
+ interval:
+ type: integer
+ probes:
+ type: integer
+ type: object
+ labels:
+ additionalProperties:
+ description: A MappingLabelGroupsArray is an array of MappingLabelGroups.
+ I know, complex.
+ items:
+ description: 'A MappingLabelGroup is a single element of a MappingLabelGroupsArray:
+ a second map, where the key is a human-readable name that identifies
+ the group.'
+ maxProperties: 1
+ minProperties: 1
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ type: array
+ description: A DomainMap is the overall Mapping.spec.Labels type.
+ It maps domains (kind of like namespaces for Mapping labels) to
+ arrays of label groups.
+ type: object
+ load_balancer:
+ properties:
+ cookie:
+ properties:
+ name:
+ type: string
+ path:
+ type: string
+ ttl:
+ type: string
+ required:
+ - name
+ type: object
+ header:
+ type: string
+ policy:
+ enum:
+ - round_robin
+ - ring_hash
+ - maglev
+ - least_request
+ type: string
+ source_ip:
+ type: boolean
+ required:
+ - policy
+ type: object
+ method:
+ type: string
+ method_regex:
+ type: boolean
+ modules:
+ items:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ type: array
+ outlier_detection:
+ type: string
+ path_redirect:
+ description: Path replacement to use when generating an HTTP redirect.
+ Used with `host_redirect`.
+ type: string
+ precedence:
+ type: integer
+ prefix:
+ type: string
+ prefix_exact:
+ type: boolean
+ prefix_redirect:
+ description: Prefix rewrite to use when generating an HTTP redirect.
+ Used with `host_redirect`.
+ type: string
+ prefix_regex:
+ type: boolean
+ priority:
+ type: string
+ query_parameters:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ redirect_response_code:
+ description: The response code to use when generating an HTTP redirect.
+ Defaults to 301. Used with `host_redirect`.
+ enum:
+ - 301
+ - 302
+ - 303
+ - 307
+ - 308
+ type: integer
+ regex_headers:
+ additionalProperties:
+ type: string
+ type: object
+ regex_query_parameters:
+ additionalProperties:
+ type: string
+ type: object
+ regex_redirect:
+ description: Prefix regex rewrite to use when generating an HTTP redirect.
+ Used with `host_redirect`.
+ properties:
+ pattern:
+ type: string
+ substitution:
+ type: string
+ type: object
+ regex_rewrite:
+ properties:
+ pattern:
+ type: string
+ substitution:
+ type: string
+ type: object
+ resolver:
+ type: string
+ respect_dns_ttl:
+ type: boolean
+ retry_policy:
+ properties:
+ num_retries:
+ type: integer
+ per_try_timeout:
+ type: string
+ retry_on:
+ enum:
+ - 5xx
+ - gateway-error
+ - connect-failure
+ - retriable-4xx
+ - refused-stream
+ - retriable-status-codes
+ type: string
+ type: object
+ rewrite:
+ type: string
+ service:
+ type: string
+ shadow:
+ type: boolean
+ timeout_ms:
+ description: The timeout for requests that use this Mapping. Overrides
+ `cluster_request_timeout_ms` set on the Ambassador Module, if it
+ exists.
+ type: integer
+ use_websocket:
+ description: 'use_websocket is deprecated, and is equivlaent to setting
+ `allow_upgrade: ["websocket"]`'
+ type: boolean
+ v3StatsName:
+ type: string
+ v3health_checks:
+ items:
+ description: HealthCheck specifies settings for performing active
+ health checking on upstreams
+ properties:
+ health_check:
+ description: Configuration for where the healthcheck request
+ should be made to
+ maxProperties: 1
+ minProperties: 1
+ properties:
+ grpc:
+ description: HealthCheck for gRPC upstreams. Only one of
+ grpc_health_check or http_health_check may be specified
+ properties:
+ authority:
+ description: The value of the :authority header in the
+ gRPC health check request. If left empty the upstream
+ name will be used.
+ type: string
+ upstream_name:
+ description: The upstream name parameter which will
+ be sent to gRPC service in the health check message
+ type: string
+ required:
+ - upstream_name
+ type: object
+ http:
+ description: HealthCheck for HTTP upstreams. Only one of
+ http_health_check or grpc_health_check may be specified
+ properties:
+ add_request_headers:
+ additionalProperties:
+ properties:
+ append:
+ type: boolean
+ v2Representation:
+ enum:
+ - ""
+ - string
+ - "null"
+ type: string
+ value:
+ type: string
+ type: object
+ type: object
+ expected_statuses:
+ items:
+ description: A range of response statuses from Start
+ to End inclusive
+ properties:
+ max:
+ description: End of the statuses to include. Must
+ be between 100 and 599 (inclusive)
+ maximum: 599
+ minimum: 100
+ type: integer
+ min:
+ description: Start of the statuses to include.
+ Must be between 100 and 599 (inclusive)
+ maximum: 599
+ minimum: 100
+ type: integer
+ required:
+ - max
+ - min
+ type: object
+ type: array
+ hostname:
+ type: string
+ path:
+ type: string
+ remove_request_headers:
+ items:
+ type: string
+ type: array
+ required:
+ - path
+ type: object
+ type: object
+ healthy_threshold:
+ description: Number of expected responses for the upstream to
+ be considered healthy. Defaults to 1.
+ type: integer
+ interval:
+ description: Interval between health checks. Defaults to every
+ 5 seconds.
+ type: string
+ timeout:
+ description: Timeout for connecting to the health checking endpoint.
+ Defaults to 3 seconds.
+ type: string
+ unhealthy_threshold:
+ description: Number of non-expected responses for the upstream
+ to be considered unhealthy. A single 503 will mark the upstream
+ as unhealthy regardless of the threshold. Defaults to 2.
+ type: integer
+ required:
+ - health_check
+ type: object
+ minItems: 1
+ type: array
+ weight:
+ type: integer
+ required:
+ - prefix
+ - service
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ status:
+ description: MappingStatus defines the observed state of Mapping
+ properties:
+ reason:
+ type: string
+ state:
+ enum:
+ - ""
+ - Inactive
+ - Running
+ type: string
+ type: object
+ type: object
+ served: true
+ storage: false
+ subresources:
+ status: {}
+ - additionalPrinterColumns:
+ - jsonPath: .spec.host
+ name: Source Host
+ type: string
+ - jsonPath: .spec.prefix
+ name: Source Prefix
+ type: string
+ - jsonPath: .spec.service
+ name: Dest Service
+ type: string
+ - jsonPath: .status.state
+ name: State
+ type: string
+ - jsonPath: .status.reason
+ name: Reason
+ type: string
+ name: v2
+ schema:
+ openAPIV3Schema:
+ description: Mapping is the Schema for the mappings API
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation
+ of an object. Servers should convert recognized schemas to the latest
+ internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this
+ object represents. Servers may infer this from the endpoint the client
+ submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: MappingSpec defines the desired state of Mapping
+ properties:
+ add_linkerd_headers:
+ type: boolean
+ add_request_headers:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ add_response_headers:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ allow_upgrade:
+ description: "A case-insensitive list of the non-HTTP protocols to
+ allow \"upgrading\" to from HTTP via the \"Connection: upgrade\"
+ mechanism[1]. After the upgrade, Ambassador does not interpret
+ the traffic, and behaves similarly to how it does for TCPMappings.
+ \n [1]: https://tools.ietf.org/html/rfc7230#section-6.7 \n For example,
+ if your upstream service supports WebSockets, you would write \n
+ allow_upgrade: - websocket \n Or if your upstream service supports
+ upgrading from HTTP to SPDY (as the Kubernetes apiserver does for
+ `kubectl exec` functionality), you would write \n allow_upgrade:
+ - spdy/3.1"
+ items:
+ type: string
+ type: array
+ # [operator] added manually by coping over from v3alpha1
+ ambassador_id:
+ description: "AmbassadorID declares which Ambassador instances should
+ pay attention to this resource. If no value is provided, the default
+ is: \n ambassador_id: - \"default\""
+ items:
+ type: string
+ type: array
+ auth_context_extensions:
+ additionalProperties:
+ type: string
+ type: object
+ auto_host_rewrite:
+ type: boolean
+ bypass_auth:
+ type: boolean
+ bypass_error_response_overrides:
+ description: If true, bypasses any `error_response_overrides` set
+ on the Ambassador module.
+ type: boolean
+ case_sensitive:
+ type: boolean
+ circuit_breakers:
+ items:
+ properties:
+ max_connections:
+ type: integer
+ max_pending_requests:
+ type: integer
+ max_requests:
+ type: integer
+ max_retries:
+ type: integer
+ priority:
+ enum:
+ - default
+ - high
+ type: string
+ type: object
+ type: array
+ cluster_idle_timeout_ms:
+ type: integer
+ cluster_max_connection_lifetime_ms:
+ type: integer
+ cluster_tag:
+ type: string
+ connect_timeout_ms:
+ type: integer
+ cors:
+ properties:
+ credentials:
+ type: boolean
+ max_age:
+ type: string
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ dns_type:
+ type: string
+ docs:
+ description: DocsInfo provides some extra information about the docs
+ for the Mapping (used by the Dev Portal)
+ properties:
+ display_name:
+ type: string
+ ignored:
+ type: boolean
+ path:
+ type: string
+ timeout_ms:
+ type: integer
+ url:
+ type: string
+ type: object
+ enable_ipv4:
+ type: boolean
+ enable_ipv6:
+ type: boolean
+ envoy_override:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ error_response_overrides:
+ description: Error response overrides for this Mapping. Replaces all
+ of the `error_response_overrides` set on the Ambassador module,
+ if any.
+ items:
+ description: A response rewrite for an HTTP error response
+ properties:
+ body:
+ description: The new response body
+ properties:
+ content_type:
+ description: The content type to set on the error response
+ body when using text_format or text_format_source. Defaults
+ to 'text/plain'.
+ type: string
+ json_format:
+ additionalProperties:
+ type: string
+ description: 'A JSON response with content-type: application/json.
+ The values can contain format text like in text_format.'
+ type: object
+ text_format:
+ description: A format string representing a text response
+ body. Content-Type can be set using the `content_type`
+ field below.
+ type: string
+ text_format_source:
+ description: A format string sourced from a file on the
+ Ambassador container. Useful for larger response bodies
+ that should not be placed inline in configuration.
+ properties:
+ filename:
+ description: The name of a file on the Ambassador pod
+ that contains a format text string.
+ type: string
+ type: object
+ type: object
+ on_status_code:
+ description: The status code to match on -- not a pointer because
+ it's required.
+ maximum: 599
+ minimum: 400
+ type: integer
+ required:
+ - body
+ - on_status_code
+ type: object
+ minItems: 1
+ type: array
+ grpc:
+ type: boolean
+ headers:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ host:
+ type: string
+ host_redirect:
+ type: boolean
+ host_regex:
+ type: boolean
+ host_rewrite:
+ type: string
+ idle_timeout_ms:
+ type: integer
+ keepalive:
+ properties:
+ idle_time:
+ type: integer
+ interval:
+ type: integer
+ probes:
+ type: integer
+ type: object
+ labels:
+ additionalProperties:
+ description: A MappingLabelGroupsArray is an array of MappingLabelGroups.
+ I know, complex.
+ items:
+ description: 'A MappingLabelGroup is a single element of a MappingLabelGroupsArray:
+ a second map, where the key is a human-readable name that identifies
+ the group.'
+ maxProperties: 1
+ minProperties: 1
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ type: array
+ description: A DomainMap is the overall Mapping.spec.Labels type.
+ It maps domains (kind of like namespaces for Mapping labels) to
+ arrays of label groups.
+ type: object
+ load_balancer:
+ properties:
+ cookie:
+ properties:
+ name:
+ type: string
+ path:
+ type: string
+ ttl:
+ type: string
+ required:
+ - name
+ type: object
+ header:
+ type: string
+ policy:
+ enum:
+ - round_robin
+ - ring_hash
+ - maglev
+ - least_request
+ type: string
+ source_ip:
+ type: boolean
+ required:
+ - policy
+ type: object
+ method:
+ type: string
+ method_regex:
+ type: boolean
+ modules:
+ items:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ type: array
+ outlier_detection:
+ type: string
+ path_redirect:
+ description: Path replacement to use when generating an HTTP redirect.
+ Used with `host_redirect`.
+ type: string
+ precedence:
+ type: integer
+ prefix:
+ type: string
+ prefix_exact:
+ type: boolean
+ prefix_redirect:
+ description: Prefix rewrite to use when generating an HTTP redirect.
+ Used with `host_redirect`.
+ type: string
+ prefix_regex:
+ type: boolean
+ priority:
+ type: string
+ query_parameters:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ redirect_response_code:
+ description: The response code to use when generating an HTTP redirect.
+ Defaults to 301. Used with `host_redirect`.
+ enum:
+ - 301
+ - 302
+ - 303
+ - 307
+ - 308
+ type: integer
+ regex_headers:
+ additionalProperties:
+ type: string
+ type: object
+ regex_query_parameters:
+ additionalProperties:
+ type: string
+ type: object
+ regex_redirect:
+ description: Prefix regex rewrite to use when generating an HTTP redirect.
+ Used with `host_redirect`.
+ properties:
+ pattern:
+ type: string
+ substitution:
+ type: string
+ type: object
+ regex_rewrite:
+ properties:
+ pattern:
+ type: string
+ substitution:
+ type: string
+ type: object
+ resolver:
+ type: string
+ respect_dns_ttl:
+ type: boolean
+ retry_policy:
+ properties:
+ num_retries:
+ type: integer
+ per_try_timeout:
+ type: string
+ retry_on:
+ enum:
+ - 5xx
+ - gateway-error
+ - connect-failure
+ - retriable-4xx
+ - refused-stream
+ - retriable-status-codes
+ type: string
+ type: object
+ rewrite:
+ type: string
+ service:
+ type: string
+ shadow:
+ type: boolean
+ timeout_ms:
+ description: The timeout for requests that use this Mapping. Overrides
+ `cluster_request_timeout_ms` set on the Ambassador Module, if it
+ exists.
+ type: integer
+ use_websocket:
+ description: 'use_websocket is deprecated, and is equivlaent to setting
+ `allow_upgrade: ["websocket"]`'
+ type: boolean
+ v3StatsName:
+ type: string
+ v3health_checks:
+ items:
+ description: HealthCheck specifies settings for performing active
+ health checking on upstreams
+ properties:
+ health_check:
+ description: Configuration for where the healthcheck request
+ should be made to
+ maxProperties: 1
+ minProperties: 1
+ properties:
+ grpc:
+ description: HealthCheck for gRPC upstreams. Only one of
+ grpc_health_check or http_health_check may be specified
+ properties:
+ authority:
+ description: The value of the :authority header in the
+ gRPC health check request. If left empty the upstream
+ name will be used.
+ type: string
+ upstream_name:
+ description: The upstream name parameter which will
+ be sent to gRPC service in the health check message
+ type: string
+ required:
+ - upstream_name
+ type: object
+ http:
+ description: HealthCheck for HTTP upstreams. Only one of
+ http_health_check or grpc_health_check may be specified
+ properties:
+ add_request_headers:
+ additionalProperties:
+ properties:
+ append:
+ type: boolean
+ v2Representation:
+ enum:
+ - ""
+ - string
+ - "null"
+ type: string
+ value:
+ type: string
+ type: object
+ type: object
+ expected_statuses:
+ items:
+ description: A range of response statuses from Start
+ to End inclusive
+ properties:
+ max:
+ description: End of the statuses to include. Must
+ be between 100 and 599 (inclusive)
+ maximum: 599
+ minimum: 100
+ type: integer
+ min:
+ description: Start of the statuses to include.
+ Must be between 100 and 599 (inclusive)
+ maximum: 599
+ minimum: 100
+ type: integer
+ required:
+ - max
+ - min
+ type: object
+ type: array
+ hostname:
+ type: string
+ path:
+ type: string
+ remove_request_headers:
+ items:
+ type: string
+ type: array
+ required:
+ - path
+ type: object
+ type: object
+ healthy_threshold:
+ description: Number of expected responses for the upstream to
+ be considered healthy. Defaults to 1.
+ type: integer
+ interval:
+ description: Interval between health checks. Defaults to every
+ 5 seconds.
+ type: string
+ timeout:
+ description: Timeout for connecting to the health checking endpoint.
+ Defaults to 3 seconds.
+ type: string
+ unhealthy_threshold:
+ description: Number of non-expected responses for the upstream
+ to be considered unhealthy. A single 503 will mark the upstream
+ as unhealthy regardless of the threshold. Defaults to 2.
+ type: integer
+ required:
+ - health_check
+ type: object
+ minItems: 1
+ type: array
+ weight:
+ type: integer
+ required:
+ - prefix
+ - service
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ status:
+ description: MappingStatus defines the observed state of Mapping
+ properties:
+ reason:
+ type: string
+ state:
+ enum:
+ - ""
+ - Inactive
+ - Running
+ type: string
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+ - additionalPrinterColumns:
+ - jsonPath: .spec.host
+ name: Source Host
+ type: string
+ - jsonPath: .spec.prefix
+ name: Source Prefix
+ type: string
+ - jsonPath: .spec.service
+ name: Dest Service
+ type: string
+ - jsonPath: .status.state
+ name: State
+ type: string
+ - jsonPath: .status.reason
+ name: Reason
+ type: string
+ name: v3alpha1
+ schema:
+ openAPIV3Schema:
+ description: Mapping is the Schema for the mappings API
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation
+ of an object. Servers should convert recognized schemas to the latest
+ internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this
+ object represents. Servers may infer this from the endpoint the client
+ submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: MappingSpec defines the desired state of Mapping
+ properties:
+ add_linkerd_headers:
+ type: boolean
+ add_request_headers:
+ additionalProperties:
+ properties:
+ append:
+ type: boolean
+ v2Representation:
+ enum:
+ - ""
+ - string
+ - "null"
+ type: string
+ value:
+ type: string
+ type: object
+ type: object
+ add_response_headers:
+ additionalProperties:
+ properties:
+ append:
+ type: boolean
+ v2Representation:
+ enum:
+ - ""
+ - string
+ - "null"
+ type: string
+ value:
+ type: string
+ type: object
+ type: object
+ allow_upgrade:
+ description: "A case-insensitive list of the non-HTTP protocols to
+ allow \"upgrading\" to from HTTP via the \"Connection: upgrade\"
+ mechanism[1]. After the upgrade, Ambassador does not interpret
+ the traffic, and behaves similarly to how it does for TCPMappings.
+ \n [1]: https://tools.ietf.org/html/rfc7230#section-6.7 \n For example,
+ if your upstream service supports WebSockets, you would write \n
+ allow_upgrade: - websocket \n Or if your upstream service supports
+ upgrading from HTTP to SPDY (as the Kubernetes apiserver does for
+ `kubectl exec` functionality), you would write \n allow_upgrade:
+ - spdy/3.1"
+ items:
+ type: string
+ type: array
+ ambassador_id:
+ description: "AmbassadorID declares which Ambassador instances should
+ pay attention to this resource. If no value is provided, the default
+ is: \n ambassador_id: - \"default\""
+ items:
+ type: string
+ type: array
+ auth_context_extensions:
+ additionalProperties:
+ type: string
+ type: object
+ auto_host_rewrite:
+ type: boolean
+ bypass_auth:
+ type: boolean
+ bypass_error_response_overrides:
+ description: If true, bypasses any `error_response_overrides` set
+ on the Ambassador module.
+ type: boolean
+ case_sensitive:
+ type: boolean
+ circuit_breakers:
+ items:
+ properties:
+ max_connections:
+ type: integer
+ max_pending_requests:
+ type: integer
+ max_requests:
+ type: integer
+ max_retries:
+ type: integer
+ priority:
+ enum:
+ - default
+ - high
+ type: string
+ type: object
+ type: array
+ cluster_idle_timeout_ms:
+ type: integer
+ cluster_max_connection_lifetime_ms:
+ type: integer
+ cluster_tag:
+ type: string
+ connect_timeout_ms:
+ type: integer
+ cors:
+ properties:
+ credentials:
+ type: boolean
+ exposed_headers:
+ items:
+ type: string
+ type: array
+ headers:
+ items:
+ type: string
+ type: array
+ max_age:
+ type: string
+ methods:
+ items:
+ type: string
+ type: array
+ origins:
+ items:
+ type: string
+ type: array
+ v2CommaSeparatedOrigins:
+ type: boolean
+ type: object
+ dns_type:
+ type: string
+ docs:
+ description: DocsInfo provides some extra information about the docs
+ for the Mapping. Docs is used by both the agent and the DevPortal.
+ properties:
+ display_name:
+ type: string
+ ignored:
+ type: boolean
+ path:
+ type: string
+ timeout_ms:
+ type: integer
+ url:
+ type: string
+ type: object
+ enable_ipv4:
+ type: boolean
+ enable_ipv6:
+ type: boolean
+ envoy_override:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ error_response_overrides:
+ description: Error response overrides for this Mapping. Replaces all
+ of the `error_response_overrides` set on the Ambassador module,
+ if any.
+ items:
+ description: A response rewrite for an HTTP error response
+ properties:
+ body:
+ description: The new response body
+ properties:
+ content_type:
+ description: The content type to set on the error response
+ body when using text_format or text_format_source. Defaults
+ to 'text/plain'.
+ type: string
+ json_format:
+ additionalProperties:
+ type: string
+ description: 'A JSON response with content-type: application/json.
+ The values can contain format text like in text_format.'
+ type: object
+ text_format:
+ description: A format string representing a text response
+ body. Content-Type can be set using the `content_type`
+ field below.
+ type: string
+ text_format_source:
+ description: A format string sourced from a file on the
+ Ambassador container. Useful for larger response bodies
+ that should not be placed inline in configuration.
+ properties:
+ filename:
+ description: The name of a file on the Ambassador pod
+ that contains a format text string.
+ type: string
+ type: object
+ type: object
+ on_status_code:
+ description: The status code to match on -- not a pointer because
+ it's required.
+ maximum: 599
+ minimum: 400
+ type: integer
+ required:
+ - body
+ - on_status_code
+ type: object
+ minItems: 1
+ type: array
+ grpc:
+ type: boolean
+ headers:
+ additionalProperties:
+ type: string
+ type: object
+ health_checks:
+ items:
+ description: HealthCheck specifies settings for performing active
+ health checking on upstreams
+ properties:
+ health_check:
+ description: Configuration for where the healthcheck request
+ should be made to
+ maxProperties: 1
+ minProperties: 1
+ properties:
+ grpc:
+ description: HealthCheck for gRPC upstreams. Only one of
+ grpc_health_check or http_health_check may be specified
+ properties:
+ authority:
+ description: The value of the :authority header in the
+ gRPC health check request. If left empty the upstream
+ name will be used.
+ type: string
+ upstream_name:
+ description: The upstream name parameter which will
+ be sent to gRPC service in the health check message
+ type: string
+ required:
+ - upstream_name
+ type: object
+ http:
+ description: HealthCheck for HTTP upstreams. Only one of
+ http_health_check or grpc_health_check may be specified
+ properties:
+ add_request_headers:
+ additionalProperties:
+ properties:
+ append:
+ type: boolean
+ v2Representation:
+ enum:
+ - ""
+ - string
+ - "null"
+ type: string
+ value:
+ type: string
+ type: object
+ type: object
+ expected_statuses:
+ items:
+ description: A range of response statuses from Start
+ to End inclusive
+ properties:
+ max:
+ description: End of the statuses to include. Must
+ be between 100 and 599 (inclusive)
+ maximum: 599
+ minimum: 100
+ type: integer
+ min:
+ description: Start of the statuses to include.
+ Must be between 100 and 599 (inclusive)
+ maximum: 599
+ minimum: 100
+ type: integer
+ required:
+ - max
+ - min
+ type: object
+ type: array
+ hostname:
+ type: string
+ path:
+ type: string
+ remove_request_headers:
+ items:
+ type: string
+ type: array
+ required:
+ - path
+ type: object
+ type: object
+ healthy_threshold:
+ description: Number of expected responses for the upstream to
+ be considered healthy. Defaults to 1.
+ type: integer
+ interval:
+ description: Interval between health checks. Defaults to every
+ 5 seconds.
+ type: string
+ timeout:
+ description: Timeout for connecting to the health checking endpoint.
+ Defaults to 3 seconds.
+ type: string
+ unhealthy_threshold:
+ description: Number of non-expected responses for the upstream
+ to be considered unhealthy. A single 503 will mark the upstream
+ as unhealthy regardless of the threshold. Defaults to 2.
+ type: integer
+ required:
+ - health_check
+ type: object
+ minItems: 1
+ type: array
+ host:
+ description: "Exact match for the hostname of a request if HostRegex
+ is false; regex match for the hostname if HostRegex is true. \n
+ Host specifies both a match for the ':authority' header of a request,
+ as well as a match criterion for Host CRDs: a Mapping that specifies
+ Host will not associate with a Host that doesn't have a matching
+ Hostname. \n If both Host and Hostname are set, an error is logged,
+ Host is ignored, and Hostname is used. \n DEPRECATED: Host is either
+ an exact match or a regex, depending on HostRegex. Use HostName
+ instead."
+ type: string
+ host_redirect:
+ type: boolean
+ host_regex:
+ description: 'DEPRECATED: Host is either an exact match or a regex,
+ depending on HostRegex. Use HostName instead.'
+ type: boolean
+ host_rewrite:
+ type: string
+ hostname:
+ description: "Hostname is a DNS glob specifying the hosts to which
+ this Mapping applies. \n Hostname specifies both a match for the
+ ':authority' header of a request, as well as a match criterion for
+ Host CRDs: a Mapping that specifies Hostname will not associate
+ with a Host that doesn't have a matching Hostname. \n If both Host
+ and Hostname are set, an error is logged, Host is ignored, and Hostname
+ is used."
+ type: string
+ idle_timeout_ms:
+ type: integer
+ keepalive:
+ properties:
+ idle_time:
+ type: integer
+ interval:
+ type: integer
+ probes:
+ type: integer
+ type: object
+ labels:
+ additionalProperties:
+ description: A MappingLabelGroupsArray is an array of MappingLabelGroups.
+ I know, complex.
+ items:
+ additionalProperties:
+ description: 'A MappingLabelsArray is the value in the MappingLabelGroup:
+ an array of label specifiers.'
+ items:
+ description: "A MappingLabelSpecifier (finally!) defines a
+ single label. \n This mimics envoy/config/route/v3/route_components.proto:RateLimit:Action:action_specifier."
+ maxProperties: 1
+ minProperties: 1
+ properties:
+ destination_cluster:
+ description: Sets the label "destination_cluster=«Envoy
+ destination cluster name»".
+ properties:
+ key:
+ enum:
+ - destination_cluster
+ type: string
+ required:
+ - key
+ type: object
+ generic_key:
+ description: Sets the label "«key»=«value»" (where by
+ default «key» is "generic_key").
+ properties:
+ key:
+ description: The default is "generic_key".
+ type: string
+ v2Shorthand:
+ type: boolean
+ value:
+ type: string
+ required:
+ - value
+ type: object
+ remote_address:
+ description: Sets the label "remote_address=«IP address
+ of the client»".
+ properties:
+ key:
+ enum:
+ - remote_address
+ type: string
+ required:
+ - key
+ type: object
+ request_headers:
+ description: If the «header_name» header is set, then
+ set the label "«key»=«Value of the «header_name» header»";
+ otherwise skip applying this label group.
+ properties:
+ header_name:
+ type: string
+ key:
+ type: string
+ omit_if_not_present:
+ type: boolean
+ required:
+ - header_name
+ - key
+ type: object
+ source_cluster:
+ description: Sets the label "source_cluster=«Envoy source
+ cluster name»".
+ properties:
+ key:
+ enum:
+ - source_cluster
+ type: string
+ required:
+ - key
+ type: object
+ type: object
+ type: array
+ description: 'A MappingLabelGroup is a single element of a MappingLabelGroupsArray:
+ a second map, where the key is a human-readable name that identifies
+ the group.'
+ maxProperties: 1
+ minProperties: 1
+ type: object
+ type: array
+ description: A DomainMap is the overall Mapping.spec.Labels type.
+ It maps domains (kind of like namespaces for Mapping labels) to
+ arrays of label groups.
+ type: object
+ load_balancer:
+ properties:
+ cookie:
+ properties:
+ name:
+ type: string
+ path:
+ type: string
+ ttl:
+ type: string
+ required:
+ - name
+ type: object
+ header:
+ type: string
+ policy:
+ enum:
+ - round_robin
+ - ring_hash
+ - maglev
+ - least_request
+ type: string
+ source_ip:
+ type: boolean
+ required:
+ - policy
+ type: object
+ method:
+ type: string
+ method_regex:
+ type: boolean
+ modules:
+ items:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ type: array
+ outlier_detection:
+ type: string
+ path_redirect:
+ description: Path replacement to use when generating an HTTP redirect.
+ Used with `host_redirect`.
+ type: string
+ precedence:
+ type: integer
+ prefix:
+ type: string
+ prefix_exact:
+ type: boolean
+ prefix_redirect:
+ description: Prefix rewrite to use when generating an HTTP redirect.
+ Used with `host_redirect`.
+ type: string
+ prefix_regex:
+ type: boolean
+ priority:
+ type: string
+ query_parameters:
+ additionalProperties:
+ type: string
+ type: object
+ redirect_response_code:
+ description: The response code to use when generating an HTTP redirect.
+ Defaults to 301. Used with `host_redirect`.
+ enum:
+ - 301
+ - 302
+ - 303
+ - 307
+ - 308
+ type: integer
+ regex_headers:
+ additionalProperties:
+ type: string
+ type: object
+ regex_query_parameters:
+ additionalProperties:
+ type: string
+ type: object
+ regex_redirect:
+ description: Prefix regex rewrite to use when generating an HTTP redirect.
+ Used with `host_redirect`.
+ properties:
+ pattern:
+ type: string
+ substitution:
+ type: string
+ type: object
+ regex_rewrite:
+ properties:
+ pattern:
+ type: string
+ substitution:
+ type: string
+ type: object
+ remove_request_headers:
+ items:
+ type: string
+ type: array
+ remove_response_headers:
+ items:
+ type: string
+ type: array
+ resolver:
+ type: string
+ respect_dns_ttl:
+ type: boolean
+ retry_policy:
+ properties:
+ num_retries:
+ type: integer
+ per_try_timeout:
+ type: string
+ retry_on:
+ enum:
+ - 5xx
+ - gateway-error
+ - connect-failure
+ - retriable-4xx
+ - refused-stream
+ - retriable-status-codes
+ type: string
+ type: object
+ rewrite:
+ type: string
+ service:
+ type: string
+ shadow:
+ type: boolean
+ stats_name:
+ type: string
+ timeout_ms:
+ description: The timeout for requests that use this Mapping. Overrides
+ `cluster_request_timeout_ms` set on the Ambassador Module, if it
+ exists.
+ type: integer
+ tls:
+ type: string
+ use_websocket:
+ description: 'use_websocket is deprecated, and is equivlaent to setting
+ `allow_upgrade: ["websocket"]`'
+ type: boolean
+ v2BoolHeaders:
+ items:
+ type: string
+ type: array
+ v2BoolQueryParameters:
+ items:
+ type: string
+ type: array
+ # TODO: uncomment when [bug](https://github.com/fabric8io/kubernetes-client/issues/5457) is resolved
+ # v2ExplicitTLS:
+ # description: V2ExplicitTLS controls some vanity/stylistic elements
+ # when converting from v3alpha1 to v2. The values in an V2ExplicitTLS
+ # should not in any way affect the runtime operation of Emissary;
+ # except that it may affect internal names in the Envoy config, which
+ # may in turn affect stats names. But it should not affect any end-user
+ # observable behavior.
+ # properties:
+ # serviceScheme:
+ # description: "ServiceScheme specifies how to spell and capitalize
+ # the scheme-part of the service URL. \n Acceptable values are
+ # \"http://\" (case-insensitive), \"https://\" (case-insensitive),
+ # or \"\". The value is used if it agrees with whether or not
+ # this resource enables TLS origination, or if something else
+ # in the resource overrides the scheme."
+ # pattern: ^([hH][tT][tT][pP][sS]?://)?$
+ # type: string
+ # tls:
+ # description: "TLS controls whether and how to represent the \"tls\"
+ # field when its value could be implied by the \"service\" field.
+ # \ In v2, there were a lot of different ways to spell an \"empty\"
+ # value, and this field specifies which way to spell it (and will
+ # therefore only be used if the value will indeed be empty). \n
+ # | Value | Representation | Meaning
+ # of representation | |--------------+---------------------------------------+------------------------------------|
+ # | \"\" | omit the field | defer
+ # to service (no TLSContext) | | \"null\" | store an explicit
+ # \"null\" in the field | defer to service (no TLSContext) |
+ # | \"string\" | store an empty string in the field | defer
+ # to service (no TLSContext) | | \"bool:false\" | store a Boolean
+ # \"false\" in the field | defer to service (no TLSContext) |
+ # | \"bool:true\" | store a Boolean \"true\" in the field |
+ # originate TLS (no TLSContext) | \n If the meaning of the
+ # representation contradicts anything else (if a TLSContext is
+ # to be used, or in the case of \"bool:true\" if TLS is not to
+ # be originated), then this field is ignored."
+ # enum:
+ # - ""
+ # - "null"
+ # - bool:true
+ # - bool:false
+ # - string
+ # type: string
+ # type: object
+ weight:
+ type: integer
+ required:
+ - prefix
+ - service
+ type: object
+ status:
+ description: MappingStatus defines the observed state of Mapping
+ properties:
+ reason:
+ type: string
+ state:
+ enum:
+ - ""
+ - Inactive
+ - Running
+ type: string
+ type: object
+ type: object
+ served: true
+ storage: false
+ subresources:
+ status: {}
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.12.0
+ labels:
+ app.kubernetes.io/instance: emissary-apiext
+ app.kubernetes.io/managed-by: kubectl_apply_-f_emissary-apiext.yaml
+ app.kubernetes.io/name: emissary-apiext
+ app.kubernetes.io/part-of: emissary-apiext
+ name: tlscontexts.getambassador.io
+spec:
+ conversion:
+ strategy: Webhook
+ webhook:
+ clientConfig:
+ service:
+ name: emissary-apiext
+ namespace: emissary-system
+ conversionReviewVersions:
+ - v1
+ group: getambassador.io
+ names:
+ categories:
+ - ambassador-crds
+ kind: TLSContext
+ listKind: TLSContextList
+ plural: tlscontexts
+ singular: tlscontext
+ preserveUnknownFields: false
+ scope: Namespaced
+ versions:
+ - name: v1
+ schema:
+ openAPIV3Schema:
+ description: TLSContext is the Schema for the tlscontexts API
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation
+ of an object. Servers should convert recognized schemas to the latest
+ internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this
+ object represents. Servers may infer this from the endpoint the client
+ submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: TLSContextSpec defines the desired state of TLSContext
+ properties:
+ alpn_protocols:
+ type: string
+ ca_secret:
+ type: string
+ cacert_chain_file:
+ type: string
+ cert_chain_file:
+ type: string
+ cert_required:
+ type: boolean
+ cipher_suites:
+ items:
+ type: string
+ type: array
+ ecdh_curves:
+ items:
+ type: string
+ type: array
+ hosts:
+ items:
+ type: string
+ type: array
+ max_tls_version:
+ enum:
+ - v1.0
+ - v1.1
+ - v1.2
+ - v1.3
+ type: string
+ min_tls_version:
+ enum:
+ - v1.0
+ - v1.1
+ - v1.2
+ - v1.3
+ type: string
+ private_key_file:
+ type: string
+ redirect_cleartext_from:
+ type: integer
+ secret:
+ type: string
+ secret_namespacing:
+ type: boolean
+ sni:
+ type: string
+ v3CRLSecret:
+ type: string
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ type: object
+ served: true
+ storage: false
+ - name: v2
+ schema:
+ openAPIV3Schema:
+ description: TLSContext is the Schema for the tlscontexts API
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation
+ of an object. Servers should convert recognized schemas to the latest
+ internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this
+ object represents. Servers may infer this from the endpoint the client
+ submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: TLSContextSpec defines the desired state of TLSContext
+ properties:
+ alpn_protocols:
+ type: string
+ # [operator] added manually by coping over from v3alpha1
+ ambassador_id:
+ description: "AmbassadorID declares which Ambassador instances should
+ pay attention to this resource. If no value is provided, the default
+ is: \n ambassador_id: - \"default\""
+ items:
+ type: string
+ type: array
+ ca_secret:
+ type: string
+ cacert_chain_file:
+ type: string
+ cert_chain_file:
+ type: string
+ cert_required:
+ type: boolean
+ cipher_suites:
+ items:
+ type: string
+ type: array
+ ecdh_curves:
+ items:
+ type: string
+ type: array
+ hosts:
+ items:
+ type: string
+ type: array
+ max_tls_version:
+ enum:
+ - v1.0
+ - v1.1
+ - v1.2
+ - v1.3
+ type: string
+ min_tls_version:
+ enum:
+ - v1.0
+ - v1.1
+ - v1.2
+ - v1.3
+ type: string
+ private_key_file:
+ type: string
+ redirect_cleartext_from:
+ type: integer
+ secret:
+ type: string
+ secret_namespacing:
+ type: boolean
+ sni:
+ type: string
+ v3CRLSecret:
+ type: string
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ type: object
+ served: true
+ storage: true
+ - name: v3alpha1
+ schema:
+ openAPIV3Schema:
+ description: TLSContext is the Schema for the tlscontexts API
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation
+ of an object. Servers should convert recognized schemas to the latest
+ internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this
+ object represents. Servers may infer this from the endpoint the client
+ submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: TLSContextSpec defines the desired state of TLSContext
+ properties:
+ alpn_protocols:
+ type: string
+ ambassador_id:
+ description: "AmbassadorID declares which Ambassador instances should
+ pay attention to this resource. If no value is provided, the default
+ is: \n ambassador_id: - \"default\""
+ items:
+ type: string
+ type: array
+ ca_secret:
+ type: string
+ cacert_chain_file:
+ type: string
+ cert_chain_file:
+ type: string
+ cert_required:
+ type: boolean
+ cipher_suites:
+ items:
+ type: string
+ type: array
+ crl_secret:
+ type: string
+ ecdh_curves:
+ items:
+ type: string
+ type: array
+ hosts:
+ items:
+ type: string
+ type: array
+ max_tls_version:
+ enum:
+ - v1.0
+ - v1.1
+ - v1.2
+ - v1.3
+ type: string
+ min_tls_version:
+ enum:
+ - v1.0
+ - v1.1
+ - v1.2
+ - v1.3
+ type: string
+ private_key_file:
+ type: string
+ redirect_cleartext_from:
+ type: integer
+ secret:
+ type: string
+ secret_namespacing:
+ type: boolean
+ sni:
+ type: string
+ type: object
+ type: object
+ served: true
+ storage: false
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterAmbassadorTest.java b/operator/src/test/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterAmbassadorTest.java
new file mode 100644
index 000000000..f959eabe6
--- /dev/null
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterAmbassadorTest.java
@@ -0,0 +1,100 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.k8s.operator.network.ambassador.dependent;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import io.getambassador.v2.Mapping;
+import io.javaoperatorsdk.operator.ReconcilerUtils;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+import java.util.stream.Stream;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class GerritClusterAmbassadorTest {
+
+ @ParameterizedTest
+ @MethodSource("provideYamlManifests")
+ public void expectedGerritClusterAmbassadorComponentsCreated(
+ String inputFile, Map expectedOutputFileNames)
+ throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
+ IllegalAccessException, InvocationTargetException {
+ GerritNetwork gerritNetwork =
+ ReconcilerUtils.loadYaml(GerritNetwork.class, this.getClass(), inputFile);
+
+ for (Map.Entry entry : expectedOutputFileNames.entrySet()) {
+ String className = entry.getKey();
+ String expectedOutputFile = entry.getValue();
+
+ Class> clazz = Class.forName(className);
+ Object dependentObject = clazz.getDeclaredConstructor(new Class[] {}).newInstance();
+
+ if (dependentObject instanceof MappingDependentResourceInterface) {
+ MappingDependentResourceInterface dependent =
+ (MappingDependentResourceInterface) dependentObject;
+ Mapping result = dependent.desired(gerritNetwork, null);
+ Mapping expected =
+ ReconcilerUtils.loadYaml(Mapping.class, this.getClass(), expectedOutputFile);
+ assertThat(result.getSpec()).isEqualTo(expected.getSpec());
+ }
+ }
+ }
+
+ private static Stream provideYamlManifests() {
+ return Stream.of(
+ Arguments.of(
+ "../../gerritnetwork_primary_replica_tls.yaml",
+ Map.of(
+ GerritClusterMappingGETReplica.class.getName(),
+ "mappingGETReplica_primary_replica.yaml",
+ GerritClusterMappingPOSTReplica.class.getName(),
+ "mappingPOSTReplica_primary_replica.yaml",
+ GerritClusterMappingPrimary.class.getName(), "mappingPrimary_primary_replica.yaml"
+ // TODO: add tls
+ )),
+ Arguments.of(
+ "../../gerritnetwork_primary_replica.yaml",
+ Map.of(
+ GerritClusterMappingGETReplica.class.getName(),
+ "mappingGETReplica_primary_replica.yaml",
+ GerritClusterMappingPOSTReplica.class.getName(),
+ "mappingPOSTReplica_primary_replica.yaml",
+ GerritClusterMappingPrimary.class.getName(),
+ "mappingPrimary_primary_replica.yaml")),
+ Arguments.of(
+ "../../gerritnetwork_primary.yaml",
+ Map.of(GerritClusterMapping.class.getName(), "mapping_primary.yaml")),
+ Arguments.of(
+ "../../gerritnetwork_replica.yaml",
+ Map.of(GerritClusterMapping.class.getName(), "mapping_replica.yaml")),
+ Arguments.of(
+ "../../gerritnetwork_receiver_replica.yaml",
+ Map.of(
+ GerritClusterMapping.class.getName(), "mapping_replica.yaml",
+ GerritClusterMappingReceiver.class.getName(), "mapping_receiver.yaml",
+ GerritClusterMappingReceiverGET.class.getName(), "mappingGET_receiver.yaml")),
+ Arguments.of(
+ "../../gerritnetwork_receiver_replica_tls.yaml",
+ Map.of(
+ GerritClusterMapping.class.getName(), "mapping_replica.yaml",
+ GerritClusterMappingReceiver.class.getName(), "mapping_receiver.yaml",
+ GerritClusterMappingReceiverGET.class.getName(), "mappingGET_receiver.yaml"
+ // TODO: add tls
+ )));
+ }
+}
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mappingGETReplica_primary_replica.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mappingGETReplica_primary_replica.yaml
new file mode 100644
index 000000000..f2d81278b
--- /dev/null
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mappingGETReplica_primary_replica.yaml
@@ -0,0 +1,15 @@
+apiVersion: getambassador.io/v2
+kind: Mapping
+metadata:
+ name: gerrit-mapping-get-replica
+ namespace: gerrit
+spec:
+ bypass_auth: true
+ rewrite: ""
+ host: example.com
+ method: GET
+ prefix: /.*/info/refs
+ prefix_regex: true
+ query_parameters:
+ service: git-upload-pack
+ service: replica:48080
\ No newline at end of file
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mappingGET_receiver.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mappingGET_receiver.yaml
new file mode 100644
index 000000000..c21948613
--- /dev/null
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mappingGET_receiver.yaml
@@ -0,0 +1,15 @@
+apiVersion: getambassador.io/v2
+kind: Mapping
+metadata:
+ name: gerrit-mapping-receiver-get
+ namespace: gerrit
+spec:
+ bypass_auth: true
+ rewrite: ""
+ host: example.com
+ method: GET
+ prefix: /.*/info/refs
+ prefix_regex: true
+ query_parameters:
+ service: git-receive-pack
+ service: receiver:48081
\ No newline at end of file
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mappingPOSTReplica_primary_replica.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mappingPOSTReplica_primary_replica.yaml
new file mode 100644
index 000000000..7ebbaefe5
--- /dev/null
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mappingPOSTReplica_primary_replica.yaml
@@ -0,0 +1,13 @@
+apiVersion: getambassador.io/v2
+kind: Mapping
+metadata:
+ name: gerrit-mapping-post-replica
+ namespace: gerrit
+spec:
+ bypass_auth: true
+ rewrite: ""
+ host: example.com
+ method: POST
+ prefix: /.*/git-upload-pack
+ prefix_regex: true
+ service: replica:48080
\ No newline at end of file
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mappingPrimary_primary_replica.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mappingPrimary_primary_replica.yaml
new file mode 100644
index 000000000..e727be040
--- /dev/null
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mappingPrimary_primary_replica.yaml
@@ -0,0 +1,11 @@
+apiVersion: getambassador.io/v2
+kind: Mapping
+metadata:
+ name: gerrit-mapping-primary
+ namespace: gerrit
+spec:
+ bypass_auth: true
+ rewrite: ""
+ host: example.com
+ prefix: /
+ service: primary:48080
\ No newline at end of file
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mapping_primary.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mapping_primary.yaml
new file mode 100644
index 000000000..1269b1d07
--- /dev/null
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mapping_primary.yaml
@@ -0,0 +1,11 @@
+apiVersion: getambassador.io/v2
+kind: Mapping
+metadata:
+ name: gerrit-mapping
+ namespace: gerrit
+spec:
+ bypass_auth: true
+ rewrite: ""
+ host: example.com
+ prefix: /
+ service: primary:48080
\ No newline at end of file
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mapping_receiver.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mapping_receiver.yaml
new file mode 100644
index 000000000..51a91cc98
--- /dev/null
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mapping_receiver.yaml
@@ -0,0 +1,12 @@
+apiVersion: getambassador.io/v2
+kind: Mapping
+metadata:
+ name: gerrit-mapping-receiver
+ namespace: gerrit
+spec:
+ bypass_auth: true
+ rewrite: ""
+ host: example.com
+ prefix: /a/projects/.*|/.*/git-receive-pack
+ prefix_regex: true
+ service: receiver:48081
\ No newline at end of file
diff --git a/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mapping_replica.yaml b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mapping_replica.yaml
new file mode 100644
index 000000000..1080648f4
--- /dev/null
+++ b/operator/src/test/resources/com/google/gerrit/k8s/operator/network/ambassador/dependent/mapping_replica.yaml
@@ -0,0 +1,11 @@
+apiVersion: getambassador.io/v2
+kind: Mapping
+metadata:
+ name: gerrit-mapping
+ namespace: gerrit
+spec:
+ bypass_auth: true
+ rewrite: ""
+ host: example.com
+ prefix: /
+ service: replica:48080
\ No newline at end of file